oracledb 6.7.2 → 6.9.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 (84) hide show
  1. package/LICENSE.txt +1 -1
  2. package/NOTICE.txt +1 -1
  3. package/README.md +39 -3
  4. package/build/Release/oracledb-6.9.0-darwin-arm64.node +0 -0
  5. package/build/Release/oracledb-6.9.0-darwin-arm64.node-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-6.9.0-darwin-x64.node +0 -0
  7. package/build/Release/oracledb-6.9.0-darwin-x64.node-buildinfo.txt +1 -0
  8. package/build/Release/oracledb-6.9.0-js-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-6.9.0-linux-arm64.node +0 -0
  10. package/build/Release/oracledb-6.9.0-linux-arm64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-6.9.0-linux-x64.node +0 -0
  12. package/build/Release/oracledb-6.9.0-linux-x64.node-buildinfo.txt +1 -0
  13. package/build/Release/oracledb-6.9.0-win32-x64.node +0 -0
  14. package/build/Release/oracledb-6.9.0-win32-x64.node-buildinfo.txt +1 -0
  15. package/lib/aqMessage.js +10 -1
  16. package/lib/configProviders/file.js +72 -0
  17. package/lib/connection.js +153 -4
  18. package/lib/dbObject.js +51 -10
  19. package/lib/errors.js +144 -36
  20. package/lib/impl/connection.js +24 -1
  21. package/lib/impl/datahandlers/buffer.js +92 -1
  22. package/lib/impl/datahandlers/constants.js +2 -0
  23. package/lib/impl/datahandlers/oson.js +15 -1
  24. package/lib/impl/datahandlers/vector.js +72 -27
  25. package/lib/impl/pool.js +10 -1
  26. package/lib/impl/resultset.js +5 -4
  27. package/lib/lob.js +9 -1
  28. package/lib/oracledb.js +209 -20
  29. package/lib/pool.js +12 -1
  30. package/lib/poolStatistics.js +3 -1
  31. package/lib/settings.js +5 -1
  32. package/lib/thin/connection.js +130 -12
  33. package/lib/thin/dbObject.js +46 -13
  34. package/lib/thin/lob.js +2 -3
  35. package/lib/thin/pool.js +112 -31
  36. package/lib/thin/protocol/capabilities.js +8 -4
  37. package/lib/thin/protocol/constants.js +31 -1
  38. package/lib/thin/protocol/messages/auth.js +42 -6
  39. package/lib/thin/protocol/messages/base.js +98 -9
  40. package/lib/thin/protocol/messages/dataType.js +2 -1
  41. package/lib/thin/protocol/messages/execute.js +46 -4
  42. package/lib/thin/protocol/messages/fastAuth.js +6 -1
  43. package/lib/thin/protocol/messages/lobOp.js +5 -22
  44. package/lib/thin/protocol/messages/logOff.js +0 -1
  45. package/lib/thin/protocol/messages/sessionRelease.js +0 -1
  46. package/lib/thin/protocol/messages/transactionSwitch.js +12 -4
  47. package/lib/thin/protocol/messages/withData.js +10 -1
  48. package/lib/thin/protocol/packet.js +1 -1
  49. package/lib/thin/protocol/protocol.js +2 -1
  50. package/lib/thin/sqlnet/ANO.js +438 -0
  51. package/lib/thin/sqlnet/connStrategy.js +2 -1
  52. package/lib/thin/sqlnet/constants.js +5 -1
  53. package/lib/thin/sqlnet/ezConnectResolver.js +4 -2
  54. package/lib/thin/sqlnet/navNodes.js +22 -3
  55. package/lib/thin/sqlnet/networkSession.js +94 -16
  56. package/lib/thin/sqlnet/ntTcp.js +50 -3
  57. package/lib/thin/sqlnet/packet.js +32 -4
  58. package/lib/thin/sqlnet/sessionAtts.js +24 -2
  59. package/lib/thin/util.js +3 -8
  60. package/lib/transformer.js +13 -2
  61. package/lib/types.js +220 -8
  62. package/lib/util.js +77 -3
  63. package/lib/version.js +2 -2
  64. package/package/install.js +3 -3
  65. package/package/prunebinaries.js +6 -12
  66. package/package.json +2 -2
  67. package/{lib/configProviders/azure.js → plugins/configProviders/azure/index.js} +70 -63
  68. package/plugins/configProviders/azurevault/index.js +84 -0
  69. package/{lib → plugins}/configProviders/base.js +13 -3
  70. package/{lib/configProviders/ociobject.js → plugins/configProviders/ociobject/index.js} +43 -78
  71. package/plugins/configProviders/ocivault/index.js +100 -0
  72. package/plugins/token/extensionAzure/index.js +101 -0
  73. package/plugins/token/extensionOci/index.js +210 -0
  74. package/build/Release/oracledb-6.7.2-darwin-arm64.node +0 -0
  75. package/build/Release/oracledb-6.7.2-darwin-arm64.node-buildinfo.txt +0 -1
  76. package/build/Release/oracledb-6.7.2-darwin-x64.node +0 -0
  77. package/build/Release/oracledb-6.7.2-darwin-x64.node-buildinfo.txt +0 -1
  78. package/build/Release/oracledb-6.7.2-js-buildinfo.txt +0 -1
  79. package/build/Release/oracledb-6.7.2-linux-arm64.node +0 -0
  80. package/build/Release/oracledb-6.7.2-linux-arm64.node-buildinfo.txt +0 -1
  81. package/build/Release/oracledb-6.7.2-linux-x64.node +0 -0
  82. package/build/Release/oracledb-6.7.2-linux-x64.node-buildinfo.txt +0 -1
  83. package/build/Release/oracledb-6.7.2-win32-x64.node +0 -0
  84. package/build/Release/oracledb-6.7.2-win32-x64.node-buildinfo.txt +0 -1
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -85,6 +85,9 @@ class ThinConnectionImpl extends ConnectionImpl {
85
85
  // immediate close of open socket on failure
86
86
  // exception won't be thrown to user
87
87
  this.nscon.disconnect(sqlNetConstants.NSFIMM);
88
+ // If connection is associated with a pool, we release it
89
+ if (this._pool)
90
+ await this._pool.release(this);
88
91
  }
