oracledb
Version:
A Node.js module for Oracle Database access from JavaScript and TypeScript
213 lines (202 loc) • 6.65 kB
JavaScript
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
// This software is dual-licensed to you under the Universal Permissive License
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
// either license.
//
// If you elect to accept the software under the Apache License, Version 2.0,
// the following applies:
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//-----------------------------------------------------------------------------
'use strict';
const { Buffer } = require('buffer');
const constants = require('../constants.js');
const Message = require('./base.js');
/**
* Abstracts all LOB operations.
*
* @class LobOpMessage
* @extends {Message}
*/
class LobOpMessage extends Message {
constructor(connImpl, options) {
super(connImpl);
/*
* source LOB locator. Reading data from it.
*/
this.sourceLobImpl = options.sourceLobImpl || null;
this.operation = options.operation;
/*
* Destination LOB locator. For copy, append operations,...
*/
this.destLobImpl = options.destLobImpl || null;
/*
* Offset from where sourceLob operation to start
*/
this.sourceOffset = options.sourceOffset || 0;
/*
* Offset from where destLob operation to start
*/
this.destOffset = options.destOffset || 0;
this.boolFlag = false;
if (options.data) { // data available For Writes
this.data = options.data;
}
this.functionCode = constants.TNS_FUNC_LOB_OP;
this.sendAmount = options.sendAmount;
this.amount = options.amount || 0; // LOB length
}
encode(buf) {
this.writeFunctionHeader(buf);
if (this.sourceLobImpl === null) {
buf.writeUInt8(0);
buf.writeUB4(0);
} else {
buf.writeUInt8(1);
buf.writeUB4(this.sourceLobImpl._locator.length);
}
if (this.destLobImpl === null) {
buf.writeUInt8(0);
buf.writeUB4(0);
} else {
buf.writeUInt8(1);
buf.writeUB4(this.destLobImpl._locator.length);
}
buf.writeUB4(0);
buf.writeUB4(0);
if (this.operation === constants.TNS_LOB_OP_CREATE_TEMP) {
buf.writeUInt8(1);
} else {
buf.writeUInt8(0);
}
buf.writeUInt8(0);
const operations = [
constants.TNS_LOB_OP_CREATE_TEMP,
constants.TNS_LOB_OP_IS_OPEN,
constants.TNS_LOB_OP_FILE_EXISTS,
constants.TNS_LOB_OP_FILE_ISOPEN,
];
if (operations.includes(this.operation)) {
buf.writeUInt8(1);
} else {
buf.writeUInt8(0);
}
buf.writeUB4(this.operation);
buf.writeUInt8(0);
buf.writeUInt8(0);
buf.writeUB8(this.sourceOffset);
buf.writeUB8(this.destOffset);
if (this.sendAmount) {
buf.writeUInt8(1);
} else {
buf.writeUInt8(0);
}
for (let i = 0; i < 3; i++) {
buf.writeUInt16BE(0);
}
if (this.sourceLobImpl) {
buf.writeBytes(this.sourceLobImpl._locator);
}
if (this.destLobImpl) {
buf.writeBytes(this.destLobImpl._locator);
}
if (this.operation === constants.TNS_LOB_OP_CREATE_TEMP) {
if (this.sourceLobImpl.dbType._csfrm === constants.CSFRM_NCHAR) {
buf.caps.checkNCharsetId();
buf.writeUB4(constants.TNS_CHARSET_UTF16);
} else {
buf.writeUB4(constants.TNS_CHARSET_UTF8);
}
}
if (this.data) {
let data;
buf.writeUInt8(constants.TNS_MSG_TYPE_LOB_DATA);
if (this.sourceLobImpl.dbType._oraTypeNum === constants.TNS_DATA_TYPE_BLOB) {
data = this.data;
} else if (this.sourceLobImpl.getCsfrm() === constants.CSFRM_NCHAR) {
data = this.data;
// TODO: avoid conversion back to string, if possible
// this is since bind data is converted to buffer automatically, but if
// it exceeds 32K for PL/SQL it must be written as a temporary LOB
if (Buffer.isBuffer(this.data)) {
data = data.toString();
}
data = Buffer.from(data, constants.TNS_ENCODING_UTF16).swap16();
} else {
data = Buffer.from(this.data);
}
buf.writeBytesWithLength(data);
}
if (this.sendAmount) {
buf.writeUB8(this.amount);
}
}
processMessage(buf, messageType) {
if (messageType === constants.TNS_MSG_TYPE_LOB_DATA) {
const oraTypeNum = this.sourceLobImpl.dbType._oraTypeNum;
let data = buf.readBytesWithLength();
if (data !== null) {
if (
oraTypeNum === constants.TNS_DATA_TYPE_BLOB ||
oraTypeNum === constants.TNS_DATA_TYPE_BFILE
) {
data = Buffer.from(data);
} else if (this.sourceLobImpl.getCsfrm() === constants.CSFRM_NCHAR) {
data = Buffer.from(data).swap16().toString('utf16le');
} else {
data = data.toString();
}
}
this.data = data;
} else {
super.processMessage(buf, messageType);
}
}
processReturnParameter(buf) {
let lobArray;
let locator;
let temp8;
let numBytes;
if (this.sourceLobImpl !== null) {
numBytes = this.sourceLobImpl._locator.length;
lobArray = buf.readBytes(numBytes);
locator = lobArray.slice(0, numBytes);
locator.copy(this.sourceLobImpl._locator);
}
if (this.destLobImpl !== null) {
numBytes = this.destLobImpl._locator.length;
lobArray = buf.readBytes(numBytes);
locator = lobArray.slice(0, numBytes);
locator.copy(this.destLobImpl._locator);
}
if (this.operation === constants.TNS_LOB_OP_CREATE_TEMP) {
buf.skipUB2(); // skip character set
buf.skipBytes(3); // skip trailing flags, amount
} else if (this.sendAmount) {
this.amount = buf.readSB8();
}
if (
this.operation === constants.TNS_LOB_OP_IS_OPEN
|| this.operation === constants.TNS_LOB_OP_FILE_EXISTS
|| this.operation === constants.TNS_LOB_OP_FILE_ISOPEN
) {
temp8 = buf.readUInt8();
this.boolFlag = temp8 > 0;
}
}
}
module.exports = LobOpMessage;