213 lines
6.5 KiB
JavaScript
213 lines
6.5 KiB
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;
|