89
92
  }
90
93
 
@@ -299,13 +302,20 @@ class ThinConnectionImpl extends ConnectionImpl {
299
302
  }
300
303
 
301
304
  // process the type code.
305
+ let tempPrecision, tempScale;
302
306
  switch (attrType) {
303
307
  case constants.TNS_OBJ_TDS_TYPE_NUMBER:
304
- metaData.precision = buf.readInt8();
305
- metaData.scale = buf.readInt8();
308
+ tempPrecision = buf.readInt8();
309
+ tempScale = buf.readInt8();
310
+ if (tempPrecision || tempScale) {
311
+ metaData.precision = tempPrecision;
312
+ metaData.scale = tempScale;
313
+ }
306
314
  return types.DB_TYPE_NUMBER;
307
315
  case constants.TNS_OBJ_TDS_TYPE_FLOAT:
308
- metaData.precision = buf.readInt8();
316
+ tempPrecision = buf.readInt8();
317
+ if (tempPrecision)
318
+ metaData.precision = tempPrecision;
309
319
  return types.DB_TYPE_NUMBER;
310
320
  case constants.TNS_OBJ_TDS_TYPE_VARCHAR:
311
321
  case constants.TNS_OBJ_TDS_TYPE_CHAR:
@@ -433,11 +443,24 @@ class ThinConnectionImpl extends ConnectionImpl {
433
443
  case
434
444
  when data_type in
435
445
  ('CHAR', 'NCHAR', 'VARCHAR2', 'NVARCHAR2', 'RAW')
436
- then data_length
446
+ then data_length
437
447
  else 0
438
448
  end,
439
- nvl(data_precision, 0),
440
- nvl(data_scale, 0)
449
+ case
450
+ when data_precision is null and data_scale is null
451
+ then 0
452
+ when data_precision is null
453
+ then 38
454
+ else data_precision
455
+ end,
456
+ case
457
+ when data_precision is null and data_scale is null
458
+ and data_type = 'NUMBER'
459
+ then -127
460
+ when data_scale is null
461
+ then 0
462
+ else data_scale
463
+ end
441
464
  from all_tab_cols
442
465
  where owner = :owner
443
466
  and table_name = :name
@@ -670,10 +693,36 @@ class ThinConnectionImpl extends ConnectionImpl {
670
693
  this._partialDbObjectTypes.push(attr.typeClass);
671
694
  }
672
695
  } else {
673
- attr.type = types.getTypeByColumnTypeName(attrInfo.dataType);
674
- attr.maxSize = attrInfo.maxSize;
675
- attr.precision = attrInfo.dataPrecision;
676
- attr.scale = attrInfo.dataScale;
696
+ if (attrInfo.dataType === 'INTEGER' || attrInfo.dataType === 'SMALLINT') {
697
+ attr.type = types.DB_TYPE_NUMBER;
698
+ attr.precision = 38;
699
+ attr.scale = 0;
700
+ } else if (attrInfo.dataType === 'REAL') {
701
+ attr.type = types.DB_TYPE_NUMBER;
702
+ attr.precision = 63;
703
+ attr.scale = -127;
704
+ } else if (attrInfo.dataType === 'DOUBLE PRECISION' || attrInfo.dataType === 'FLOAT') {
705
+ attr.type = types.DB_TYPE_NUMBER;
706
+ // the database sends type name "FLOAT" instead of type name "REAL"
707
+ // when looking at table metadata but not when examining database
708
+ // object attribute metadata so account for that here
709
+ if (attrInfo.dataPrecision != null || attrInfo.dataPrecision != undefined)
710
+ attr.precision = Number(attrInfo.dataPrecision);
711
+ else
712
+ attr.precision = 126;
713
+ attr.scale = -127;
714
+ } else {
715
+ attr.type = types.getTypeByColumnTypeName(attrInfo.dataType);
716
+ if (attrInfo.maxSize != null || attrInfo.maxSize != undefined) {
717
+ const tempMaxSize = Number(attrInfo.maxSize);
718
+ if (tempMaxSize)
719
+ attr.maxSize = tempMaxSize;
720
+ }
721
+ if (attr.type === types.DB_TYPE_NUMBER) {
722
+ attr.precision = Number(attrInfo.dataPrecision);
723
+ attr.scale = Number(attrInfo.dataScale);
724
+ }
725
+ }
677
726
  }
678
727
  attributes.push(attr);
679
728
  }
