node-oracledb/lib/lob.js

240 lines
7.3 KiB
JavaScript

// Copyright (c) 2016, 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 { Duplex } = require('stream');
const constants = require('./constants.js');
const errors = require('./errors.js');
const nodbUtil = require('./util.js');
const types = require('./types.js');
class Lob extends Duplex {
constructor() {
super({ decodeStrings: false });
this.offset = 1;
this._isActive = false;
this.once('finish', function() {
if (this._autoCloseLob) {
this.destroy();
}
});
}
// called by stream.destroy() and ensures that the LOB is closed if it has
// not already been closed (never called directly)
async _destroy(err, cb) {
// if LOB was already closed, nothing to do!
if (err && err.message.startsWith("NJS-108:"))
delete this._impl;
if (this._impl) {
const lobImpl = this._impl;
delete this._impl;
try {
await lobImpl.close();
} catch (closeErr) {
cb(closeErr);
return;
}
}
cb(err);
}
// implementation of streaming read; if LOB is set to auto-close, the lob is
// automatically closed when an error occurs or when there are no more bytes
// to transfer; all that needs to be done here is to destroy the streaming
// LOB
async _read() {
try {
const data = await this._serializedRead(this.offset);
if (data) {
this.offset += data.length;
this.push(data);
} else {
this.push(null);
if (this._autoCloseLob) {
this.destroy();
}
}
} catch (err) {
if (this._autoCloseLob)
this.destroy(err);
throw err;
}
}
// simple wrapper so that serialization can take place on a JavaScript fn
async _readData(offset) {
errors.assert(this._impl, errors.ERR_INVALID_LOB);
try {
return await this._impl.read(offset);
} catch (err) {
throw errors.transformErr(err, this._readData);
}
}
// called to associate a LOB implementation with this user facing object
_setup(lobImpl, autoCloseLob) {
this._impl = lobImpl;
this._chunkSize = lobImpl.getChunkSize();
this._pieceSize = lobImpl.getPieceSize();
this._length = lobImpl.getLength();
this._type = lobImpl.getType();
if (typeof this._type === 'number') {
this._type = types.getTypeByNum(this._type);
}
this._autoCloseLob = autoCloseLob;
}
// implementation of streaming write; if LOB is set to auto-close, the lob is
// automatically closed in the "finish" event; all that needs to be done here
// is to destroy the streaming LOB
async _write(data, encoding, cb) {
// convert data if needed
if (this.type == constants.DB_TYPE_BLOB && !Buffer.isBuffer(data)) {
data = Buffer.from(data);
} else if (this.type == constants.DB_TYPE_CLOB &&
Buffer.isBuffer(data)) {
data = data.toString();
}
try {
await this._serializedWrite(this.offset, data);
} catch (err) {
if (this._autoCloseLob)
this.destroy(err);
cb(err);
return;
}
this.offset += data.length;
cb(null);
}
// simple wrapper so that serialization can take place on a JavaScript fn
async _writeData(offset, data) {
errors.assert(this._impl, errors.ERR_INVALID_LOB);
try {
await this._impl.write(offset, data);
} catch (err) {
throw errors.transformErr(err, this._writeData);
}
}
//---------------------------------------------------------------------------
// chunkSize
//
// Property for the chunk size of the LOB.
//---------------------------------------------------------------------------
get chunkSize() {
return this._chunkSize;
}
//---------------------------------------------------------------------------
// close()
//
// Close the LOB and make it unusable for further operations. If the LOB is
// already closed, nothing is done in order to support multiple close()
// calls.
//
// This method is deprecated and will be removed in a future version of the
// node-oracledb driver. Use lob.destroy() instead. NOTE: this method will
// emit a duplicate "close" event in order to be compatible with previous
// versions of node-oracledb.
//---------------------------------------------------------------------------
async close() {
errors.assertArgCount(arguments, 0, 0);
if (this._impl) {
const lobImpl = this._impl;
delete this._impl;
try {
await lobImpl.close();
this.emit('close');
} catch (err) {
this.destroy(err);
}
}
}
//---------------------------------------------------------------------------
// getData()
//
// Returns all of the data in the LOB as a single string or buffer.
//---------------------------------------------------------------------------
async getData() {
errors.assertArgCount(arguments, 0, 0);
errors.assert(this._impl, errors.ERR_INVALID_LOB);
return await this._impl.getData();
}
//---------------------------------------------------------------------------
// length
//
// Property for the length of the LOB.
//---------------------------------------------------------------------------
get length() {
return this._length;
}
//---------------------------------------------------------------------------
// pieceSize
//
// Property for the size to use for each piece that is transferred when
// reading from the LOB.
//---------------------------------------------------------------------------
get pieceSize() {
return this._pieceSize;
}
set pieceSize(value) {
errors.assertPropValue(Number.isInteger(value) && value >= 0, "pieceSize");
errors.assert(this._impl, errors.ERR_INVALID_LOB);
this._impl.setPieceSize(value);
this._pieceSize = value;
}
//---------------------------------------------------------------------------
// type
//
// Property for the type of the LOB.
//---------------------------------------------------------------------------
get type() {
return this._type;
}
}
nodbUtil.wrapFns(Lob.prototype, errors.ERR_BUSY_LOB,
"close",
"getData");
Lob.prototype._serializedRead = nodbUtil.serialize(Lob.prototype._readData);
Lob.prototype._serializedWrite = nodbUtil.serialize(Lob.prototype._writeData);
module.exports = Lob;