Skip to content

Commit

Permalink
Add BFILE support
Browse files Browse the repository at this point in the history
  • Loading branch information
sharadraju committed Jul 15, 2024
1 parent e4a261d commit abbbaf7
Show file tree
Hide file tree
Showing 14 changed files with 377 additions and 18 deletions.
6 changes: 4 additions & 2 deletions doc/src/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ node-oracledb `v6.6.0 <https://github.com/oracle/node-oracledb/compare/v6.5.1...
Common Changes
++++++++++++++

#) Added support for Centralized Configuration Providers (Azure App Configuration Store and OCI
#) Added support for Centralized Configuration Providers (Azure App Configuration Store and OCI
Object Storage).
Node-oracledb extracts configuration information from the the supported provider and uses it to
Node-oracledb extracts configuration information from the the supported provider and uses it to
connect to the database.

Thin Mode Changes
Expand Down Expand Up @@ -58,6 +58,8 @@ Common Changes

#) Test and documentation updates.

#) Added support for ``oracledb.DB_TYPE_BFILE`` data type.

Thin Mode Changes
+++++++++++++++++

Expand Down
8 changes: 8 additions & 0 deletions lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ const ERR_INVALID_TPC_END_FLAGS = 151;
const ERR_UNKNOWN_TRANSACTION_STATE = 152;
const ERR_INVALID_TRANSACTION_SIZE = 153;
const ERR_INVALID_BRANCH_SIZE = 154;
const ERR_OPERATION_NOT_SUPPORTED_ON_BFILE = 155;
const ERR_OPERATION_ONLY_SUPPORTED_ON_BFILE = 156;

// Oracle Net layer errors start from 500
const ERR_CONNECTION_CLOSED = 500;
Expand Down Expand Up @@ -456,6 +458,10 @@ messages.set(ERR_INVALID_TRANSACTION_SIZE, // NJS-153
'size of the transaction ID is %d and cannot exceed 64');
messages.set(ERR_INVALID_BRANCH_SIZE, // NJS-154
'size of the branch ID is %d and cannot exceed 64');
messages.set(ERR_OPERATION_NOT_SUPPORTED_ON_BFILE, // NJS-155
'operation is not supported on BFILE LOBs');
messages.set(ERR_OPERATION_ONLY_SUPPORTED_ON_BFILE, // NJS-156
'operation is only supported on BFILE LOBs');

// Oracle Net layer errors

Expand Down Expand Up @@ -889,6 +895,8 @@ module.exports = {
ERR_INVALID_TRANSACTION_SIZE,
ERR_INVALID_BRANCH_SIZE,
ERR_CONNECTION_CLOSED_CODE: `${ERR_PREFIX}-${ERR_CONNECTION_CLOSED}`,
ERR_OPERATION_NOT_SUPPORTED_ON_BFILE,
ERR_OPERATION_ONLY_SUPPORTED_ON_BFILE,
WRN_COMPILATION_CREATE,
assert,
assertArgCount,
Expand Down
27 changes: 27 additions & 0 deletions lib/impl/lob.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ class LobImpl {
errors.throwNotImplemented("closing a LOB");
}

//---------------------------------------------------------------------------
// fileExists()
//
// Checks if BFILE present or not.
//---------------------------------------------------------------------------
fileExists() {
errors.throwNotImplemented("check if BFILE exists");
}

//---------------------------------------------------------------------------
// getData()
//
Expand All @@ -62,6 +71,15 @@ class LobImpl {
errors.throwNotImplemented("getting all of the data from a LOB");
}

//---------------------------------------------------------------------------
// getDirFileName()
//
// returns directory and filename of LOB (BFILE).
//----------------------------------------------------------------------------
getDirFileName() {
errors.throwNotImplemented("getting directory and filename of LOB(BFILE)");
}

//---------------------------------------------------------------------------
// read()
//
Expand All @@ -71,6 +89,15 @@ class LobImpl {
errors.throwNotImplemented("reading from a LOB");
}

//---------------------------------------------------------------------------
// setDirFileName()
//
// sets directory and filename for LOB (BFILE)
//---------------------------------------------------------------------------
setDirFileName() {
errors.throwNotImplemented("setting directory and filename of LOB(BFILE)");
}

//---------------------------------------------------------------------------
// write()
//
Expand Down
3 changes: 2 additions & 1 deletion lib/impl/resultset.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ class ResultSetImpl {
this.nestedCursorIndices.push(i);
} else if (info.fetchType === types.DB_TYPE_CLOB ||
info.fetchType === types.DB_TYPE_NCLOB ||
info.fetchType === types.DB_TYPE_BLOB) {
info.fetchType === types.DB_TYPE_BLOB ||
info.fetchType === types.DB_TYPE_BFILE) {
this.lobIndices.push(i);
} else if (info.fetchType === types.DB_TYPE_OBJECT) {
this.dbObjectIndices.push(i);
Expand Down
53 changes: 52 additions & 1 deletion lib/lob.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ class Lob extends Duplex {
});
}

// called by BFILE specific functions to throw errors for other
// datatypes.
_checkIsBfile() {
if (this.type !== types.DB_TYPE_BFILE) {
errors.throwErr(errors.ERR_OPERATION_ONLY_SUPPORTED_ON_BFILE);
}
}

// called by functions not allowed for BFILE and throw errors if
// such operations are performed on BFILE.
_checkNotBfile() {
if (this.type === types.DB_TYPE_BFILE) {
errors.throwErr(errors.ERR_OPERATION_NOT_SUPPORTED_ON_BFILE);
}
}

// 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) {
Expand Down Expand Up @@ -101,7 +117,10 @@ class Lob extends Duplex {
// called to associate a LOB implementation with this user facing object
_setup(lobImpl, autoCloseLob) {
this._impl = lobImpl;
this._chunkSize = lobImpl.getChunkSize();
// chunk size is not defined for BFILE LOBs
if (this.type !== types.DB_TYPE_BFILE) {
this._chunkSize = lobImpl.getChunkSize();
}
this._pieceSize = lobImpl.getPieceSize();
this._length = lobImpl.getLength();
this._type = lobImpl.getType();
Expand Down Expand Up @@ -153,6 +172,7 @@ class Lob extends Duplex {
// Property for the chunk size of the LOB.
//---------------------------------------------------------------------------
get chunkSize() {
this._checkNotBfile();
return this._chunkSize;
}

Expand Down Expand Up @@ -209,6 +229,36 @@ class Lob extends Duplex {
return await this._impl.getData(offset, amount);
}

//---------------------------------------------------------------------------
// getDirFileName()
// To obtain the BFILE Lob object properties dirName & fileName
//---------------------------------------------------------------------------
getDirFileName() {
this._checkIsBfile();
return this._impl.getDirFileName();
}

//--------------------------------------------------------------------------
// setDirFileName()
// To set the BFILE Lob object properties dirName & fileName
//--------------------------------------------------------------------------
setDirFileName(a1) {
this._checkIsBfile();
errors.assertArgCount(arguments, 1, 1);
errors.assertParamValue(nodbUtil.isObject(a1), 1);
this._impl.setDirFileName(a1);
}

//---------------------------------------------------------------------------
// fileExists
//
// To obtain file existence status of BFILE file
//---------------------------------------------------------------------------
async fileExists() {
this._checkIsBfile();
return await this._impl.fileExists();
}

//---------------------------------------------------------------------------
// length
//
Expand Down Expand Up @@ -248,6 +298,7 @@ class Lob extends Duplex {

nodbUtil.wrapFns(Lob.prototype, errors.ERR_BUSY_LOB,
"close",
"fileExists",
"getData");
Lob.prototype._serializedRead = nodbUtil.serialize(Lob.prototype._readData);
Lob.prototype._serializedWrite = nodbUtil.serialize(Lob.prototype._writeData);
Expand Down
127 changes: 126 additions & 1 deletion lib/thin/lob.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ 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 {

Expand All @@ -54,6 +55,11 @@ class ThinLobImpl extends LobImpl {
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;
}
Expand Down Expand Up @@ -127,10 +133,26 @@ class ThinLobImpl extends LobImpl {
// Internal method returning the data obtained from the database.
//---------------------------------------------------------------------------
async getData(offset = 1, len = this._length) {
let shouldClose = false;
if (!len) {
len = this._length;
}
return await this.read(offset, len);
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;
}

//---------------------------------------------------------------------------
Expand Down Expand Up @@ -207,6 +229,45 @@ class ThinLobImpl extends LobImpl {
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()
//
Expand All @@ -231,6 +292,20 @@ class ThinLobImpl extends LobImpl {
}
}

//---------------------------------------------------------------------------
// 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()
//
Expand All @@ -251,6 +326,56 @@ class ThinLobImpl extends LobImpl {
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;
6 changes: 6 additions & 0 deletions lib/thin/protocol/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,12 +492,17 @@ module.exports = {
TNS_LOB_OP_CLOSE: 0x10000,
TNS_LOB_OP_IS_OPEN: 0x11000,
TNS_LOB_OP_ARRAY: 0x80000,
TNS_LOB_OP_FILE_OPEN: 0x0100,
TNS_LOB_OP_FILE_CLOSE: 0x0200,
TNS_LOB_OP_FILE_ISOPEN: 0x0400,
TNS_LOB_OP_FILE_EXISTS: 0x0800,

// LOB locator constants
TNS_LOB_LOC_OFFSET_FLAG_1: 4,
TNS_LOB_LOC_OFFSET_FLAG_3: 6,
TNS_LOB_LOC_OFFSET_FLAG_4: 7,
TNS_LOB_QLOCATOR_VERSION: 4,
TNS_LOB_LOC_FIXED_OFFSET: 16,

// LOB locator flags (byte 1)
TNS_LOB_LOC_FLAGS_BLOB: 0x01,
Expand All @@ -513,6 +518,7 @@ module.exports = {

// other LOB constants
TNS_LOB_OPEN_READ_WRITE: 2,
TNS_LOB_OPEN_READ_ONLY: 11,
TNS_LOB_PREFETCH_FLAG: 0x2000000,

// base JSON constants
Expand Down
Loading

0 comments on commit abbbaf7

Please sign in to comment.