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.
Files changed (65) hide show
  1. package/README.md +2 -2
  2. package/build/Release/oracledb-6.2.0-darwin-x64.node +0 -0
  3. package/build/Release/oracledb-6.2.0-darwin-x64.node-buildinfo.txt +1 -0
  4. package/build/Release/oracledb-6.2.0-js-buildinfo.txt +1 -0
  5. package/build/Release/oracledb-6.2.0-linux-arm64.node +0 -0
  6. package/build/Release/oracledb-6.2.0-linux-arm64.node-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-6.2.0-linux-x64.node +0 -0
  8. package/build/Release/oracledb-6.2.0-linux-x64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-6.2.0-win32-x64.node +0 -0
  10. package/build/Release/oracledb-6.2.0-win32-x64.node-buildinfo.txt +1 -0
  11. package/examples/dbconfig.js +7 -14
  12. package/examples/example.js +0 -1
  13. package/lib/aqQueue.js +40 -26
  14. package/lib/connection.js +16 -4
  15. package/lib/dbObject.js +9 -0
  16. package/lib/errors.js +17 -13
  17. package/lib/impl/connection.js +9 -0
  18. package/lib/impl/resultset.js +7 -4
  19. package/lib/oracledb.js +26 -18
  20. package/lib/pool.js +7 -1
  21. package/lib/sodaCollection.js +12 -1
  22. package/lib/sodaOperation.js +16 -0
  23. package/lib/thin/connection.js +229 -182
  24. package/lib/thin/dbObject.js +4 -1
  25. package/lib/thin/pool.js +153 -68
  26. package/lib/thin/protocol/buffer.js +83 -64
  27. package/lib/thin/protocol/constants.js +3 -14
  28. package/lib/thin/protocol/encryptDecrypt.js +17 -17
  29. package/lib/thin/protocol/messages/auth.js +21 -23
  30. package/lib/thin/protocol/messages/base.js +29 -45
  31. package/lib/thin/protocol/messages/connectionCookie.js +76 -0
  32. package/lib/thin/protocol/messages/dataType.js +13 -9
  33. package/lib/thin/protocol/messages/execute.js +7 -7
  34. package/lib/thin/protocol/messages/index.js +2 -0
  35. package/lib/thin/protocol/messages/protocol.js +31 -20
  36. package/lib/thin/protocol/messages/withData.js +58 -47
  37. package/lib/thin/protocol/oson.js +17 -14
  38. package/lib/thin/protocol/packet.js +36 -14
  39. package/lib/thin/protocol/utils.js +2 -2
  40. package/lib/thin/resultSet.js +6 -6
  41. package/lib/thin/sqlnet/connStrategy.js +130 -5
  42. package/lib/thin/sqlnet/constants.js +2 -0
  43. package/lib/thin/sqlnet/ezConnectResolver.js +29 -61
  44. package/lib/thin/sqlnet/navNodes.js +34 -27
  45. package/lib/thin/sqlnet/networkSession.js +46 -47
  46. package/lib/thin/sqlnet/ntTcp.js +30 -14
  47. package/lib/thin/sqlnet/nvStrToNvPair.js +16 -16
  48. package/lib/thin/sqlnet/packet.js +10 -3
  49. package/lib/thin/sqlnet/paramParser.js +9 -9
  50. package/lib/thin/sqlnet/sessionAtts.js +4 -0
  51. package/lib/thin/statement.js +345 -113
  52. package/lib/thin/util.js +23 -86
  53. package/lib/transformer.js +3 -1
  54. package/lib/util.js +4 -13
  55. package/lib/version.js +2 -2
  56. package/package.json +1 -1
  57. package/build/Release/oracledb-6.0.3-darwin-x64.node +0 -0
  58. package/build/Release/oracledb-6.0.3-darwin-x64.node-buildinfo.txt +0 -1
  59. package/build/Release/oracledb-6.0.3-js-buildinfo.txt +0 -1
  60. package/build/Release/oracledb-6.0.3-linux-arm64.node +0 -0
  61. package/build/Release/oracledb-6.0.3-linux-arm64.node-buildinfo.txt +0 -1
  62. package/build/Release/oracledb-6.0.3-linux-x64.node +0 -0
  63. package/build/Release/oracledb-6.0.3-linux-x64.node-buildinfo.txt +0 -1
  64. package/build/Release/oracledb-6.0.3-win32-x64.node +0 -0
  65. 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
