Skip to content

Commit abbbaf7

Browse files
committed
Add BFILE support
1 parent e4a261d commit abbbaf7

File tree

14 files changed

+377
-18
lines changed

14 files changed

+377
-18
lines changed

doc/src/release_notes.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ node-oracledb `v6.6.0 <https://github.com/oracle/node-oracledb/compare/v6.5.1...
1313
Common Changes
1414
++++++++++++++
1515

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

2121
Thin Mode Changes
@@ -58,6 +58,8 @@ Common Changes
5858

5959
#) Test and documentation updates.
6060

61+
#) Added support for ``oracledb.DB_TYPE_BFILE`` data type.
62+
6163
Thin Mode Changes
6264
+++++++++++++++++
6365

lib/errors.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ const ERR_INVALID_TPC_END_FLAGS = 151;
161161
const ERR_UNKNOWN_TRANSACTION_STATE = 152;
162162
const ERR_INVALID_TRANSACTION_SIZE = 153;
163163
const ERR_INVALID_BRANCH_SIZE = 154;
164+
const ERR_OPERATION_NOT_SUPPORTED_ON_BFILE = 155;
165+
const ERR_OPERATION_ONLY_SUPPORTED_ON_BFILE = 156;
164166

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

460466
// Oracle Net layer errors
461467

@@ -889,6 +895,8 @@ module.exports = {
889895
ERR_INVALID_TRANSACTION_SIZE,
890896
ERR_INVALID_BRANCH_SIZE,
891897
ERR_CONNECTION_CLOSED_CODE: `${ERR_PREFIX}-${ERR_CONNECTION_CLOSED}`,
898+
ERR_OPERATION_NOT_SUPPORTED_ON_BFILE,
899+
ERR_OPERATION_ONLY_SUPPORTED_ON_BFILE,
892900
WRN_COMPILATION_CREATE,
893901
assert,
894902
assertArgCount,

lib/impl/lob.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ class LobImpl {
5353
errors.throwNotImplemented("closing a LOB");
5454
}
5555

56+
//---------------------------------------------------------------------------
57+
// fileExists()
58+
//
59+
// Checks if BFILE present or not.
60+
//---------------------------------------------------------------------------
61+
fileExists() {
62+
errors.throwNotImplemented("check if BFILE exists");
63+
}
64+
5665
//---------------------------------------------------------------------------
5766
// getData()
5867
//
@@ -62,6 +71,15 @@ class LobImpl {
6271
errors.throwNotImplemented("getting all of the data from a LOB");
6372
}
6473

74+
//---------------------------------------------------------------------------
75+
// getDirFileName()
76+
//
77+
// returns directory and filename of LOB (BFILE).
78+
//----------------------------------------------------------------------------
79+
getDirFileName() {
80+
errors.throwNotImplemented("getting directory and filename of LOB(BFILE)");
81+
}
82+
6583
//---------------------------------------------------------------------------
6684
// read()
6785
//
@@ -71,6 +89,15 @@ class LobImpl {
7189
errors.throwNotImplemented("reading from a LOB");
7290
}
7391

92+
//---------------------------------------------------------------------------
93+
// setDirFileName()
94+
//
95+
// sets directory and filename for LOB (BFILE)
96+
//---------------------------------------------------------------------------
97+
setDirFileName() {
98+
errors.throwNotImplemented("setting directory and filename of LOB(BFILE)");
99+
}
100+
74101
//---------------------------------------------------------------------------
75102
// write()
76103
//

lib/impl/resultset.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ class ResultSetImpl {
236236
this.nestedCursorIndices.push(i);
237237
} else if (info.fetchType === types.DB_TYPE_CLOB ||
238238
info.fetchType === types.DB_TYPE_NCLOB ||
239-
info.fetchType === types.DB_TYPE_BLOB) {
239+
info.fetchType === types.DB_TYPE_BLOB ||
240+
info.fetchType === types.DB_TYPE_BFILE) {
240241
this.lobIndices.push(i);
241242
} else if (info.fetchType === types.DB_TYPE_OBJECT) {
242243
this.dbObjectIndices.push(i);

lib/lob.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ class Lob extends Duplex {
4646
});
4747
}
4848

49+
// called by BFILE specific functions to throw errors for other
50+
// datatypes.
51+
_checkIsBfile() {
52+
if (this.type !== types.DB_TYPE_BFILE) {
53+
errors.throwErr(errors.ERR_OPERATION_ONLY_SUPPORTED_ON_BFILE);
54+
}
55+
}
56+
57+
// called by functions not allowed for BFILE and throw errors if
58+
// such operations are performed on BFILE.
59+
_checkNotBfile() {
60+
if (this.type === types.DB_TYPE_BFILE) {
61+
errors.throwErr(errors.ERR_OPERATION_NOT_SUPPORTED_ON_BFILE);
62+
}
63+
}
64+
4965
// called by stream.destroy() and ensures that the LOB is closed if it has
5066
// not already been closed (never called directly)
5167
async _destroy(err, cb) {
@@ -101,7 +117,10 @@ class Lob extends Duplex {
101117
// called to associate a LOB implementation with this user facing object
102118
_setup(lobImpl, autoCloseLob) {
103119
this._impl = lobImpl;
104-
this._chunkSize = lobImpl.getChunkSize();
120+
// chunk size is not defined for BFILE LOBs
121+
if (this.type !== types.DB_TYPE_BFILE) {
122+
this._chunkSize = lobImpl.getChunkSize();
123+
}
105124
this._pieceSize = lobImpl.getPieceSize();
106125
this._length = lobImpl.getLength();
107126
this._type = lobImpl.getType();
@@ -153,6 +172,7 @@ class Lob extends Duplex {
153172
// Property for the chunk size of the LOB.
154173
//---------------------------------------------------------------------------
155174
get chunkSize() {
175+
this._checkNotBfile();
156176
return this._chunkSize;
157177
}
158178

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

232+
//---------------------------------------------------------------------------
233+
// getDirFileName()
234+
// To obtain the BFILE Lob object properties dirName & fileName
235+
//---------------------------------------------------------------------------
236+
getDirFileName() {
237+
this._checkIsBfile();
238+
return this._impl.getDirFileName();
239+
}
240+
241+
//--------------------------------------------------------------------------
242+
// setDirFileName()
243+
// To set the BFILE Lob object properties dirName & fileName
244+
//--------------------------------------------------------------------------
245+
setDirFileName(a1) {
246+
this._checkIsBfile();
247+
errors.assertArgCount(arguments, 1, 1);
248+
errors.assertParamValue(nodbUtil.isObject(a1), 1);
249+
this._impl.setDirFileName(a1);
250+
}
251+
252+
//---------------------------------------------------------------------------
253+
// fileExists
254+
//
255+
// To obtain file existence status of BFILE file
256+
//---------------------------------------------------------------------------
257+
async fileExists() {
258+
this._checkIsBfile();
259+
return await this._impl.fileExists();
260+
}
261+
212262
//---------------------------------------------------------------------------
213263
// length
214264
//
@@ -248,6 +298,7 @@ class Lob extends Duplex {
248298

249299
nodbUtil.wrapFns(Lob.prototype, errors.ERR_BUSY_LOB,
250300
"close",
301+
"fileExists",
251302
"getData");
252303
Lob.prototype._serializedRead = nodbUtil.serialize(Lob.prototype._readData);
253304
Lob.prototype._serializedWrite = nodbUtil.serialize(Lob.prototype._writeData);

lib/thin/lob.js

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const LobImpl = require('../impl/lob.js');
3131
const constants = require('./protocol/constants.js');
3232
const LobOpMessage = require('./protocol/messages/lobOp.js');
3333
const errors = require('../errors.js');
34+
const types = require('../types.js');
3435

3536
class ThinLobImpl extends LobImpl {
3637

@@ -54,6 +55,11 @@ class ThinLobImpl extends LobImpl {
5455
await this.conn._protocol._processMessage(message);
5556
if (options.operation === constants.TNS_LOB_OP_READ) {
5657
return (message.data) ? message.data : null;
58+
} else if (
59+
options.operation === constants.TNS_LOB_OP_FILE_EXISTS ||
60+
options.operation === constants.TNS_LOB_OP_FILE_ISOPEN
61+
) {
62+
return message.boolFlag;
5763
} else {
5864
return message.amount;
5965
}
@@ -127,10 +133,26 @@ class ThinLobImpl extends LobImpl {
127133
// Internal method returning the data obtained from the database.
128134
//---------------------------------------------------------------------------
129135
async getData(offset = 1, len = this._length) {
136+
let shouldClose = false;
130137
if (!len) {
131138
len = this._length;
132139
}
133-
return await this.read(offset, len);
140+
if (this.dbType === types.DB_TYPE_BFILE) {
141+
if (!await this.isFileOpen()) {
142+
shouldClose = true;
143+
await this.openFile();
144+
}
145+
}
146+
let data;
147+
// if read fails and BFILE was opened by application, we close it.
148+
try {
149+
data = await this.read(offset, len);
150+
} finally {
151+
if (shouldClose) {
152+
await this.closeFile();
153+
}
154+
}
155+
return data;
134156
}
135157

136158
//---------------------------------------------------------------------------
@@ -207,6 +229,45 @@ class ThinLobImpl extends LobImpl {
207229
await this._getChunkSizeAsync();
208230
}
209231

232+
//---------------------------------------------------------------------------
233+
// fileExists()
234+
//
235+
// Internal method for returning whether the file referenced by a BFILE
236+
// exists.
237+
//---------------------------------------------------------------------------
238+
async fileExists() {
239+
this.checkConn();
240+
const options = {
241+
operation: constants.TNS_LOB_OP_FILE_EXISTS,
242+
sourceLobImpl: this,
243+
};
244+
return await this._sendMessage(options);
245+
}
246+
247+
//---------------------------------------------------------------------------
248+
// getDirFileName()
249+
//
250+
// Internal method for returning the directory alias and name of the file
251+
// referenced by a BFILE
252+
//---------------------------------------------------------------------------
253+
getDirFileName() {
254+
const dirNameOffset = constants.TNS_LOB_LOC_FIXED_OFFSET + 2;
255+
const dirNameLen = this._locator.readUInt16BE(
256+
constants.TNS_LOB_LOC_FIXED_OFFSET
257+
);
258+
const fileNameOffset = constants.TNS_LOB_LOC_FIXED_OFFSET + dirNameLen + 4;
259+
const fileNameLen = this._locator.readUInt16BE(
260+
dirNameOffset + dirNameLen
261+
);
262+
const dirName = this._locator.slice(
263+
dirNameOffset, dirNameOffset + dirNameLen
264+
).toString();
265+
const fileName = this._locator.slice(
266+
fileNameOffset, fileNameOffset + fileNameLen
267+
).toString();
268+
return { dirName: dirName, fileName: fileName };
269+
}
270+
210271
//---------------------------------------------------------------------------
211272
// checkConn()
212273
//
@@ -231,6 +292,20 @@ class ThinLobImpl extends LobImpl {
231292
}
232293
}
233294

295+
//---------------------------------------------------------------------------
296+
// closeFile()
297+
//
298+
// Internal method to close the opened file for BFILE LOBs.
299+
//---------------------------------------------------------------------------
300+
async closeFile() {
301+
this.checkConn();
302+
const options = {
303+
operation: constants.TNS_LOB_OP_FILE_CLOSE,
304+
sourceLobImpl: this,
305+
};
306+
await this._sendMessage(options);
307+
}
308+
234309
//---------------------------------------------------------------------------
235310
// init()
236311
//
@@ -251,6 +326,56 @@ class ThinLobImpl extends LobImpl {
251326
this._pieceSize = chunkSize;
252327
}
253328

329+
//---------------------------------------------------------------------------
330+
// isFileOpen()
331+
//
332+
// Internal method to check if the file is already open.
333+
//---------------------------------------------------------------------------
334+
async isFileOpen() {
335+
const options = {
336+
operation: constants.TNS_LOB_OP_FILE_ISOPEN,
337+
sourceLobImpl: this
338+
};
339+
await this._sendMessage(options);
340+
}
341+
342+
//---------------------------------------------------------------------------
343+
// openFile()
344+
//
345+
// Internal method for opening file (BFILE).
346+
//---------------------------------------------------------------------------
347+
async openFile() {
348+
this.checkConn();
349+
const options = {
350+
operation: constants.TNS_LOB_OP_FILE_OPEN,
351+
sourceLobImpl: this,
352+
amount: constants.TNS_LOB_OPEN_READ_ONLY,
353+
sendAmount: true
354+
};
355+
return await this._sendMessage(options);
356+
}
357+
358+
//---------------------------------------------------------------------------
359+
// setDirFileName()
360+
//
361+
// Internal method for setting the directory alias and name of the file
362+
// referenced by a BFILE
363+
//---------------------------------------------------------------------------
364+
setDirFileName(dirObject) {
365+
const dirNameLen = Buffer.byteLength(dirObject.dirName);
366+
const dirNameOffset = constants.TNS_LOB_LOC_FIXED_OFFSET + 2;
367+
const fileNameOffset = dirNameOffset + dirNameLen + 2;
368+
const fileNameLen = Buffer.byteLength(dirObject.fileName);
369+
const newLocLen = fileNameOffset + fileNameLen;
370+
const newLocator = Buffer.allocUnsafe(newLocLen);
371+
this._locator.copy(newLocator, 0, 0, constants.TNS_LOB_LOC_FIXED_OFFSET + 1);
372+
newLocator.writeUInt16BE(dirNameLen, constants.TNS_LOB_LOC_FIXED_OFFSET);
373+
newLocator.write(dirObject.dirName, dirNameOffset);
374+
newLocator.writeInt16BE(fileNameLen, dirNameOffset + dirNameLen);
375+
newLocator.write(dirObject.fileName, fileNameOffset);
376+
this._locator = newLocator;
377+
}
378+
254379
}
255380

256381
module.exports = ThinLobImpl;

lib/thin/protocol/constants.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,12 +492,17 @@ module.exports = {
492492
TNS_LOB_OP_CLOSE: 0x10000,
493493
TNS_LOB_OP_IS_OPEN: 0x11000,
494494
TNS_LOB_OP_ARRAY: 0x80000,
495+
TNS_LOB_OP_FILE_OPEN: 0x0100,
496+
TNS_LOB_OP_FILE_CLOSE: 0x0200,
497+
TNS_LOB_OP_FILE_ISOPEN: 0x0400,
498+
TNS_LOB_OP_FILE_EXISTS: 0x0800,
495499

496500
// LOB locator constants
497501
TNS_LOB_LOC_OFFSET_FLAG_1: 4,
498502
TNS_LOB_LOC_OFFSET_FLAG_3: 6,
499503
TNS_LOB_LOC_OFFSET_FLAG_4: 7,
500504
TNS_LOB_QLOCATOR_VERSION: 4,
505+
TNS_LOB_LOC_FIXED_OFFSET: 16,
501506

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

514519
// other LOB constants
515520
TNS_LOB_OPEN_READ_WRITE: 2,
521+
TNS_LOB_OPEN_READ_ONLY: 11,
516522
TNS_LOB_PREFETCH_FLAG: 0x2000000,
517523

518524
// base JSON constants

0 commit comments

Comments
 (0)