oracledb 6.0.3 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/build/Release/oracledb-6.2.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.2.0-darwin-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.2.0-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.2.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.2.0-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.2.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.2.0-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.2.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.2.0-win32-x64.node-buildinfo.txt +1 -0
- package/examples/dbconfig.js +7 -14
- package/examples/example.js +0 -1
- package/lib/aqQueue.js +40 -26
- package/lib/connection.js +16 -4
- package/lib/dbObject.js +9 -0
- package/lib/errors.js +17 -13
- package/lib/impl/connection.js +9 -0
- package/lib/impl/resultset.js +7 -4
- package/lib/oracledb.js +26 -18
- package/lib/pool.js +7 -1
- package/lib/sodaCollection.js +12 -1
- package/lib/sodaOperation.js +16 -0
- package/lib/thin/connection.js +229 -182
- package/lib/thin/dbObject.js +4 -1
- package/lib/thin/pool.js +153 -68
- package/lib/thin/protocol/buffer.js +83 -64
- package/lib/thin/protocol/constants.js +3 -14
- package/lib/thin/protocol/encryptDecrypt.js +17 -17
- package/lib/thin/protocol/messages/auth.js +21 -23
- package/lib/thin/protocol/messages/base.js +29 -45
- package/lib/thin/protocol/messages/connectionCookie.js +76 -0
- package/lib/thin/protocol/messages/dataType.js +13 -9
- package/lib/thin/protocol/messages/execute.js +7 -7
- package/lib/thin/protocol/messages/index.js +2 -0
- package/lib/thin/protocol/messages/protocol.js +31 -20
- package/lib/thin/protocol/messages/withData.js +58 -47
- package/lib/thin/protocol/oson.js +17 -14
- package/lib/thin/protocol/packet.js +36 -14
- package/lib/thin/protocol/utils.js +2 -2
- package/lib/thin/resultSet.js +6 -6
- package/lib/thin/sqlnet/connStrategy.js +130 -5
- package/lib/thin/sqlnet/constants.js +2 -0
- package/lib/thin/sqlnet/ezConnectResolver.js +29 -61
- package/lib/thin/sqlnet/navNodes.js +34 -27
- package/lib/thin/sqlnet/networkSession.js +46 -47
- package/lib/thin/sqlnet/ntTcp.js +30 -14
- package/lib/thin/sqlnet/nvStrToNvPair.js +16 -16
- package/lib/thin/sqlnet/packet.js +10 -3
- package/lib/thin/sqlnet/paramParser.js +9 -9
- package/lib/thin/sqlnet/sessionAtts.js +4 -0
- package/lib/thin/statement.js +345 -113
- package/lib/thin/util.js +23 -86
- package/lib/transformer.js +3 -1
- package/lib/util.js +4 -13
- package/lib/version.js +2 -2
- package/package.json +1 -1
- package/build/Release/oracledb-6.0.3-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.0.3-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.0.3-js-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.0.3-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.0.3-linux-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.0.3-linux-x64.node +0 -0
- package/build/Release/oracledb-6.0.3-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.0.3-win32-x64.node +0 -0
- package/build/Release/oracledb-6.0.3-win32-x64.node-buildinfo.txt +0 -1
|
@@ -57,6 +57,7 @@ class MessageWithData extends Message {
|
|
|
57
57
|
this.inFetch = false;
|
|
58
58
|
this.parseOnly = false;
|
|
59
59
|
this.resultSetsToSetup = [];
|
|
60
|
+
this.deferredErr = null;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
/**
|
|
@@ -65,11 +66,12 @@ class MessageWithData extends Message {
|
|
|
65
66
|
processMessage(buf, messageType) {
|
|
66
67
|
if (messageType === constants.TNS_MSG_TYPE_DESCRIBE_INFO) {
|
|
67
68
|
buf.skipBytesChunked();
|
|
69
|
+
const prevQueryVars = this.statement.queryVars;
|
|
68
70
|
this.statement.queryVars = [];
|
|
69
71
|
this.statement.numQueryVars = 0;
|
|
70
72
|
this.statement.bufferRowCount = 0;
|
|
71
73
|
this.statement.bufferRowIndex = 0;
|
|
72
|
-
this.processDescribeInfo(buf, this.resultSet);
|
|
74
|
+
this.processDescribeInfo(buf, this.resultSet, prevQueryVars);
|
|
73
75
|
this.outVariables = this.statement.queryVars;
|
|
74
76
|
} else if (messageType === constants.TNS_MSG_TYPE_ROW_HEADER) {
|
|
75
77
|
this.processRowHeader(buf);
|
|
@@ -115,15 +117,30 @@ class MessageWithData extends Message {
|
|
|
115
117
|
this.errorOccurred = false;
|
|
116
118
|
this.statement.moreRowsToFetch = false;
|
|
117
119
|
} else if (this.errorInfo.num !== 0 && this.errorInfo.cursorId !== 0) {
|
|
118
|
-
this.connection._addCursorToClose(this.statement.cursorId);
|
|
119
120
|
this.connection.statementCache.delete(this.statement.sql);
|
|
121
|
+
this.statement.returnToCache = false;
|
|
120
122
|
}
|
|
121
123
|
if (this.errorInfo.batchErrors) {
|
|
122
124
|
this.errorOccurred = false;
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
|
|
126
|
-
|
|
128
|
+
//---------------------------------------------------------------------------
|
|
129
|
+
// If we have fetched this column earlier, we set that
|
|
130
|
+
// fetch type for the describe info variable received
|
|
131
|
+
// assuming the returned column order is same as previous.
|
|
132
|
+
//---------------------------------------------------------------------------
|
|
133
|
+
_adjustFetchType(pVar, cVar) {
|
|
134
|
+
if ((cVar.fetchInfo.dbType._oraTypeNum === constants.TNS_DATA_TYPE_CLOB
|
|
135
|
+
&& pVar.fetchInfo.fetchType._oraTypeNum === constants.TNS_DATA_TYPE_LONG)
|
|
136
|
+
|| (cVar.fetchInfo.dbType._oraTypeNum === constants.TNS_DATA_TYPE_BLOB
|
|
137
|
+
&& pVar.fetchInfo.fetchType._oraTypeNum === constants.TNS_DATA_TYPE_LONG_RAW)) {
|
|
138
|
+
cVar.type = pVar.fetchInfo.fetchType;
|
|
139
|
+
cVar.maxSize = pVar.maxSize;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
processDescribeInfo(buf, resultSet, prevQueryVars) {
|
|
127
144
|
const statement = resultSet.statement;
|
|
128
145
|
buf.skipUB4(); // max row size
|
|
129
146
|
statement.numQueryVars = buf.readUB4();
|
|
@@ -133,6 +150,9 @@ class MessageWithData extends Message {
|
|
|
133
150
|
resultSet.metadata = [];
|
|
134
151
|
for (let i = 0; i < statement.numQueryVars; i++) {
|
|
135
152
|
const variable = this.processColumnInfo(buf, i + 1);
|
|
153
|
+
if (prevQueryVars && i < prevQueryVars.length) {
|
|
154
|
+
this._adjustFetchType(prevQueryVars[i], variable);
|
|
155
|
+
}
|
|
136
156
|
statement.queryVars.push(variable);
|
|
137
157
|
resultSet.metadata.push(variable.fetchInfo);
|
|
138
158
|
}
|
|
@@ -257,8 +277,7 @@ class MessageWithData extends Message {
|
|
|
257
277
|
buf.skipUB2(); // buffer length
|
|
258
278
|
let numBytes = buf.readUB4();
|
|
259
279
|
if (numBytes > 0) {
|
|
260
|
-
buf.
|
|
261
|
-
this.getBitVector(buf, numBytes);
|
|
280
|
+
this.bitVector = Buffer.from(buf.readBytesWithLength());
|
|
262
281
|
}
|
|
263
282
|
numBytes = buf.readUB4();
|
|
264
283
|
if (numBytes > 0) {
|
|
@@ -267,28 +286,28 @@ class MessageWithData extends Message {
|
|
|
267
286
|
}
|
|
268
287
|
|
|
269
288
|
isDuplicateData(columnName) {
|
|
270
|
-
if (this.bitVector
|
|
289
|
+
if (!this.bitVector) {
|
|
271
290
|
return false;
|
|
272
291
|
}
|
|
273
|
-
|
|
274
|
-
|
|
292
|
+
const byteNum = Math.floor(columnName / 8);
|
|
293
|
+
const bitNum = columnName % 8;
|
|
275
294
|
return (this.bitVector[byteNum] & (1 << bitNum)) === 0;
|
|
276
295
|
}
|
|
277
296
|
|
|
278
297
|
processRowData(buf) {
|
|
279
298
|
let value;
|
|
280
|
-
for (
|
|
299
|
+
for (const [col, variable] of this.outVariables.entries()) {
|
|
281
300
|
if (variable.isArray) {
|
|
282
301
|
variable.numElementsInArray = buf.readUB4();
|
|
283
|
-
|
|
302
|
+
const values = new Array(variable.numElementsInArray).fill(null);
|
|
284
303
|
for (let pos = 0; pos < variable.numElementsInArray; pos++) {
|
|
285
304
|
value = this.processColumnData(buf, variable, pos);
|
|
286
305
|
values[pos] = value;
|
|
287
306
|
}
|
|
288
307
|
variable.values[this.rowIndex] = values;
|
|
289
308
|
} else if (this.statement.isReturning) {
|
|
290
|
-
|
|
291
|
-
|
|
309
|
+
const numRows = buf.readUB4();
|
|
310
|
+
const values = Array(numRows).fill(null);
|
|
292
311
|
for (let j = 0; j < numRows; j++) {
|
|
293
312
|
values[j] = this.processColumnData(buf, variable, j);
|
|
294
313
|
}
|
|
@@ -331,7 +350,7 @@ class MessageWithData extends Message {
|
|
|
331
350
|
}
|
|
332
351
|
this.outVariables = [];
|
|
333
352
|
for (let i = 0; i < numBinds; i++) { // bind directions
|
|
334
|
-
|
|
353
|
+
const bindInfo = this.statement.bindInfoList[i];
|
|
335
354
|
bindInfo.bindDir = buf.readUInt8();
|
|
336
355
|
if (bindInfo.bindDir === constants.TNS_BIND_DIR_INPUT) {
|
|
337
356
|
continue;
|
|
@@ -383,11 +402,11 @@ class MessageWithData extends Message {
|
|
|
383
402
|
if (!this.inFetch) {
|
|
384
403
|
colValue = buf.readStr(constants.CSFRM_IMPLICIT);
|
|
385
404
|
} else {
|
|
386
|
-
|
|
405
|
+
const numBytes = buf.readUInt8();
|
|
387
406
|
if (isNullLength(numBytes)) {
|
|
388
407
|
colValue = null;
|
|
389
408
|
} else {
|
|
390
|
-
|
|
409
|
+
const rowid = buf.readRowID();
|
|
391
410
|
colValue = utils.encodeRowID(rowid);
|
|
392
411
|
}
|
|
393
412
|
}
|
|
@@ -406,7 +425,7 @@ class MessageWithData extends Message {
|
|
|
406
425
|
if (colValue !== null)
|
|
407
426
|
colValue = parseFloat(colValue);
|
|
408
427
|
} else if (oraTypeNum === constants.TNS_DATA_TYPE_CURSOR) {
|
|
409
|
-
|
|
428
|
+
const numBytes = buf.readUInt8();
|
|
410
429
|
if (isNullLength(numBytes)) {
|
|
411
430
|
colValue = null;
|
|
412
431
|
} else {
|
|
@@ -425,7 +444,7 @@ class MessageWithData extends Message {
|
|
|
425
444
|
} else if (oraTypeNum === constants.TNS_DATA_TYPE_BOOLEAN) {
|
|
426
445
|
colValue = buf.readBool();
|
|
427
446
|
} else if (oraTypeNum === constants.TNS_DATA_TYPE_CLOB || oraTypeNum === constants.TNS_DATA_TYPE_BLOB) {
|
|
428
|
-
|
|
447
|
+
const bvalue = buf.readUB4();
|
|
429
448
|
if (bvalue > 0) { // Non Null data in column
|
|
430
449
|
colValue = new ThinLobImpl();
|
|
431
450
|
const length = buf.readUB8();
|
|
@@ -454,7 +473,7 @@ class MessageWithData extends Message {
|
|
|
454
473
|
if (actualNumBytes < 0 && oraTypeNum === constants.TNS_DATA_TYPE_BOOLEAN) {
|
|
455
474
|
colValue = null;
|
|
456
475
|
} else if (actualNumBytes !== 0 && colValue !== null) {
|
|
457
|
-
|
|
476
|
+
this.saveDeferredErr(errors.ERR_INSUFFICIENT_BUFFER_FOR_BINDS);
|
|
458
477
|
}
|
|
459
478
|
} else if (oraTypeNum === constants.TNS_DATA_TYPE_LONG || oraTypeNum === constants.TNS_DATA_TYPE_LONG_RAW || variable.maxSize > buf.caps.maxStringSize) {
|
|
460
479
|
buf.skipSB4(); // null indicator
|
|
@@ -497,31 +516,25 @@ class MessageWithData extends Message {
|
|
|
497
516
|
buf.skip(numBytes);
|
|
498
517
|
}
|
|
499
518
|
if (this.arrayDmlRowCounts) {
|
|
500
|
-
|
|
501
|
-
|
|
519
|
+
const numRows = buf.readUB4();
|
|
520
|
+
const rowCounts = this.options.dmlRowCounts = [];
|
|
502
521
|
for (let i = 0; i < numRows; i++) {
|
|
503
|
-
|
|
522
|
+
const rowCount = buf.readUB8();
|
|
504
523
|
rowCounts.push(rowCount);
|
|
505
524
|
}
|
|
506
525
|
}
|
|
507
526
|
}
|
|
508
527
|
|
|
509
|
-
process(buf) {
|
|
510
|
-
this.bitVector = this.savedBitVector;
|
|
511
|
-
super.process(buf);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
savePoint(buf) {
|
|
515
|
-
this.savedBitVector = this.bitVector;
|
|
516
|
-
super.savePoint(buf);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
528
|
async postProcess() {
|
|
529
|
+
if (this.deferredErr) {
|
|
530
|
+
throw this.deferredErr;
|
|
531
|
+
}
|
|
532
|
+
|
|
520
533
|
if (this.outVariables) {
|
|
521
534
|
for (const variable of this.outVariables) {
|
|
522
535
|
if (variable.isArray) {
|
|
523
|
-
|
|
524
|
-
|
|
536
|
+
if (variable.outConverter) {
|
|
537
|
+
for (let pos = 0; pos < variable.numElementsInArray; pos++) {
|
|
525
538
|
variable.values[0][pos] = await variable.outConverter(variable.values[0][pos]);
|
|
526
539
|
}
|
|
527
540
|
}
|
|
@@ -546,8 +559,10 @@ class MessageWithData extends Message {
|
|
|
546
559
|
variable.type = variable.fetchInfo.fetchType;
|
|
547
560
|
variable.maxSize = constants.TNS_MAX_LONG_LENGTH;
|
|
548
561
|
}
|
|
549
|
-
resultSet.statement.
|
|
550
|
-
|
|
562
|
+
if (!resultSet.statement.noPrefetch) {
|
|
563
|
+
resultSet.statement.requiresDefine = true;
|
|
564
|
+
resultSet.statement.noPrefetch = true;
|
|
565
|
+
}
|
|
551
566
|
}
|
|
552
567
|
}
|
|
553
568
|
}
|
|
@@ -580,10 +595,6 @@ class MessageWithData extends Message {
|
|
|
580
595
|
if (this.statement.numQueryVars % 8 > 0) {
|
|
581
596
|
numBytes += 1;
|
|
582
597
|
}
|
|
583
|
-
this.getBitVector(buf, numBytes);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
getBitVector(buf, numBytes) {
|
|
587
598
|
this.bitVector = Buffer.from(buf.readBytes(numBytes));
|
|
588
599
|
}
|
|
589
600
|
|
|
@@ -651,15 +662,15 @@ class MessageWithData extends Message {
|
|
|
651
662
|
const objType = variable.objType;
|
|
652
663
|
buf.writeUB4(objType.oid.length);
|
|
653
664
|
buf.writeBytesWithLength(objType.oid);
|
|
654
|
-
buf.
|
|
665
|
+
buf.writeUB2(objType.version);
|
|
655
666
|
} else {
|
|
656
667
|
buf.writeUB4(0); // OID
|
|
657
|
-
buf.
|
|
668
|
+
buf.writeUB2(0); // version
|
|
658
669
|
}
|
|
659
670
|
if (variable.type._csfrm !== 0) {
|
|
660
|
-
buf.
|
|
671
|
+
buf.writeUB2(constants.TNS_CHARSET_UTF8);
|
|
661
672
|
} else {
|
|
662
|
-
buf.
|
|
673
|
+
buf.writeUB2(0);
|
|
663
674
|
}
|
|
664
675
|
buf.writeUInt8(variable.type._csfrm);
|
|
665
676
|
buf.writeUB4(lobPrefetchLength); // max chars (LOB prefetch)
|
|
@@ -670,14 +681,14 @@ class MessageWithData extends Message {
|
|
|
670
681
|
}
|
|
671
682
|
|
|
672
683
|
writeBindParamsRow(buf, params, pos) {
|
|
673
|
-
|
|
684
|
+
const offset = this.offset;
|
|
674
685
|
let foundLong = false;
|
|
675
686
|
for (const bindInfo of params) {
|
|
676
687
|
if (bindInfo.isReturnBind)
|
|
677
688
|
continue;
|
|
678
|
-
|
|
689
|
+
const variable = bindInfo.bindVar;
|
|
679
690
|
if (variable.isArray) {
|
|
680
|
-
|
|
691
|
+
const numElements = variable.values.length;
|
|
681
692
|
buf.writeUB4(numElements);
|
|
682
693
|
for (let i = 0; i < numElements; i++) {
|
|
683
694
|
this.writeBindParamsColumn(buf, variable, variable.values[i]);
|
|
@@ -800,7 +811,7 @@ class MessageWithData extends Message {
|
|
|
800
811
|
for (let i = 0; i < numResults; i++) {
|
|
801
812
|
const numBytes = buf.readUInt8();
|
|
802
813
|
buf.skipBytes(numBytes);
|
|
803
|
-
|
|
814
|
+
const childResultSet = this.createCursorFromDescribe(buf);
|
|
804
815
|
childResultSet.statement.cursorId = buf.readUB2();
|
|
805
816
|
this.options.implicitResultSet.push(childResultSet);
|
|
806
817
|
}
|
|
@@ -352,7 +352,7 @@ class OsonFieldNamesSegment extends GrowableBuffer {
|
|
|
352
352
|
//---------------------------------------------------------------------------
|
|
353
353
|
_examineNode(value) {
|
|
354
354
|
if (Array.isArray(value)) {
|
|
355
|
-
for (
|
|
355
|
+
for (const element of value) {
|
|
356
356
|
this._examineNode(element);
|
|
357
357
|
}
|
|
358
358
|
} else if (value && Array.isArray(value.fields)) {
|
|
@@ -416,10 +416,11 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
416
416
|
//---------------------------------------------------------------------------
|
|
417
417
|
_encodeArray(value, fnamesSeg) {
|
|
418
418
|
this._encodeContainer(constants.TNS_JSON_TYPE_ARRAY, value.length);
|
|
419
|
-
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
419
|
+
const len = value.length * 4;
|
|
420
|
+
const pos = this.reserveBytes(len);
|
|
421
|
+
let offsetsBufPos = pos;
|
|
422
|
+
for (const element of value) {
|
|
423
|
+
this.buf.writeUInt32BE(this.pos, offsetsBufPos);
|
|
423
424
|
offsetsBufPos += 4;
|
|
424
425
|
this.encodeNode(element, fnamesSeg);
|
|
425
426
|
}
|
|
@@ -456,19 +457,21 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
456
457
|
_encodeObject(value, fnamesSeg) {
|
|
457
458
|
const numChildren = value.values.length;
|
|
458
459
|
this._encodeContainer(constants.TNS_JSON_TYPE_OBJECT, numChildren);
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
460
|
+
const len = numChildren * (fnamesSeg.fieldIdSize + 4);
|
|
461
|
+
const pos = this.reserveBytes(len);
|
|
462
|
+
let fieldIdOffset = pos;
|
|
463
|
+
let valueOffset = pos + (numChildren * fnamesSeg.fieldIdSize);
|
|
464
|
+
|
|
462
465
|
for (let i = 0; i < value.fields.length; i++) {
|
|
463
466
|
const fieldName = fnamesSeg.fieldNamesMap.get(value.fields[i]);
|
|
464
467
|
if (fnamesSeg.fieldIdSize == 1) {
|
|
465
|
-
buf[fieldIdOffset] = fieldName.fieldId;
|
|
468
|
+
this.buf[fieldIdOffset] = fieldName.fieldId;
|
|
466
469
|
} else if (fnamesSeg.fieldIdSize == 2) {
|
|
467
|
-
buf.writeUInt16BE(fieldName.fieldId, fieldIdOffset);
|
|
470
|
+
this.buf.writeUInt16BE(fieldName.fieldId, fieldIdOffset);
|
|
468
471
|
} else {
|
|
469
|
-
buf.writeUInt32BE(fieldName.fieldId, fieldIdOffset);
|
|
472
|
+
this.buf.writeUInt32BE(fieldName.fieldId, fieldIdOffset);
|
|
470
473
|
}
|
|
471
|
-
buf.writeUInt32BE(this.pos, valueOffset);
|
|
474
|
+
this.buf.writeUInt32BE(this.pos, valueOffset);
|
|
472
475
|
fieldIdOffset += fnamesSeg.fieldIdSize;
|
|
473
476
|
valueOffset += 4;
|
|
474
477
|
this.encodeNode(value.values[i], fnamesSeg);
|
|
@@ -627,12 +630,12 @@ class OsonEncoder extends GrowableBuffer {
|
|
|
627
630
|
this.writeUInt16BE(0);
|
|
628
631
|
|
|
629
632
|
// write array of hash ids
|
|
630
|
-
for (
|
|
633
|
+
for (const fieldName of fnamesSeg.fieldNames) {
|
|
631
634
|
this.writeUInt8(fieldName.hashId);
|
|
632
635
|
}
|
|
633
636
|
|
|
634
637
|
// write array of field name offsets
|
|
635
|
-
for (
|
|
638
|
+
for (const fieldName of fnamesSeg.fieldNames) {
|
|
636
639
|
if (fnamesSeg.pos < 65536) {
|
|
637
640
|
this.writeUInt16BE(fieldName.offset);
|
|
638
641
|
} else {
|
|
@@ -31,10 +31,10 @@ const { Buffer } = require('buffer');
|
|
|
31
31
|
const constants = require("./constants.js");
|
|
32
32
|
const oson = require('./oson.js');
|
|
33
33
|
const utils = require('./utils.js');
|
|
34
|
+
const errors = require("../../errors.js");
|
|
34
35
|
|
|
35
36
|
const TNS_BASE64_ALPHABET_ARRAY = Buffer.from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 'utf8');
|
|
36
37
|
|
|
37
|
-
|
|
38
38
|
/**
|
|
39
39
|
* Class used for byte chunks used in the ChunkedBytesBuffer.
|
|
40
40
|
*/
|
|
@@ -51,7 +51,7 @@ class BytesChunk {
|
|
|
51
51
|
if (remainder > 0) {
|
|
52
52
|
this.allocLen += (constants.CHUNKED_BYTES_CHUNK_SIZE - remainder);
|
|
53
53
|
}
|
|
54
|
-
this.buf = Buffer.
|
|
54
|
+
this.buf = Buffer.allocUnsafe(this.allocLen);
|
|
55
55
|
this.actualLen = 0;
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -132,6 +132,7 @@ class ChunkedBytesBuffer {
|
|
|
132
132
|
*
|
|
133
133
|
* @class ReadPacket
|
|
134
134
|
*/
|
|
135
|
+
|
|
135
136
|
class ReadPacket extends BaseBuffer {
|
|
136
137
|
|
|
137
138
|
/**
|
|
@@ -139,8 +140,9 @@ class ReadPacket extends BaseBuffer {
|
|
|
139
140
|
* @param {Object} adapter used for sending/receiving data
|
|
140
141
|
* @param {Object} capabilities
|
|
141
142
|
*/
|
|
143
|
+
|
|
142
144
|
constructor(nsi, caps) {
|
|
143
|
-
super(
|
|
145
|
+
super();
|
|
144
146
|
this.nsi = nsi;
|
|
145
147
|
this.caps = caps;
|
|
146
148
|
this.chunkedBytesBuf = new ChunkedBytesBuffer();
|
|
@@ -227,7 +229,7 @@ class ReadPacket extends BaseBuffer {
|
|
|
227
229
|
if (inChunkedRead) {
|
|
228
230
|
buf = this.chunkedBytesBuf.getBuf(numBytes);
|
|
229
231
|
} else {
|
|
230
|
-
buf = Buffer.
|
|
232
|
+
buf = Buffer.allocUnsafe(numBytes);
|
|
231
233
|
}
|
|
232
234
|
|
|
233
235
|
// copy the bytes to the buffer from the remainder of this packet
|
|
@@ -324,23 +326,23 @@ class ReadPacket extends BaseBuffer {
|
|
|
324
326
|
|
|
325
327
|
// Handle physical rowid
|
|
326
328
|
if (buf && buf[0] === 1) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
329
|
+
const rba = buf.readUInt32BE(1);
|
|
330
|
+
const partitionID = buf.readUInt16BE(5);
|
|
331
|
+
const blockNum = buf.readUInt32BE(7);
|
|
332
|
+
const slotNum = buf.readUInt16BE(11);
|
|
331
333
|
return utils.encodeRowID({rba, partitionID, blockNum, slotNum});
|
|
332
334
|
}
|
|
333
335
|
|
|
334
336
|
// handle logical rowid
|
|
335
337
|
let outputLen = Math.floor(inputLen / 3) * 4;
|
|
336
|
-
|
|
338
|
+
const remainder = inputLen % 3;
|
|
337
339
|
if (remainder === 1) {
|
|
338
340
|
outputLen += 1;
|
|
339
341
|
} else if (remainder === 2) {
|
|
340
342
|
outputLen += 3;
|
|
341
343
|
}
|
|
342
344
|
|
|
343
|
-
|
|
345
|
+
const outputValue = Buffer.allocUnsafe(outputLen);
|
|
344
346
|
inputLen -= 1;
|
|
345
347
|
outputValue[0] = 42;
|
|
346
348
|
outputOffset += 1;
|
|
@@ -385,16 +387,19 @@ class ReadPacket extends BaseBuffer {
|
|
|
385
387
|
}
|
|
386
388
|
|
|
387
389
|
readRowID() {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
+
const rba = this.readUB4();
|
|
391
|
+
const partitionID = this.readUB2();
|
|
390
392
|
this.skipUB1();
|
|
391
|
-
|
|
392
|
-
|
|
393
|
+
const blockNum = this.readUB4();
|
|
394
|
+
const slotNum = this.readUB2();
|
|
393
395
|
return {rba, partitionID, blockNum, slotNum};
|
|
394
396
|
}
|
|
395
397
|
|
|
396
398
|
skipBytesChunked() {
|
|
397
399
|
const numBytes = this.readUInt8();
|
|
400
|
+
if (numBytes === 0 || numBytes === constants.TNS_NULL_LENGTH_INDICATOR) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
398
403
|
if (numBytes !== constants.TNS_LONG_LENGTH_INDICATOR) {
|
|
399
404
|
this.skipBytes(numBytes);
|
|
400
405
|
} else {
|
|
@@ -407,6 +412,23 @@ class ReadPacket extends BaseBuffer {
|
|
|
407
412
|
}
|
|
408
413
|
}
|
|
409
414
|
|
|
415
|
+
readNullTerminatedBytes(maxSize = 50) {
|
|
416
|
+
let offset = 0;
|
|
417
|
+
const tmp = Buffer.allocUnsafe(maxSize);
|
|
418
|
+
while (offset < maxSize) {
|
|
419
|
+
tmp[offset] = this.readUInt8();
|
|
420
|
+
if (tmp[offset] === 0) {
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
offset = offset + 1;
|
|
424
|
+
}
|
|
425
|
+
if (offset === maxSize) {
|
|
426
|
+
const reason = `Byte Arrray Exceeded MaxSize ${maxSize}`;
|
|
427
|
+
errors.throwErr(errors.ERR_INTERNAL, reason);
|
|
428
|
+
}
|
|
429
|
+
return tmp.subarray(0, offset + 1);
|
|
430
|
+
}
|
|
431
|
+
|
|
410
432
|
}
|
|
411
433
|
|
|
412
434
|
|
|
@@ -43,7 +43,7 @@ function _convertBase64(result, value, size, offset) {
|
|
|
43
43
|
function encodeRowID(rowID) {
|
|
44
44
|
let offset = 0;
|
|
45
45
|
if (rowID.rba !== 0 || rowID.partitionID !== 0 || rowID.blockNum !== 0 || rowID.slotNum != 0) {
|
|
46
|
-
|
|
46
|
+
const result = Buffer.allocUnsafe(constants.TNS_MAX_ROWID_LENGTH);
|
|
47
47
|
offset = _convertBase64(result, rowID.rba, 6, offset);
|
|
48
48
|
offset = _convertBase64(result, rowID.partitionID, 3, offset);
|
|
49
49
|
offset = _convertBase64(result, rowID.blockNum, 6, offset);
|
|
@@ -59,7 +59,7 @@ function setObfuscatedValue(value) {
|
|
|
59
59
|
const bytes = Buffer.from(value, 'utf8');
|
|
60
60
|
const len = Buffer.byteLength(value);
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
const arr = [];
|
|
63
63
|
for (let i = 0; i < len; i++) {
|
|
64
64
|
arr.push(buf[i] ^ bytes[i]);
|
|
65
65
|
}
|
package/lib/thin/resultSet.js
CHANGED
|
@@ -68,10 +68,10 @@ class ThinResultSetImpl extends ResultSetImpl {
|
|
|
68
68
|
// Returns rows fetched to the common layer in array format
|
|
69
69
|
//---------------------------------------------------------------------------
|
|
70
70
|
_processRows(numRowsFetched) {
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const rows = [];
|
|
72
|
+
const bufferRowIndex = this.statement.bufferRowIndex;
|
|
73
73
|
for (let row = bufferRowIndex; row < bufferRowIndex + numRowsFetched; row++) {
|
|
74
|
-
|
|
74
|
+
const rowObj = [];
|
|
75
75
|
for (let col = 0; col < this.statement.numQueryVars; col++) {
|
|
76
76
|
rowObj.push(this.statement.queryVars[col].values[row]);
|
|
77
77
|
}
|
|
@@ -99,13 +99,13 @@ class ThinResultSetImpl extends ResultSetImpl {
|
|
|
99
99
|
} else {
|
|
100
100
|
// We fetch for the required number of row
|
|
101
101
|
options.fetchArraySize = options.fetchArraySize - (this.statement.bufferRowCount - this.statement.bufferRowIndex);
|
|
102
|
-
|
|
102
|
+
const prevBufferRowCount = this.statement.bufferRowCount;
|
|
103
103
|
if (this.statement.moreRowsToFetch && options.fetchArraySize > 0) {
|
|
104
104
|
await this._fetchMoreRows(options);
|
|
105
105
|
}
|
|
106
106
|
options.fetchArraySize = numRows || this.options.fetchArraySize;
|
|
107
107
|
if (prevBufferRowCount === this.statement.bufferRowCount) {
|
|
108
|
-
|
|
108
|
+
const numRowsFetched = this.statement.bufferRowCount - this.statement.bufferRowIndex;
|
|
109
109
|
this.statement.bufferRowCount = 0;
|
|
110
110
|
if (numRowsFetched > 0) {
|
|
111
111
|
return this._processRows(numRowsFetched);
|
|
@@ -113,7 +113,7 @@ class ThinResultSetImpl extends ResultSetImpl {
|
|
|
113
113
|
return [];
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
|
|
116
|
+
const numRowsFetched = this.statement.bufferRowCount - this.statement.bufferRowIndex;
|
|
117
117
|
return this._processRows(numRowsFetched);
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -54,6 +54,7 @@ class ConnectDescription {
|
|
|
54
54
|
class ConnStrategy {
|
|
55
55
|
constructor() {
|
|
56
56
|
this.nextOptToTry = 0;
|
|
57
|
+
this.reorderDescriptionList = 0;
|
|
57
58
|
this.retryCount = 0;
|
|
58
59
|
this.lastRetryCounter = 0;
|
|
59
60
|
this.lastRetryConnectDescription = 0;
|
|
@@ -95,9 +96,13 @@ class ConnStrategy {
|
|
|
95
96
|
this.retryCount = config.retryCount;
|
|
96
97
|
}
|
|
97
98
|
}
|
|
99
|
+
if (!this.reorderDescriptionList) {
|
|
100
|
+
this.descriptionList = SOLE_INST_DHCACHE.reorderDescriptionList(this.descriptionList);
|
|
101
|
+
this.reorderDescriptionList = true;
|
|
102
|
+
}
|
|
98
103
|
/* We try the address list at least once and upto (1 + retryCount) times */
|
|
99
104
|
for (let d = this.lastRetryConnectDescription; d < this.descriptionList.length; d++) {
|
|
100
|
-
|
|
105
|
+
const desc = this.descriptionList[d];
|
|
101
106
|
let cOpts = new Array();
|
|
102
107
|
cOpts = desc.getConnectOptions();
|
|
103
108
|
let delay = desc.delayInMillis;
|
|
@@ -108,8 +113,13 @@ class ConnStrategy {
|
|
|
108
113
|
}
|
|
109
114
|
}
|
|
110
115
|
for (let i = this.lastRetryCounter; i <= this.retryCount; ++i) {
|
|
116
|
+
//Conn options must be reordered only when all options are tried
|
|
117
|
+
// i.e for retry and before the first try.
|
|
118
|
+
if (this.nextOptToTry == 0) {
|
|
119
|
+
cOpts = SOLE_INST_DHCACHE.reorderAddresses(cOpts);
|
|
120
|
+
}
|
|
111
121
|
while (this.nextOptToTry < cOpts.length) {
|
|
112
|
-
|
|
122
|
+
const copt = cOpts[this.nextOptToTry];
|
|
113
123
|
this.lastRetryCounter = i;
|
|
114
124
|
this.lastRetryConnectDescription = d;
|
|
115
125
|
this.nextOptToTry++;
|
|
@@ -150,7 +160,7 @@ async function createNode(str) {
|
|
|
150
160
|
else
|
|
151
161
|
nvpair = str; //Already a NVPair
|
|
152
162
|
|
|
153
|
-
|
|
163
|
+
const arg = nvpair.name.toUpperCase();
|
|
154
164
|
let navobj = null;
|
|
155
165
|
switch (arg) {
|
|
156
166
|
case "ADDRESS":
|
|
@@ -170,8 +180,123 @@ async function createNode(str) {
|
|
|
170
180
|
`unknown top element ${arg}`);
|
|
171
181
|
}
|
|
172
182
|
navobj.initFromNVPair(nvpair);
|
|
173
|
-
|
|
183
|
+
const cs = new ConnStrategy();
|
|
174
184
|
await navobj.navigate(cs);
|
|
175
185
|
return cs;
|
|
176
186
|
}
|
|
177
|
-
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class DownHostsCache {
|
|
190
|
+
|
|
191
|
+
constructor() {
|
|
192
|
+
// Timeout for each item in the cache
|
|
193
|
+
this.DOWN_HOSTS_TIMEOUT = 600;
|
|
194
|
+
// Minimum amount of time between each refresh
|
|
195
|
+
this.MIN_TIME_BETWEEN_REFRESH = 60;
|
|
196
|
+
// DownHostsCache Map
|
|
197
|
+
this.downHostsCacheMap = new Map();
|
|
198
|
+
// Last Refresh Time
|
|
199
|
+
this.lastRefreshTime = 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Add an address to the cache
|
|
204
|
+
*
|
|
205
|
+
* @param connOption
|
|
206
|
+
* address to be cached
|
|
207
|
+
* @return Map with address as key and time of insertion as value
|
|
208
|
+
*/
|
|
209
|
+
markDownHost(addr) {
|
|
210
|
+
return this.downHostsCacheMap.set(addr, Date.now());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Remove elements older than DownHostsTimeout
|
|
214
|
+
refreshCache() {
|
|
215
|
+
if (Date.now() - this.MIN_TIME_BETWEEN_REFRESH * 1000 > this.lastRefreshTime) {
|
|
216
|
+
this.downHostsCacheMap.forEach((value, key) => {
|
|
217
|
+
const entryTime = value;
|
|
218
|
+
if (entryTime != null && ((Date.now() - this.DOWN_HOSTS_TIMEOUT * 1000) > entryTime)) {
|
|
219
|
+
this.downHostsCacheMap.delete(key);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
this.lastRefreshTime = Date.now();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Reorder addresses such that cached elements
|
|
228
|
+
* occur at the end of the array.
|
|
229
|
+
*/
|
|
230
|
+
reorderAddresses(cOpts) {
|
|
231
|
+
this.refreshCache();
|
|
232
|
+
|
|
233
|
+
let topIdx = 0, btmIdx = cOpts.length - 1;
|
|
234
|
+
|
|
235
|
+
while (topIdx < btmIdx) {
|
|
236
|
+
|
|
237
|
+
// increment topIdx if the address is not cached
|
|
238
|
+
while (topIdx <= btmIdx
|
|
239
|
+
&& !this.isDownHostsCached(cOpts[topIdx]))
|
|
240
|
+
topIdx++;
|
|
241
|
+
|
|
242
|
+
// decrement btmIdx if address is cached
|
|
243
|
+
while (btmIdx >= topIdx
|
|
244
|
+
&& this.isDownHostsCached(cOpts[btmIdx]))
|
|
245
|
+
btmIdx--;
|
|
246
|
+
|
|
247
|
+
// swap cached with uncached
|
|
248
|
+
if (topIdx < btmIdx)
|
|
249
|
+
[cOpts[topIdx], cOpts[btmIdx]] = [cOpts[btmIdx], cOpts[topIdx]];
|
|
250
|
+
|
|
251
|
+
}
|
|
252
|
+
return cOpts;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Return if a desc is cached.
|
|
256
|
+
* A desc is cached if all the connection options(addresses)
|
|
257
|
+
* in that description are cached.
|
|
258
|
+
*/
|
|
259
|
+
isDownDescCached(desc) {
|
|
260
|
+
const cOpts = desc.getConnectOptions();
|
|
261
|
+
for (let i = 0; i < cOpts.length; i++) {
|
|
262
|
+
if (!this.isDownHostsCached(cOpts[i]))
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Reorder description list such that description with all connection options in downcache
|
|
269
|
+
* is pushed to the end of the description list
|
|
270
|
+
*/
|
|
271
|
+
reorderDescriptionList(descs) {
|
|
272
|
+
this.refreshCache();
|
|
273
|
+
|
|
274
|
+
let topIdx = 0, btmIdx = descs.length - 1;
|
|
275
|
+
|
|
276
|
+
while (topIdx < btmIdx) {
|
|
277
|
+
|
|
278
|
+
// increment topIdx if the desc is not cached
|
|
279
|
+
while (topIdx <= btmIdx
|
|
280
|
+
&& !this.isDownDescCached(descs[topIdx]))
|
|
281
|
+
topIdx++;
|
|
282
|
+
|
|
283
|
+
// decrement btmIdx if desc is cached
|
|
284
|
+
while (btmIdx >= topIdx
|
|
285
|
+
&& this.isDownDescCached(descs[btmIdx]))
|
|
286
|
+
btmIdx--;
|
|
287
|
+
|
|
288
|
+
// swap cached with uncached
|
|
289
|
+
if (topIdx < btmIdx) {
|
|
290
|
+
[descs[topIdx], descs[btmIdx]] = [descs[btmIdx], descs[topIdx]];
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return descs;
|
|
294
|
+
}
|
|
295
|
+
// Return if a host is cached
|
|
296
|
+
isDownHostsCached(copt) {
|
|
297
|
+
return this.downHostsCacheMap.has(copt.host);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// Single instance
|
|
301
|
+
const SOLE_INST_DHCACHE = new DownHostsCache();
|
|
302
|
+
module.exports = { createNode, SOLE_INST_DHCACHE };
|