- processDescribeInfo(buf, resultSet) {
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.skipUB1(); // skip repeated length
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 === null || this.bitVector === undefined) {
289
+ if (!this.bitVector) {
271
290
  return false;
272
291
  }
273
- let byteNum = Math.floor(columnName / 8);
274
- let bitNum = columnName % 8;
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 (let [col, variable] of this.outVariables.entries()) {
299
+ for (const [col, variable] of this.outVariables.entries()) {
281
300
  if (variable.isArray) {
282
301
  variable.numElementsInArray = buf.readUB4();
283
- let values = new Array(variable.numElementsInArray).fill(null);
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
- let numRows = buf.readUB4();
291
- let values = Array(numRows).fill(null);
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
- let bindInfo = this.statement.bindInfoList[i];
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
- let numBytes = buf.readUInt8();
405
+ const numBytes = buf.readUInt8();
387
406
  if (isNullLength(numBytes)) {
388
407
  colValue = null;
389
408
  } else {
390
- let rowid = buf.readRowID();
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
- let numBytes = buf.readUInt8();
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
- let bvalue = buf.readUB4();
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
- errors.throwErr(errors.ERR_INSUFFICIENT_BUFFER_FOR_BINDS);
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
- let numRows = buf.readUB4();
501
- let rowCounts = this.options.dmlRowCounts = [];
519
+ const numRows = buf.readUB4();
520
+ const rowCounts = this.options.dmlRowCounts = [];
502
521
  for (let i = 0; i < numRows; i++) {
503
- let rowCount = buf.readUB8();
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
- for (let pos = 0; pos < variable.numElementsInArray; pos++) {
524
- if (variable.outConverter) {
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.requiresDefine = true;
550
- resultSet.statement.noPrefetch = true;
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.writeUB4(objType.version);
665
+ buf.writeUB2(objType.version);
655
666
  } else {
656
667
  buf.writeUB4(0); // OID
657
- buf.writeUB4(0); // version
668
+ buf.writeUB2(0); // version
658
669
  }
659
670
  if (variable.type._csfrm !== 0) {
660
- buf.writeUB4(constants.TNS_CHARSET_UTF8);
671
+ buf.writeUB2(constants.TNS_CHARSET_UTF8);
661
672
  } else {
662
- buf.writeUB4(0);
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
- let offset = this.offset;
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
- let variable = bindInfo.bindVar;
689
+ const variable = bindInfo.bindVar;
679
690
  if (variable.isArray) {
680
- let numElements = variable.values.length;
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
- let childResultSet = this.createCursorFromDescribe(buf);
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 (let element of value) {
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
- let offsetsBufPos = 0;
420
- const offsetsBuf = this.reserveBytes(value.length * 4);
421
- for (let element of value) {
422
- offsetsBuf.writeUInt32BE(this.pos, offsetsBufPos);
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
- let fieldIdOffset = 0;
460
- let valueOffset = numChildren * fnamesSeg.fieldIdSize;
461
- const buf = this.reserveBytes(numChildren * (fnamesSeg.fieldIdSize + 4));
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 (let fieldName of fnamesSeg.fieldNames) {
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 (let fieldName of fnamesSeg.fieldNames) {
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.alloc(this.allocLen);
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(nsi.sAtts.sdu);
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.alloc(numBytes);
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
- let rba = buf.readUInt32BE(1);
328
- let partitionID = buf.readUInt16BE(5);
329
- let blockNum = buf.readUInt32BE(7);
330
- let slotNum = buf.readUInt16BE(11);
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
- let remainder = inputLen % 3;
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
- let outputValue = Buffer.alloc(outputLen);
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
- let rba = this.readUB4();
389
- let partitionID = this.readUB2();
390
+ const rba = this.readUB4();
391
+ const partitionID = this.readUB2();
390
392
  this.skipUB1();
391
- let blockNum = this.readUB4();
392
- let slotNum = this.readUB2();
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
- let result = Buffer.alloc(constants.TNS_MAX_ROWID_LENGTH);
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
- let arr = [];
62
+ const arr = [];
63
63
  for (let i = 0; i < len; i++) {
64
64
  arr.push(buf[i] ^ bytes[i]);
65
65
  }
@@ -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
- let rows = [];
72
- let bufferRowIndex = this.statement.bufferRowIndex;
71
+ const rows = [];
72
+ const bufferRowIndex = this.statement.bufferRowIndex;
73
73
  for (let row = bufferRowIndex; row < bufferRowIndex + numRowsFetched; row++) {
74
- let rowObj = [];
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
- let prevBufferRowCount = this.statement.bufferRowCount;
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
- let numRowsFetched = this.statement.bufferRowCount - this.statement.bufferRowIndex;
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
- let numRowsFetched = this.statement.bufferRowCount - this.statement.bufferRowIndex;
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
- let desc = this.descriptionList[d];
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
- let copt = cOpts[this.nextOptToTry];
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
- let arg = nvpair.name.toUpperCase();
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
- let cs = new ConnStrategy();
183
+ const cs = new ConnStrategy();
174
184
  await navobj.navigate(cs);
175
185
  return cs;
176
186
  }
177
- module.exports = { createNode };
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 };