@@ -715,6 +764,9 @@ class ThinConnectionImpl extends ConnectionImpl {
715
764
  await this._protocol.breakMessage();
716
765
  }
717
766
 
767
+ isCompressionEnabled() {
768
+ return this.nscon.compressionEnabled;
769
+ }
718
770
  isHealthy() {
719
771
  try {
720
772
  if (this.nscon.recvInbandNotif() === 0)
@@ -769,6 +821,7 @@ class ThinConnectionImpl extends ConnectionImpl {
769
821
  this.remoteAddress = '';
770
822
  this.comboKey = null; // used in changePassword API
771
823
  this.tpcContext = null;
824
+ this._sessionlessData = null;
772
825
 
773
826
  this.nscon = new nsi();
774
827
  finalizationRegistry.register(this, this.nscon);
@@ -839,7 +892,7 @@ class ThinConnectionImpl extends ConnectionImpl {
839
892
  this.nscon.endOfRequestSupport = endOfRequestSupport;
840
893
  await this._protocol._processMessage(authMessage);
841
894
  }
842
- if (!params.token) { // non-token Authentication
895
+ if (!params.externalAuth) { // non-token Authentication
843
896
  await this._protocol._processMessage(authMessage); // OAUTH
844
897
  }
845
898
  } catch (err) {
@@ -1230,6 +1283,55 @@ class ThinConnectionImpl extends ConnectionImpl {
1230
1283
  errors.throwErr(errors.ERR_UNKNOWN_TRANSACTION_STATE, message.state);
1231
1284
  }
1232
1285
  }
1286
+ //---------------------------------------------------------------------------
1287
+ // Ensure no sessionless transaction was started through server procedure
1288
+ //---------------------------------------------------------------------------
1289
+ _validateSessionlessState() {
1290
+ if (this._sessionlessData?.startedOnServer) {
1291
+ errors.throwErr(errors.ERR_SESSIONLESS_DIFFERING_METHODS);
1292
+ }
1293
+ }
1294
+
1295
+ //---------------------------------------------------------------------------
1296
+ // Begin/Resume a sessionless transaction with provided transactionId
1297
+ //---------------------------------------------------------------------------
1298
+ async startSessionlessTransaction(transactionId, timeout, flags,
1299
+ deferRoundTrip) {
1300
+ this._validateSessionlessState();
1301
+ if (this._sessionlessData)
1302
+ errors.throwErr(errors.ERR_SESSIONLESS_ALREADY_ACTIVE);
1303
+ const message = new messages.TransactionSwitchMessage(this);
1304
+ message.xid = {
1305
+ globalTransactionId: transactionId,
1306
+ branchQualifier: "",
1307
+ formatId: constants.TNS_TPC_TRANS_SESSIONLESS_FORMAT
1308
+ };
1309
+ message.timeout = timeout;
1310
+ message.operation = constants.TNS_TPC_TXN_START;
1311
+ message.flags = constants.TNS_TPC_TRANS_SESSIONLESS | flags;
1312
+ if (deferRoundTrip) {
1313
+ message.messageType = constants.TNS_MSG_TYPE_PIGGYBACK;
1314
+ this._sessionlessData = {
1315
+ piggyback: message,
1316
+ pending: true
1317
+ };
1318
+ } else {
1319
+ await this._protocol._processMessage(message);
1320
+ }
1321
+ }
1322
+
1323
+ //---------------------------------------------------------------------------
1324
+ // Suspend the active sessionless transaction
1325
+ //---------------------------------------------------------------------------
1326
+ async suspendSessionlessTransaction() {
1327
+ this._validateSessionlessState();
1328
+ if (!this._sessionlessData)
1329
+ errors.throwErr(errors.ERR_SESSIONLESS_INACTIVE);
1330
+ const message = new messages.TransactionSwitchMessage(this);
1331
+ message.operation = constants.TNS_TPC_TXN_DETACH;
1332
+ message.flags = constants.TNS_TPC_TRANS_SESSIONLESS;
1333
+ await this._protocol._processMessage(message);
1334
+ }
1233
1335
 
1234
1336
 
1235
1337
  //---------------------------------------------------------------------------
@@ -1270,6 +1372,14 @@ class ThinConnectionImpl extends ConnectionImpl {
1270
1372
  return this.internalName;
1271
1373
  }
1272
1374
 
1375
+ //---------------------------------------------------------------------------
1376
+ // Returns the Logical Transaction ID (ltxid) associated with the connection.
1377
+ // Used with Oracle Database Transaction Guard feature.
1378
+ //---------------------------------------------------------------------------
1379
+ getLTXID() {
1380
+ return this._ltxid;
1381
+ }
1382
+
1273
1383
  //---------------------------------------------------------------------------
1274
1384
  // Returns the Oracle Database domain name associated with the connection.
1275
1385
  //---------------------------------------------------------------------------
@@ -1312,6 +1422,14 @@ class ThinConnectionImpl extends ConnectionImpl {
1312
1422
  return this.maxOpenCursors;
1313
1423
  }
1314
1424
 
1425
+ //---------------------------------------------------------------------------
1426
+ // Returns the maximum length of identifiers supported by the database to
1427
+ // which this connection has been established.
1428
+ //---------------------------------------------------------------------------
1429
+ getMaxIdentifierLength() {
1430
+ return this.maxIdentifierLength;
1431
+ }
1432
+
1315
1433
  //---------------------------------------------------------------------------
1316
1434
  // Returns the Oracle Database service name associated with the connection.
1317
1435
  //---------------------------------------------------------------------------
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2023, 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2023, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -55,10 +55,10 @@ class DbObjectPickleBuffer extends GrowableBuffer {
55
55
  // Reads the next byte and checks to see if the value is atomically null. If
56
56
  // not, the byte is returned to the buffer for further processing.
57
57
  //---------------------------------------------------------------------------
58
- getIsAtomicNull() {
58
+ getIsAtomicNull(isCollection) {
59
59
  const value = this.readUInt8();
60
60
  if (value === constants.TNS_OBJ_ATOMIC_NULL ||
61
- value === constants.TNS_NULL_LENGTH_INDICATOR) {
61
+ (isCollection && value === constants.TNS_NULL_LENGTH_INDICATOR)) {
62
62
  return true;
63
63
  }
64
64
  this.pos -= 1;
@@ -365,13 +365,10 @@ class ThinDbObjectImpl extends DbObjectImpl {
365
365
  // Unpacks a single value and returns it.
366
366
  //---------------------------------------------------------------------------
367
367
  _unpackValue(buf, type, typeClass) {
368
- let isNull, obj, value;
368
+ let isNull, obj, value, isCollection;
369
369
  switch (type) {
370
370
  case types.DB_TYPE_NUMBER:
371
- value = buf.readOracleNumber();
372
- if (value !== null)
373
- value = parseFloat(value);
374
- return value;
371
+ return buf.readOracleNumber();
375
372
  case types.DB_TYPE_BINARY_INTEGER:
376
373
  return buf.readBinaryInteger();
377
374
  case types.DB_TYPE_VARCHAR:
@@ -399,14 +396,16 @@ class ThinDbObjectImpl extends DbObjectImpl {
399
396
  return buf.readBool();
400
397
  case types.DB_TYPE_OBJECT:
401
398
  case types.DB_TYPE_XMLTYPE:
402
- isNull = buf.getIsAtomicNull();
399
+ obj = new ThinDbObjectImpl(typeClass);
400
+ isCollection = obj._objType.isCollection ||
401
+ this._objType.isCollection;
402
+ isNull = buf.getIsAtomicNull(isCollection);
403
403
  if (isNull)
404
404
  return null;
405
- obj = new ThinDbObjectImpl(typeClass);
406
405
  if (obj._objType.isXmlType) {
407
406
  return readXML(obj._objType._connection, buf.readBytesWithLength());
408
407
  }
409
- if (obj._objType.isCollection || this._objType.isCollection) {
408
+ if (isCollection) {
410
409
  obj.packedData = Buffer.from(buf.readBytesWithLength());
411
410
  } else {
412
411
  obj._unpackDataFromBuf(buf);
@@ -429,7 +428,7 @@ class ThinDbObjectImpl extends DbObjectImpl {
429
428
  if (objType.maxNumElements > 0 &&
430
429
  this.unpackedArray.length >= objType.maxNumElements) {
431
430
  errors.throwErr(errors.ERR_INVALID_COLL_INDEX_SET,
432
- this.unpackedArray.length, 0, objType.maxNumElements);
431
+ this.unpackedArray.length, 0, objType.maxNumElements - 1);
433
432
  }
434
433
  this.unpackedArray.push(value);
435
434
  } else {
@@ -446,6 +445,26 @@ class ThinDbObjectImpl extends DbObjectImpl {
446
445
  }
447
446
  }
448
447
 
448
+ //---------------------------------------------------------------------------
449
+ // copy
450
+ //
451
+ // Creates and returns a copy of the ThinDBObjectImpl object. The copy is
452
+ // independent of the original object that was copied.
453
+ //---------------------------------------------------------------------------
454
+ copy() {
455
+ // We send in marshalled data of the original object to the constructor
456
+ // when we create the object copy
457
+ const newObjImpl = new ThinDbObjectImpl(this._objType, this._getPackedData());
458
+
459
+ // Set other properties
460
+ newObjImpl.toid = this.toid;
461
+ newObjImpl.flags = this.flags;
462
+ newObjImpl.imageFlags = this.imageFlags;
463
+ newObjImpl.imageVersion = this.imageVersion;
464
+
465
+ return newObjImpl;
466
+ }
467
+
449
468
  //---------------------------------------------------------------------------
450
469
  // deleteElement()
451
470
  //
@@ -459,7 +478,7 @@ class ThinDbObjectImpl extends DbObjectImpl {
459
478
  }
460
479
  this.unpackedArray.splice(index, 1);
461
480
  } else {
462
- this._unpackedAssocKeys = undefined;
481
+ this.unpackedAssocKeys = undefined;
463
482
  this.unpackedAssocArray.delete(index);
464
483
  }
465
484
  }
@@ -610,6 +629,20 @@ class ThinDbObjectImpl extends DbObjectImpl {
610
629
  return result;
611
630
  }
612
631
 
632
+ //---------------------------------------------------------------------------
633
+ // getLength
634
+ //
635
+ // Gets the size of the database object if it is a collection. Else returns
636
+ // undefined.
637
+ //---------------------------------------------------------------------------
638
+ getLength() {
639
+ this._ensureUnpacked();
640
+ if (this.unpackedArray)
641
+ return this.unpackedArray.length;
642
+ if (this.unpackedAssocArray)
643
+ return this.unpackedAssocArray.size;
644
+ }
645
+
613
646
  //---------------------------------------------------------------------------
614
647
  // hasElement()
615
648
  //
package/lib/thin/lob.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -220,10 +220,9 @@ class ThinLobImpl extends LobImpl {
220
220
  const options = {
221
221
  operation: constants.TNS_LOB_OP_CREATE_TEMP,
222
222
  sourceLobImpl: this,
223
- amount: constants.TNS_DURATION_SESSION,
224
223
  destOffset: dbType._oraTypeNum,
225
224
  sourceOffset: dbType._csfrm,
226
- sendAmount: true
225
+ destLength: constants.TNS_DURATION_SESSION
227
226
  };
228
227
  await this._sendMessage(options);
229
228
  await this._getChunkSizeAsync();
package/lib/thin/pool.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -35,7 +35,6 @@ const util = require('../util.js');
35
35
  const thinUtil = require('./util.js');
36
36
  const {getConnectionInfo} = require('./sqlnet/networkSession.js');
37
37
  const crypto = require('crypto');
38
- const EventEmitter = require('events');
39
38
  const Timers = require('timers');
40
39
 
41
40
  class ThinPoolImpl extends PoolImpl {
@@ -57,6 +56,7 @@ class ThinPoolImpl extends PoolImpl {
57
56
  this._poolTimeout = params.poolTimeout;
58
57
  this._poolPingInterval = params.poolPingInterval;
59
58
  this._poolPingTimeout = params.poolPingTimeout;
59
+ this._maxLifetimeSession = params.maxLifetimeSession || 0;
60
60
  this._stmtCacheSize = params.stmtCacheSize;
61
61
 
62
62
  // The user Config filterd from common layer is cached except
@@ -77,6 +77,9 @@ class ThinPoolImpl extends PoolImpl {
77
77
  this._schedulerJob = null;
78
78
  this._poolCloseWaiter = null;
79
79
  this._pendingRequests = [];
80
+ this._connsToDrop = [];
81
+ this._bgCleaner = null;
82
+ this._poolCloseCleaner = null;
80
83
 
81
84
  // password obfuscation
82
85
  if (this._password !== undefined) {
@@ -117,11 +120,6 @@ class ThinPoolImpl extends PoolImpl {
117
120
  this._accessTokenConfig = params.accessTokenConfig;
118
121
  this._isDRCPEnabled = false;
119
122
  this._implicitPool = null;
120
- this.eventEmitter = new EventEmitter();
121
- // listener to remove dead or idle connections
122
- this.eventEmitter.on('_removePoolConnection', async (connImpl) => {
123
- await this._destroy(connImpl);
124
- });
125
123
  }
126
124
 
127
125
  //---------------------------------------------------------------------------
@@ -142,6 +140,9 @@ class ThinPoolImpl extends PoolImpl {
142
140
  // create a background task. It will create minimum connections in the pool
143
141
  // and expand the pool as required.
144
142
  this.bgThreadFunc();
143
+ // create a background task to remove connections that are to in
144
+ // _connsToDrop, reason for dropping may be poolTimeout or poolMax decrease
145
+ this._bgConnCleaner();
145
146
  }
146
147
 
147
148
  //---------------------------------------------------------------------------
@@ -197,7 +198,11 @@ class ThinPoolImpl extends PoolImpl {
197
198
  protocolUtil.getDeobfuscatedValue(this._token, this._obfuscatedToken);
198
199
  if (util.isTokenExpired(clonedAttrs.token)) {
199
200
  if (typeof this._accessTokenFn === 'function') {
200
- accessToken = await this._accessTokenFn(true, this._accessTokenConfig);
201
+ try {
202
+ accessToken = await this._accessTokenFn(true, this._accessTokenConfig);
203
+ } catch (error) {
204
+ errors.throwWrapErr(error, errors.ERR_ACCESS_TOKEN);
205
+ }
201
206
  if (typeof accessToken === 'string') {
202
207
  clonedAttrs.token = accessToken;
203
208
  if (util.isTokenExpired(clonedAttrs.token)) {
@@ -211,7 +216,7 @@ class ThinPoolImpl extends PoolImpl {
211
216
  }
212
217
  } else if (typeof accessToken === 'object') {
213
218
  clonedAttrs.token = accessToken.token;
214
- clonedAttrs.privateKey = accessToken.privateKey;
219
+ clonedAttrs.privateKey = util.denormalizePrivateKey(accessToken.privateKey);
215
220
  if (util.isTokenExpired(clonedAttrs.token)) {
216
221
  // IAM token is expired
217
222
  errors.throwErr(errors.ERR_TOKEN_HAS_EXPIRED);
@@ -250,9 +255,13 @@ class ThinPoolImpl extends PoolImpl {
250
255
  // destroy connection when pool close operation is called
251
256
  //---------------------------------------------------------------------------
252
257
  async _destroy(connection) {
253
- if (connection.nscon.ntAdapter.connected) {
254
- connection._dropSess = true;
255
- await connection.close();
258
+ try {
259
+ if (connection.nscon.ntAdapter.connected) {
260
+ connection._dropSess = true;
261
+ await connection.close();
262
+ }
263
+ } catch (err) {
264
+ return;
256
265
  }
257
266
  }
258
267
 
@@ -286,7 +295,12 @@ class ThinPoolImpl extends PoolImpl {
286
295
  await this._destroy(conn);
287
296
  }
288
297
 
289
- this.eventEmitter.removeAllListeners();
298
+ // Let the bgCleaner destroy all connsToDrop and stop
299
+ await new Promise((resolve) => {
300
+ this._poolCloseCleaner = resolve;
301
+ if (this._bgCleaner)
302
+ this._bgCleaner();
303
+ });
290
304
  }
291
305
 
292
306
  //---------------------------------------------------------------------------
@@ -352,6 +366,13 @@ class ThinPoolImpl extends PoolImpl {
352
366
  return this._poolTimeout;
353
367
  }
354
368
 
369
+ //---------------------------------------------------------------------------
370
+ // returns the pool max lifetime for a session (seconds)
371
+ //---------------------------------------------------------------------------
372
+ getMaxLifetimeSession() {
373
+ return this._maxLifetimeSession;
374
+ }
375
+
355
376
  //---------------------------------------------------------------------------
356
377
  // returns whether the SODA metadata cache is enabled or not
357
378
  //---------------------------------------------------------------------------
@@ -395,10 +416,13 @@ class ThinPoolImpl extends PoolImpl {
395
416
  break;
396
417
  }
397
418
 
398
- this.eventEmitter.emit('_removePoolConnection', conn);
419
+ this._connsToDrop.push(conn);
399
420
  this._freeConnectionList.shift();
400
421
  }
401
422
 
423
+ // call cleaner for destroying connections added to _connsToDrop
424
+ if (this._bgCleaner)
425
+ this._bgCleaner();
402
426
  this._schedulerJob = null;
403
427
  this._setScheduler();
404
428
  }
@@ -434,15 +458,6 @@ class ThinPoolImpl extends PoolImpl {
434
458
 
435
459
  // continue until a close request is received
436
460
  while (!this._poolCloseWaiter) {
437
- // eliminate connections that exceed the poolMax (this should only happen
438
- // after a reconfiguration has taken place where the maximum number of
439
- // connections is reduced)
440
- let numToDestroy = this._poolMax - this.getConnectionsOpen();
441
- while (numToDestroy > 0 && this._freeConnectionList.length > 0) {
442
- const connToBeRemoved = this._freeConnectionList.shift();
443
- this.eventEmitter.emit('_removePoolConnection', connToBeRemoved);
444
- numToDestroy--;
445
- }
446
461
 
447
462
  const numToCreate = this._getNumConnsToCreate();
448
463
  // connection creation is going on serially and not concurrently
@@ -455,6 +470,7 @@ class ThinPoolImpl extends PoolImpl {
455
470
  await conn.connect(config);
456
471
  conn._newSession = true;
457
472
  conn._dropSess = false;
473
+ conn._creationTime = Date.now();
458
474
  conn._lastTimeUsed = Date.now();
459
475
  this._freeConnectionList.push(conn);
460
476
  } catch (err) {
@@ -487,7 +503,8 @@ class ThinPoolImpl extends PoolImpl {
487
503
  }
488
504
 
489
505
  // if no pending requests, wait for pending requests to appear!
490
- if (this._pendingRequests.length == 0 || this._bgErr) {
506
+ if ((this._pendingRequests.length == 0 || this._bgErr) &&
507
+ this.getConnectionsOpen() >= this._poolMin) {
491
508
  await new Promise((resolve) => {
492
509
  this.bgWaiter = resolve;
493
510
  });
@@ -499,6 +516,27 @@ class ThinPoolImpl extends PoolImpl {
499
516
  this._poolCloseWaiter();
500
517
  }
501
518
 
519
+ //---------------------------------------------------------------------------
520
+ // _bgConnCleaner()
521
+ // Method which stays in the stack and performs cleanup whenever a connection
522
+ // gets added for cleanup to _connsToDrop
523
+ // acquire a connection from connection pool
524
+ //---------------------------------------------------------------------------
525
+ async _bgConnCleaner() {
526
+ // run until a pool.close() is issued
527
+ while (!this._poolCloseCleaner) {
528
+
529
+ // wait for _bgCleaner to be called for cleanup
530
+ if (this._connsToDrop.length == 0)
531
+ await new Promise(resolve => this._bgCleaner = resolve);
532
+
533
+ // destroy connections in _connsToDrop
534
+ while (this._connsToDrop.length)
535
+ await this._destroy(this._connsToDrop.pop());
536
+ }
537
+ this._poolCloseCleaner();
538
+ }
539
+
502
540
  //---------------------------------------------------------------------------
503
541
  // acquire()
504
542
  //
@@ -510,9 +548,10 @@ class ThinPoolImpl extends PoolImpl {
510
548
  while (this._freeConnectionList.length > 0) {
511
549
  const conn = this._freeConnectionList.pop();
512
550
 
513
- // if connection is unhealthy, drop it from the pool
514
- if (!conn.isHealthy()) {
515
- this.eventEmitter.emit('_removePoolConnection', conn);
551
+ // drop from the pool if the connection is unhealthy, or if the
552
+ // connection's lifetime exceeds maxLifetimeSession
553
+ if (!this._shouldRetainInPool(conn)) {
554
+ this._invokeBgCleaner(conn);
516
555
  continue;
517
556
  }
518
557
 
@@ -591,11 +630,11 @@ class ThinPoolImpl extends PoolImpl {
591
630
  if (conn.nscon.connected) {
592
631
  conn._lastTimeUsed = Date.now();
593
632
  conn._newSession = false;
594
- if ((this._freeConnectionList.length + this._usedConnectionList.size)
595
- < this._poolMax) {
633
+ if ((this.getConnectionsOpen() < this._poolMax) &&
634
+ this._shouldRetainInPool(conn)) {
596
635
  this._freeConnectionList.push(conn);
597
636
  } else {
598
- this.eventEmitter.emit('_removePoolConnection', conn);
637
+ this._invokeBgCleaner(conn);
599
638
  }
600
639
  }
601
640
 
@@ -648,9 +687,51 @@ class ThinPoolImpl extends PoolImpl {
648
687
  this._poolMin = params.poolMin;
649
688
  }
650
689
 
651
- if (this.bgWaiter) {
690
+ if (params.maxLifetimeSession !== undefined) {
691
+ this._maxLifetimeSession = params.maxLifetimeSession;
692
+ }
693
+
694
+ let numToDestroy = this.getConnectionsOpen() - this._poolMax;
695
+ while (this._freeConnectionList.length && numToDestroy > 0) {
696
+ const conn = this._freeConnectionList.pop();
697
+ this._connsToDrop.push(conn);
698
+ numToDestroy--;
699
+ }
700
+
701
+ // call cleanup for destroying connections in _connsToDrop
702
+ if (this._bgCleaner)
703
+ this._bgCleaner();
704
+ }
705
+
706
+ //---------------------------------------------------------------------------
707
+ // _invokeBgCleaner()
708
+ //
709
+ // drop conn and invoke the bgCleaner while also ensuring min connections
710
+ //---------------------------------------------------------------------------
711
+ _invokeBgCleaner(conn) {
712
+ this._connsToDrop.push(conn);
713
+ if (this._bgCleaner)
714
+ this._bgCleaner();
715
+
716
+ // ensure min connections are maintained in pool
717
+ if ((this.getConnectionsOpen() < this._poolMin) && this.bgWaiter)
652
718
  this.bgWaiter();
719
+ }
720
+
721
+ //---------------------------------------------------------------------------
722
+ // _shouldRetainInPool()
723
+ //
724
+ // returns true if the connection should be retained in pool
725
+ //---------------------------------------------------------------------------
726
+ _shouldRetainInPool(conn) {
727
+ if (!conn.isHealthy())
728
+ return false;
729
+ // check if maxLifetimeSession of conn has expired
730
+ if (this._maxLifetimeSession !== 0) {
731
+ if ((Date.now() - conn._creationTime) > (this._maxLifetimeSession * 1000))
732
+ return false;
653
733
  }
734
+ return true;
654
735
  }
655
736
  }
656
737
 
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -104,11 +104,14 @@ class Capabilities {
104
104
  constants.TNS_CCAP_LOB2_QUASI | constants.TNS_CCAP_LOB2_2GB_PREFETCH;
105
105
  this.compileCaps[constants.TNS_CCAP_TTC3] =
106
106
  constants.TNS_CCAP_IMPLICIT_RESULTS | constants.TNS_CCAP_BIG_CHUNK_CLR |
107
- constants.TNS_CCAP_KEEP_OUT_ORDER;
107
+ constants.TNS_CCAP_KEEP_OUT_ORDER | constants.TNS_CCAP_LTXID;
108
+ this.compileCaps[constants.TNS_CCAP_OCI3] = constants.TNS_CCAP_OCI3_OCSSYNC;
108
109
  this.compileCaps[constants.TNS_CCAP_TTC2] = constants.TNS_CCAP_ZLNP;
109
110
  this.compileCaps[constants.TNS_CCAP_OCI2] = constants.TNS_CCAP_DRCP;
110
111
  this.compileCaps[constants.TNS_CCAP_CLIENT_FN] =
111
112
  constants.TNS_CCAP_CLIENT_FN_MAX;
113
+ this.compileCaps[constants.TNS_CCAP_SESS_SIGNATURE_VERSION] =
114
+ constants.TNS_CCAP_FIELD_VERSION_12_2;
112
115
  this.compileCaps[constants.TNS_CCAP_TTC4] =
113
116
  constants.TNS_CCAP_INBAND_NOTIFICATION;
114
117
  if (nscon.endOfRequestSupport) {
@@ -117,9 +120,10 @@ class Capabilities {
117
120
  this.compileCaps[constants.TNS_CCAP_CTB_FEATURE_BACKPORT] =
118
121
  constants.TNS_CCAP_CTB_IMPLICIT_POOL;
119
122
  this.compileCaps[constants.TNS_CCAP_TTC5] =
120
- constants.TNS_CCAP_VECTOR_SUPPORT;
123
+ constants.TNS_CCAP_VECTOR_SUPPORT |
124
+ constants.TNS_CCAP_TTC5_SESSIONLESS_TXNS;
121
125
  this.compileCaps[constants.TNS_CCAP_VECTOR_FEATURES] =
122
- constants.TNS_CCAP_VECTOR_FEATURE_BINARY;
126
+ constants.TNS_CCAP_VECTOR_FEATURE_BINARY | constants.TNS_CCAP_VECTOR_FEATURE_SPARSE;
123
127
  }
124
128
 
125
129
  initRuntimeCaps() {