UNPKG

oracledb

Version:

A Node.js module for Oracle Database access from JavaScript and TypeScript

382 lines (353 loc) 13.2 kB
// Copyright (c) 2022, 2024, 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 LobImpl = require('../impl/lob.js'); const constants = require('./protocol/constants.js'); const LobOpMessage = require('./protocol/messages/lobOp.js'); const errors = require('../errors.js'); const types = require('../types.js'); class ThinLobImpl extends LobImpl { //--------------------------------------------------------------------------- // _getConnImpl() // // Common method on all classes that make use of a connection -- used to // ensure serialization of all use of the connection. //--------------------------------------------------------------------------- _getConnImpl() { return this.conn; } //--------------------------------------------------------------------------- // _sendMessage() // // Sends a LOB operation message to the server and processes the response. //--------------------------------------------------------------------------- async _sendMessage(options) { const message = new LobOpMessage(this.conn, options); await this.conn._protocol._processMessage(message); if (options.operation === constants.TNS_LOB_OP_READ) { return (message.data) ? message.data : null; } else if ( options.operation === constants.TNS_LOB_OP_FILE_EXISTS || options.operation === constants.TNS_LOB_OP_FILE_ISOPEN ) { return message.boolFlag; } else { return message.amount; } } //--------------------------------------------------------------------------- // getChunkSize() // // Internal method for returning the chunk size of the LOB. //--------------------------------------------------------------------------- getChunkSize() { return this._chunkSize; } //--------------------------------------------------------------------------- // _getChunkSizeAsync() // // Internal method for returning the chunk size of the LOB fetched from // the database. //--------------------------------------------------------------------------- async _getChunkSizeAsync() { this.checkConn(); const options = { operation: constants.TNS_LOB_OP_GET_CHUNK_SIZE, sourceLobImpl: this, sendAmount: true }; this._chunkSize = this._pieceSize = await this._sendMessage(options); } //--------------------------------------------------------------------------- // getLength() // // Internal method for returning the length of a LOB. //--------------------------------------------------------------------------- getLength() { return this._length; } //--------------------------------------------------------------------------- // getPieceSize() // // Internal method returning the size to use for each piece that is // transferred when reading from the LOB. //--------------------------------------------------------------------------- getPieceSize() { return this._pieceSize; } //--------------------------------------------------------------------------- // setPieceSize() // // Internal method to set the pieceSize for LOBs. //--------------------------------------------------------------------------- setPieceSize(value) { this._pieceSize = value; } //--------------------------------------------------------------------------- // getType() // // Internal method returning the datatype of LOBs. //--------------------------------------------------------------------------- getType() { return this.dbType; } //--------------------------------------------------------------------------- // getData() // // Internal method returning the data obtained from the database. //--------------------------------------------------------------------------- async getData(offset = 1, len = this._length) { let shouldClose = false; if (!len) { len = this._length; } if (this.dbType === types.DB_TYPE_BFILE) { if (!await this.isFileOpen()) { shouldClose = true; await this.openFile(); } } let data; // if read fails and BFILE was opened by application, we close it. try { data = await this.read(offset, len); } finally { if (shouldClose) { await this.closeFile(); } } return data; } //--------------------------------------------------------------------------- // read() // // Internal method for reading a portion (or all) of the data in the LOB. //--------------------------------------------------------------------------- async read(offset, length) { this.checkConn(); const options = { operation: constants.TNS_LOB_OP_READ, sourceLobImpl: this, sourceOffset: offset, sendAmount: true, amount: length || this._pieceSize }; return await this._sendMessage(options); } //--------------------------------------------------------------------------- // write() // // Internal method for writing data to the LOB object. //--------------------------------------------------------------------------- async write(offset, data) { this.checkConn(); const options = { operation: constants.TNS_LOB_OP_WRITE, sourceLobImpl: this, sourceOffset: offset, data: data }; await this._sendMessage(options); this._length += data.length; } //--------------------------------------------------------------------------- // getCsfrm() // // Return the character set encoding used by the LOB. //--------------------------------------------------------------------------- getCsfrm() { if (this.dbType._csfrm !== constants.CSFRM_NCHAR) { if (this._locator[constants.TNS_LOB_LOC_OFFSET_FLAG_3] & constants.TNS_LOB_LOC_FLAGS_VAR_LENGTH_CHARSET) { return constants.CSFRM_NCHAR; } } return this.dbType._csfrm; } /** * Creates a temporary LOB. * * @param {object} conn Connection Impl object * @param {number} dbType indicates BLOB/CLOB DB type */ async create(conn, dbType) { this.dirtyLength = false; this.conn = conn; this.dbType = dbType; this._locator = Buffer.alloc(40); this._isTempLob = true; this._length = 0; const options = { operation: constants.TNS_LOB_OP_CREATE_TEMP, sourceLobImpl: this, amount: constants.TNS_DURATION_SESSION, destOffset: dbType._oraTypeNum, sourceOffset: dbType._csfrm, sendAmount: true }; await this._sendMessage(options); await this._getChunkSizeAsync(); } //--------------------------------------------------------------------------- // fileExists() // // Internal method for returning whether the file referenced by a BFILE // exists. //--------------------------------------------------------------------------- async fileExists() { this.checkConn(); const options = { operation: constants.TNS_LOB_OP_FILE_EXISTS, sourceLobImpl: this, }; return await this._sendMessage(options); } //--------------------------------------------------------------------------- // getDirFileName() // // Internal method for returning the directory alias and name of the file // referenced by a BFILE //--------------------------------------------------------------------------- getDirFileName() { const dirNameOffset = constants.TNS_LOB_LOC_FIXED_OFFSET + 2; const dirNameLen = this._locator.readUInt16BE( constants.TNS_LOB_LOC_FIXED_OFFSET ); const fileNameOffset = constants.TNS_LOB_LOC_FIXED_OFFSET + dirNameLen + 4; const fileNameLen = this._locator.readUInt16BE( dirNameOffset + dirNameLen ); const dirName = this._locator.slice( dirNameOffset, dirNameOffset + dirNameLen ).toString(); const fileName = this._locator.slice( fileNameOffset, fileNameOffset + fileNameLen ).toString(); return { dirName: dirName, fileName: fileName }; } //--------------------------------------------------------------------------- // checkConn() // // Internal method to check the connection. //--------------------------------------------------------------------------- checkConn() { if (!this.conn.nscon.connected) errors.throwErr(errors.ERR_INVALID_CONNECTION); } //--------------------------------------------------------------------------- // close() // // Internal method to close the LOBs using piggyback mechanism. //--------------------------------------------------------------------------- close() { this.checkConn(); if (this._isTempLob) { // Add to freelist which will be sent in piggyback fashion this.conn._tempLobsToClose.push(this._locator); this.conn._tempLobsTotalSize += this._locator.length; } } //--------------------------------------------------------------------------- // closeFile() // // Internal method to close the opened file for BFILE LOBs. //--------------------------------------------------------------------------- async closeFile() { this.checkConn(); const options = { operation: constants.TNS_LOB_OP_FILE_CLOSE, sourceLobImpl: this, }; await this._sendMessage(options); } //--------------------------------------------------------------------------- // init() // // Internal method to initialize LOBs. //--------------------------------------------------------------------------- init(conn, locator, dbType, len, chunkSize) { this.dirtyLength = false; this.conn = conn; this._locator = locator; this._isTempLob = false; if (this._locator[constants.TNS_LOB_LOC_OFFSET_FLAG_4] & constants.TNS_LOB_LOC_FLAGS_TEMP === constants.TNS_LOB_LOC_FLAGS_TEMP || this._locator[constants.TNS_LOB_LOC_OFFSET_FLAG_1] & constants.TNS_LOB_LOC_FLAGS_ABSTRACT === constants.TNS_LOB_LOC_FLAGS_ABSTRACT) { this._isTempLob = true; } this.dbType = dbType; this._length = len; this._chunkSize = chunkSize; this._pieceSize = chunkSize; } //--------------------------------------------------------------------------- // isFileOpen() // // Internal method to check if the file is already open. //--------------------------------------------------------------------------- async isFileOpen() { const options = { operation: constants.TNS_LOB_OP_FILE_ISOPEN, sourceLobImpl: this }; await this._sendMessage(options); } //--------------------------------------------------------------------------- // openFile() // // Internal method for opening file (BFILE). //--------------------------------------------------------------------------- async openFile() { this.checkConn(); const options = { operation: constants.TNS_LOB_OP_FILE_OPEN, sourceLobImpl: this, amount: constants.TNS_LOB_OPEN_READ_ONLY, sendAmount: true }; return await this._sendMessage(options); } //--------------------------------------------------------------------------- // setDirFileName() // // Internal method for setting the directory alias and name of the file // referenced by a BFILE //--------------------------------------------------------------------------- setDirFileName(dirObject) { const dirNameLen = Buffer.byteLength(dirObject.dirName); const dirNameOffset = constants.TNS_LOB_LOC_FIXED_OFFSET + 2; const fileNameOffset = dirNameOffset + dirNameLen + 2; const fileNameLen = Buffer.byteLength(dirObject.fileName); const newLocLen = fileNameOffset + fileNameLen; const newLocator = Buffer.allocUnsafe(newLocLen); this._locator.copy(newLocator, 0, 0, constants.TNS_LOB_LOC_FIXED_OFFSET + 1); newLocator.writeUInt16BE(dirNameLen, constants.TNS_LOB_LOC_FIXED_OFFSET); newLocator.write(dirObject.dirName, dirNameOffset); newLocator.writeInt16BE(fileNameLen, dirNameOffset + dirNameLen); newLocator.write(dirObject.fileName, fileNameOffset); this._locator = newLocator; } } module.exports = ThinLobImpl;