oracledb 6.9.0 → 7.0.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 (100) hide show
  1. package/LICENSE.txt +1 -1
  2. package/NOTICE.txt +1 -1
  3. package/README.md +9 -9
  4. package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
  5. package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-7.0.0-js-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
  8. package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
  10. package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
  12. package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +1 -0
  13. package/eslint.config.js +39 -0
  14. package/lib/aqDeqOptions.js +18 -1
  15. package/lib/aqQueue.js +10 -6
  16. package/lib/connection.js +418 -39
  17. package/lib/dbObject.js +25 -4
  18. package/lib/endUserSecurityContext.js +176 -0
  19. package/lib/errors.js +99 -8
  20. package/lib/impl/aqDeqOptions.js +10 -1
  21. package/lib/impl/connection.js +76 -2
  22. package/lib/impl/datahandlers/buffer.js +68 -5
  23. package/lib/impl/datahandlers/oson.js +7 -2
  24. package/lib/impl/datahandlers/vector.js +25 -5
  25. package/lib/impl/lob.js +9 -0
  26. package/lib/impl/parserHelpers.js +443 -0
  27. package/lib/impl/resultset.js +1 -1
  28. package/lib/impl/sodaDocument.js +5 -14
  29. package/lib/lob.js +27 -14
  30. package/lib/obfuscation.js +97 -0
  31. package/lib/oracledb.js +163 -49
  32. package/lib/parser.js +204 -0
  33. package/lib/pipeline.js +113 -0
  34. package/lib/pool.js +203 -147
  35. package/lib/poolStatistics.js +1 -1
  36. package/lib/resultset.js +42 -21
  37. package/lib/settings.js +1 -0
  38. package/lib/sodaCollection.js +12 -8
  39. package/lib/sodaDatabase.js +35 -8
  40. package/lib/sodaDocument.js +24 -6
  41. package/lib/sodaOperation.js +7 -6
  42. package/lib/thin/aq.js +475 -0
  43. package/lib/thin/connection.js +518 -54
  44. package/lib/thin/dbObject.js +7 -1
  45. package/lib/thin/lob.js +20 -1
  46. package/lib/thin/pool.js +42 -62
  47. package/lib/thin/protocol/capabilities.js +21 -4
  48. package/lib/thin/protocol/constants.js +143 -2
  49. package/lib/thin/protocol/encryptDecrypt.js +45 -10
  50. package/lib/thin/protocol/messages/aqArray.js +238 -0
  51. package/lib/thin/protocol/messages/aqBase.js +305 -0
  52. package/lib/thin/protocol/messages/aqDeq.js +164 -0
  53. package/lib/thin/protocol/messages/aqEnq.js +135 -0
  54. package/lib/thin/protocol/messages/auth.js +18 -28
  55. package/lib/thin/protocol/messages/base.js +245 -4
  56. package/lib/thin/protocol/messages/dataType.js +8 -3
  57. package/lib/thin/protocol/messages/directPathLoadStream.js +377 -0
  58. package/lib/thin/protocol/messages/directPathOp.js +65 -0
  59. package/lib/thin/protocol/messages/directPathPrepare.js +158 -0
  60. package/lib/thin/protocol/messages/endPipeline.js +56 -0
  61. package/lib/thin/protocol/messages/execute.js +9 -7
  62. package/lib/thin/protocol/messages/index.js +9 -1
  63. package/lib/thin/protocol/messages/protocol.js +2 -2
  64. package/lib/thin/protocol/messages/withData.js +10 -142
  65. package/lib/thin/protocol/packet.js +120 -28
  66. package/lib/thin/protocol/protocol.js +110 -25
  67. package/lib/thin/protocol/utils.js +1 -26
  68. package/lib/thin/resultSet.js +13 -19
  69. package/lib/thin/sqlnet/ezConnectResolver.js +2 -2
  70. package/lib/thin/sqlnet/networkSession.js +63 -41
  71. package/lib/thin/sqlnet/ntTcp.js +24 -0
  72. package/lib/thin/sqlnet/nvStrToNvPair.js +50 -25
  73. package/lib/thin/sqlnet/packet.js +25 -4
  74. package/lib/thin/sqlnet/paramParser.js +10 -7
  75. package/lib/thin/statementCache.js +2 -4
  76. package/lib/traceHandler.js +59 -2
  77. package/lib/transformer.js +8 -3
  78. package/lib/types.js +36 -22
  79. package/lib/util.js +6 -19
  80. package/lib/version.js +3 -3
  81. package/package/install.js +5 -5
  82. package/package/prunebinaries.js +1 -1
  83. package/package.json +1 -1
  84. package/plugins/configProviders/awsCommon.js +125 -0
  85. package/plugins/configProviders/awss3/index.js +138 -0
  86. package/plugins/configProviders/awssecretsmanager/index.js +147 -0
  87. package/plugins/configProviders/azure/index.js +7 -5
  88. package/plugins/token/extensionAzure/index.js +0 -1
  89. package/plugins/token/extensionOci/index.js +0 -1
  90. package/build/Release/oracledb-6.9.0-darwin-arm64.node +0 -0
  91. package/build/Release/oracledb-6.9.0-darwin-arm64.node-buildinfo.txt +0 -1
  92. package/build/Release/oracledb-6.9.0-darwin-x64.node +0 -0
  93. package/build/Release/oracledb-6.9.0-darwin-x64.node-buildinfo.txt +0 -1
  94. package/build/Release/oracledb-6.9.0-js-buildinfo.txt +0 -1
  95. package/build/Release/oracledb-6.9.0-linux-arm64.node +0 -0
  96. package/build/Release/oracledb-6.9.0-linux-arm64.node-buildinfo.txt +0 -1
  97. package/build/Release/oracledb-6.9.0-linux-x64.node +0 -0
  98. package/build/Release/oracledb-6.9.0-linux-x64.node-buildinfo.txt +0 -1
  99. package/build/Release/oracledb-6.9.0-win32-x64.node +0 -0
  100. package/build/Release/oracledb-6.9.0-win32-x64.node-buildinfo.txt +0 -1
package/lib/pool.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2016, 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2016, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -34,7 +34,7 @@ const settings = require('./settings.js');
34
34
  const nodbUtil = require('./util.js');
35
35
  const impl = require('./impl');
36
36
  const PoolStatistics = require('./poolStatistics.js');
37
-
37
+ const traceHandler = require('./traceHandler.js');
38
38
 
39
39
  class Pool extends EventEmitter {
40
40
 
@@ -46,8 +46,12 @@ class Pool extends EventEmitter {
46
46
  this._enableStatistics = false;
47
47
  this._timeOfReset = this._createdDate = Date.now();
48
48
  this._sessionCallback = undefined;
49
- this._connRequestQueue = [];
49
+ this._pendingRequestQueue = new Set();
50
50
  this._connectionClass = settings.connectionClass;
51
+ this._bgRequestWaiter = null;
52
+ this._bgRequestId = 0;
53
+ this._processing = 0;
54
+ this._ensureNoPendingRequests = null;
51
55
  }
52
56
 
53
57
  //---------------------------------------------------------------------------
@@ -68,36 +72,6 @@ class Pool extends EventEmitter {
68
72
  }
69
73
  }
70
74
 
71
- //---------------------------------------------------------------------------
72
- // _checkRequestQueue()
73
- //
74
- // When a connection is returned to the pool, this method is called (via an
75
- // event handler) to determine when requests for connections should be
76
- // resumed and cancels any timeout that may have been associated with the
77
- // request. This method is also called from reconfigure() so that waiting
78
- // connection requests can be processed. Note the use of a local variable for
79
- // the number of connections out. This is because the connection requests will
80
- // not resume until after the loop is finished, and therefore the number of
81
- // connections the pool thinks is out will not be incremented.
82
- //---------------------------------------------------------------------------
83
- _checkRequestQueue() {
84
- let connectionsOut = this._connectionsOut;
85
- while (this._connRequestQueue.length > 0 && connectionsOut < this.poolMax) {
86
- connectionsOut += 1;
87
- const payload = this._connRequestQueue.shift();
88
- if (this._enableStatistics) {
89
- this._totalRequestsDequeued += 1;
90
- this._updateWaitStatistics(payload);
91
- }
92
- if (payload.timeoutHandle) {
93
- clearTimeout(payload.timeoutHandle);
94
- }
95
- // inform the waiter that processing can continue
96
- payload.resolve();
97
- }
98
- }
99
-
100
- //---------------------------------------------------------------------------
101
75
  // _enableStats (DEPRECATED)
102
76
  //
103
77
  // Property for whether statistics are enabled on the pool.
@@ -118,7 +92,7 @@ class Pool extends EventEmitter {
118
92
  this._totalFailedRequests = 0;
119
93
  this._totalRequestsRejected = 0;
120
94
  this._totalRequestTimeouts = 0;
121
- this._maximumQueueLength = this._connRequestQueue.length;
95
+ this._maximumQueueLength = this._pendingRequestQueue.size;
122
96
  this._totalTimeInQueue = 0;
123
97
  this._minTimeInQueue = 0;
124
98
  this._maxTimeInQueue = 0;
@@ -143,9 +117,7 @@ class Pool extends EventEmitter {
143
117
  this._status = constants.POOL_STATUS_OPEN;
144
118
  this._connectionsOut = 0;
145
119
  this._poolAlias = poolAlias;
146
-
147
- // register event handler for when request queue should be checked
148
- this.on('_checkRequestQueue', this._checkRequestQueue);
120
+ this._bgRequestHandler();
149
121
 
150
122
  this._resetStatistics();
151
123
 
@@ -157,8 +129,8 @@ class Pool extends EventEmitter {
157
129
  // Update pool wait statistics after a connect request has spent some time in
158
130
  // the queue.
159
131
  //---------------------------------------------------------------------------
160
- _updateWaitStatistics(payload) {
161
- const waitTime = Date.now() - payload.enqueuedTime;
132
+ _updateWaitStatistics(request) {
133
+ const waitTime = Date.now() - request.enqueuedTime;
162
134
  this._totalTimeInQueue += waitTime;
163
135
  if (this._minTimeInQueue === 0) {
164
136
  this._minTimeInQueue = waitTime;
@@ -280,10 +252,12 @@ class Pool extends EventEmitter {
280
252
  // if the pool is draining/reconfiguring/closed, throw an appropriate error
281
253
  this._checkPoolOpen(false);
282
254
 
255
+ if (forceClose)
256
+ this._status = constants.POOL_STATUS_DRAINING;
257
+
283
258
  // wait for the pool to become empty or for the drain timeout to expire
284
259
  // (whichever comes first)
285
260
  if (drainTime > 0) {
286
- this._status = constants.POOL_STATUS_DRAINING;
287
261
  await new Promise(resolve => {
288
262
  const timeout = setTimeout(() => {
289
263
  this.removeAllListeners('_allCheckedIn');
@@ -302,10 +276,21 @@ class Pool extends EventEmitter {
302
276
  errors.throwErr(errors.ERR_POOL_HAS_BUSY_CONNECTIONS);
303
277
  }
304
278
 
279
+ // ensure no connection requests are being processed before proceeding to
280
+ // close the pool (required for thick mode)
281
+ if (this._processing != 0)
282
+ await new Promise(resolve => this._ensureNoPendingRequests = resolve);
283
+
305
284
  // close the pool
306
285
  await this._impl.close();
307
286
  this._status = constants.POOL_STATUS_CLOSED;
287
+
288
+ // stop the _bgRequestWaiter waiting in background
289
+ if (this._bgRequestWaiter)
290
+ this._bgRequestWaiter();
291
+
308
292
  this.emit('_afterPoolClose');
293
+ traceHandler.getTraceInstance()?.onPoolClose(this);
309
294
 
310
295
  }
311
296
 
@@ -391,7 +376,6 @@ class Pool extends EventEmitter {
391
376
  // queueTimeout milliseconds.
392
377
  //---------------------------------------------------------------------------
393
378
  async getConnection(a1) {
394
- let poolMax;
395
379
  let options = {};
396
380
 
397
381
  // check arguments
@@ -412,131 +396,195 @@ class Pool extends EventEmitter {
412
396
  this._totalConnectionRequests += 1;
413
397
  }
414
398
 
415
- // getting the poolMax setting on the pool may fail if the pool is no
416
- // longer valid
417
- try {
418
- poolMax = this.poolMax;
419
- } catch (err) {
420
- if (this._enableStatistics) {
421
- this._totalFailedRequests += 1;
422
- }
423
- throw err;
424
- }
425
-
426
- while (this._connectionsOut >= poolMax ||
427
- this.status === constants.POOL_STATUS_RECONFIGURING) {
428
-
429
- // when the queue is huge, throw error early without waiting for queue
430
- // timeout
431
- if (this._connRequestQueue.length >= this._queueMax &&
432
- this._queueMax >= 0) {
433
- if (this._enableStatistics) {
434
- this._totalRequestsRejected += 1;
399
+ if (this._bgRequestId == Number.MAX_SAFE_INTEGER)
400
+ this._bgRequestId = 0;
401
+
402
+ const request = {
403
+ id: this._bgRequestId++,
404
+ options: options,
405
+ conn: null,
406
+ isWaiting: true,
407
+ error: null,
408
+ timeoutHandle: null,
409
+ enqueuedTime: Date.now(),
410
+ wasQueued: false
411
+ };
412
+
413
+ // This promise acquires a connection from pool
414
+ const promiseToAcquire = new Promise((resolve, reject) => {
415
+ request.resolve = resolve;
416
+ request.reject = reject;
417
+
418
+ // If we haven't reached poolMax we shouldn't add to queue(honor queueMax)
419
+ if (this._connectionsOut < this.poolMax) {
420
+ this._processRequest(request);
421
+ } else {
422
+ // Ensure queueMax is not exceeded
423
+ if (this._pendingRequestQueue.size == this._queueMax) {
424
+ if (this._enableStatistics)
425
+ this._totalRequestsRejected += 1;
426
+ return reject(errors.getErr(errors.ERR_QUEUE_MAX_EXCEEDED,
427
+ this._queueMax));
435
428
  }
436
- errors.throwErr(errors.ERR_QUEUE_MAX_EXCEEDED, this._queueMax);
437
- }
438
429
 
439
- // if too many connections are out, wait until room is made available or
440
- // the queue timeout expires
441
- await new Promise((resolve, reject) => {
442
-
443
- // set up a payload which will be added to the queue for processing
444
- const payload = { resolve: resolve, reject: reject };
445
-
446
- // if using a queue timeout, establish the timeout so that when it
447
- // expires the payload will be removed from the queue and an exception
448
- // thrown
449
- if (this._queueTimeout !== 0) {
450
- payload.timeoutHandle = setTimeout(() => {
451
- const ix = this._connRequestQueue.indexOf(payload);
452
- if (ix >= 0) {
453
- this._connRequestQueue.splice(ix, 1);
454
- }
455
- if (this._enableStatistics) {
456
- this._totalRequestTimeouts += 1;
457
- this._updateWaitStatistics(payload);
458
- }
459
- try {
460
- errors.throwErr(errors.ERR_CONN_REQUEST_TIMEOUT,
461
- this._queueTimeout);
462
- } catch (err) {
463
- reject(err);
464
- }
465
- }, this._queueTimeout);
466
- }
430
+ // Queueing our request
431
+ request.wasQueued = true;
432
+ this._pendingRequestQueue.add(request);
433
+ traceHandler.getTraceInstance()?.onPoolWait(this);
467
434
 
468
- // add payload to the queue
469
- this._connRequestQueue.push(payload);
470
435
  if (this._enableStatistics) {
471
- payload.enqueuedTime = Date.now();
472
436
  this._totalRequestsEnqueued += 1;
473
437
  this._maximumQueueLength = Math.max(this._maximumQueueLength,
474
- this._connRequestQueue.length);
438
+ this._pendingRequestQueue.size);
475
439
  }
440
+ }
441
+ });
442
+
443
+ // This promise will timeout after queueTimeout seconds
444
+ const promiseToTimeout = new Promise ((_, reject) => {
445
+ if (this._queueTimeout > 0)
446
+ request.timeoutHandle = setTimeout(() => {
447
+ request.isWaiting = false;
448
+ this._pendingRequestQueue.delete(request);
449
+ if (this.enableStatistics)
450
+ this._totalRequestTimeouts += 1;
451
+ traceHandler.getTraceInstance()?.onPoolRequestTimeout(this);
452
+ reject(errors.getErr(errors.ERR_CONN_REQUEST_TIMEOUT,
453
+ this._queueTimeout));
454
+ }, this._queueTimeout);
455
+ });
456
+
457
+ // Earliest promise to resolve/reject gets fulfilled
458
+ try {
459
+ await Promise.race([promiseToAcquire, promiseToTimeout]);
460
+ } catch (err) {
461
+ request.error = err;
462
+ }
476
463
 
477
- });
464
+ clearTimeout(request.timeoutHandle);
465
+ // Update time waited by request
466
+ if (this._enableStatistics && request.wasQueued)
467
+ this._updateWaitStatistics(request);
478
468
 
479
- // check if pool is draining/closed after delay has
480
- // completed and throw an appropriate error
481
- this._checkPoolOpen(true);
469
+ // Request failed to be fulfilled (errored/timeout)
470
+ if (request.error)
471
+ throw request.error;
482
472
 
483
- }
473
+ traceHandler.getTraceInstance()?.onPoolAcquire(this);
484
474
 
485
- // room is available in the queue, so proceed to acquire a connection from
486
- // the pool; adjust the connections out immediately in order to ensure that
487
- // another attempt doesn't proceed while this one is underway
488
- this._connectionsOut += 1;
489
- try {
475
+ return request.conn;
476
+ }
490
477
 
491
- // acquire connection from the pool
492
- const conn = new Connection();
493
- conn._impl = await this._impl.getConnection(options);
494
- conn._pool = this;
478
+ //---------------------------------------------------------------------------
479
+ // _bgRequestHandler()
480
+ //
481
+ // This method handles the queuing of all pending requests and processes them
482
+ // depending on the open connections and if the request is still waiting.
483
+ //---------------------------------------------------------------------------
484
+ async _bgRequestHandler() {
485
+ while (this.status != constants.POOL_STATUS_DRAINING ||
486
+ this.status != constants.POOL_STATUS_CLOSED) {
495
487
 
496
- // invoke tag fixup callback method if one has been specified and the
497
- // actual tag on the connection doesn't match the one requested, or the
498
- // connection is freshly created; if the callback fails, close the
499
- // connection and remove it from the pool
500
- const requestedTag = options.tag || "";
501
- if (typeof this.sessionCallback === 'function' &&
502
- (conn._impl._newSession || conn.tag != requestedTag)) {
503
- try {
504
- await new Promise((resolve, reject) => {
505
- this.sessionCallback(conn, requestedTag, function(err) {
506
- if (err) {
507
- reject(err);
508
- } else {
509
- resolve();
510
- }
511
- });
512
- });
513
- } catch (err) {
514
- await conn.close({ drop: true });
515
- throw err;
516
- }
488
+ const request = this._pendingRequestQueue.values().next().value;
489
+ if (request && (this._connectionsOut < this.poolMax)) {
490
+ this._processRequest(request);
491
+ } else {
492
+ await new Promise((resolve) => {
493
+ this._bgRequestWaiter = resolve;
494
+ });
495
+ this._bgRequestWaiter = null;
517
496
  }
497
+ }
518
498
 
519
- // when connection is closed, check to see if another request should be
520
- // processed and update any stats, as needed
521
- conn.on('_afterConnClose', () => {
522
- this._connectionsOut -= 1;
523
- this.emit('_checkRequestQueue');
524
- if (this._connectionsOut == 0) {
525
- this.emit('_allCheckedIn');
526
- }
527
- });
528
-
529
- return (conn);
499
+ // reject _pendingRequests waiting to be processed
500
+ for (const request of this._pendingRequestQueue)
501
+ request.reject(errors.throwErr(errors.ERR_POOL_CLOSED));
502
+ }
530
503
 
504
+ //---------------------------------------------------------------------------
505
+ // _processRequest()
506
+ //
507
+ // Method called to process a connection request and return the required
508
+ // 'connection'/'error returned by DB' if the client is still waiting.
509
+ //---------------------------------------------------------------------------
510
+ async _processRequest(request) {
511
+ const conn = new Connection();
512
+ try {
513
+ this._processing += 1;
514
+ this._connectionsOut += 1;
515
+ this._pendingRequestQueue.delete(request);
516
+ conn._impl = await this._impl.getConnection(request.options);
517
+ conn._pool = this;
518
+ await this._tagFixup(conn, request.options.tag);
519
+ if (request.isWaiting)
520
+ request.conn = conn;
521
+ else
522
+ await conn.close({});
523
+ request.resolve();
531
524
  } catch (err) {
532
- this._connectionsOut -= 1;
533
- if (this._enableStatistics) {
534
- this._totalFailedRequests += 1;
525
+ if (request.isWaiting) {
526
+ if (this._enableStatistics)
527
+ this._totalFailedRequests += 1;
528
+ request.reject(err);
529
+ }
530
+ // Request errored, so we have some space in pool
531
+ this._release(conn._impl, request.options);
532
+ } finally {
533
+ this._processing -= 1;
534
+ if (this.status == constants.POOL_STATUS_DRAINING &&
535
+ this._processing == 0 && this._ensureNoPendingRequests)
536
+ this._ensureNoPendingRequests();
537
+ }
538
+ }
539
+
540
+ //---------------------------------------------------------------------------
541
+ // _tagFixup()
542
+ //
543
+ // Method to check if requested tag matches the connection's tag.
544
+ // If it does not match the callback function is invoked.
545
+ //---------------------------------------------------------------------------
546
+ async _tagFixup(conn, tag) {
547
+ const requestedTag = tag || "";
548
+ if (typeof this.sessionCallback === 'function' &&
549
+ (conn._impl._newSession ||
550
+ conn.tag != requestedTag)) {
551
+ try {
552
+ await new Promise((resolve, reject) => {
553
+ this.sessionCallback(conn, requestedTag, function(err) {
554
+ if (err) {
555
+ reject(err);
556
+ } else {
557
+ resolve();
558
+ }
559
+ });
560
+ });
561
+ } catch (err) {
562
+ await conn.close({ drop: true });
563
+ throw err;
535
564
  }
536
- this.emit('_checkRequestQueue');
537
- throw err;
538
565
  }
566
+ }
539
567
 
568
+ //---------------------------------------------------------------------------
569
+ // _release()
570
+ //
571
+ // Method called by 'close() in connection.js after a release'/'error in
572
+ // _processRequest' to decrease connectionsOut & wake up _bgRequestWaiter
573
+ //---------------------------------------------------------------------------
574
+ async _release(impl, options) {
575
+ this._connectionsOut -= 1;
576
+ try {
577
+ // Directly close the connection using the underlying connection impl
578
+ if (impl)
579
+ await impl.close(options);
580
+ } finally {
581
+ if (this._connectionsOut == 0)
582
+ this.emit('_allCheckedIn');
583
+ setImmediate(() => {
584
+ if (this._pendingRequestQueue.size > 0 && this._bgRequestWaiter)
585
+ this._bgRequestWaiter();
586
+ });
587
+ }
540
588
  }
541
589
 
542
590
  //---------------------------------------------------------------------------
@@ -747,7 +795,6 @@ class Pool extends EventEmitter {
747
795
  } finally {
748
796
  this._status = constants.POOL_STATUS_OPEN;
749
797
  }
750
- this.emit('_checkRequestQueue');
751
798
  }
752
799
 
753
800
  //---------------------------------------------------------------------------
@@ -817,6 +864,15 @@ class Pool extends EventEmitter {
817
864
  get connectTraceConfig() {
818
865
  return this._impl && this._impl._getConnectTraceConfig();
819
866
  }
867
+
868
+ //---------------------------------------------------------------------------
869
+ // Symbol.asyncDispose
870
+ //
871
+ // Implement explicit resource management.
872
+ //---------------------------------------------------------------------------
873
+ async [Symbol.asyncDispose]() {
874
+ await this.close();
875
+ }
820
876
  }
821
877
 
822
878
  nodbUtil.wrapFns(Pool.prototype, false,
@@ -54,7 +54,7 @@ class PoolStatistics {
54
54
  this.rejectedRequests = pool._totalRequestsRejected;
55
55
  this.requestTimeouts = pool._totalRequestTimeouts;
56
56
  this.maximumQueueLength = pool._maximumQueueLength;
57
- this.currentQueueLength = pool._connRequestQueue.length;
57
+ this.currentQueueLength = pool._pendingRequestQueue.size;
58
58
  this.timeInQueue = pool._totalTimeInQueue;
59
59
  this.minimumTimeInQueue = pool._minTimeInQueue;
60
60
  this.maximumTimeInQueue = pool._maxTimeInQueue;
package/lib/resultset.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2016, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2016, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -59,7 +59,7 @@ class ResultSet {
59
59
 
60
60
  // fetch all rows
61
61
  let rowsFetched = [];
62
- while (true) { // eslint-disable-line
62
+ while (true) {
63
63
  if (maxRows > 0 && fetchArraySize >= maxRows) {
64
64
  fetchArraySize = maxRows;
65
65
  }
@@ -206,12 +206,7 @@ class ResultSet {
206
206
  this._impl = resultSetImpl;
207
207
  }
208
208
 
209
- //---------------------------------------------------------------------------
210
- // close()
211
- //
212
- // Close the result set and make it unusable for further operations.
213
- //---------------------------------------------------------------------------
214
- async close() {
209
+ async _close() {
215
210
  errors.assertArgCount(arguments, 0, 0);
216
211
  errors.assert(this._impl && this._connection._impl, errors.ERR_INVALID_RS);
217
212
 
@@ -226,13 +221,15 @@ class ResultSet {
226
221
  }
227
222
 
228
223
  //---------------------------------------------------------------------------
229
- // getRow()
224
+ // close()
230
225
  //
231
- // Returns a single row to the caller from the result set, if one is
232
- // available. Rows are buffered in a JavaScript array in order to avoid trips
233
- // through the thread pool that would be required if implemented in C.
226
+ // Close the result set and make it unusable for further operations.
234
227
  //---------------------------------------------------------------------------
235
- async getRow() {
228
+ async close() {
229
+ await this._close.apply(this, arguments);
230
+ }
231
+
232
+ async _getRow() {
236
233
  errors.assertArgCount(arguments, 0, 0);
237
234
  errors.assert(this._impl && this._connection._impl, errors.ERR_INVALID_RS);
238
235
 
@@ -252,15 +249,17 @@ class ResultSet {
252
249
  }
253
250
 
254
251
  //---------------------------------------------------------------------------
255
- // getRows()
252
+ // getRow()
256
253
  //
257
- // Check to see if any rows are in the JS buffer (which could result from
258
- // interspersed calls to getRow() and getRows()). If no rows are in the
259
- // buffer, the call is just proxied to the implementation layer. Otherwise,
260
- // rows are pulled from the buffer and potentially concatenated with rows
261
- // from calls to the implementation's getRows().
254
+ // Returns a single row to the caller from the result set, if one is
255
+ // available. Rows are buffered in a JavaScript array in order to avoid trips
256
+ // through the thread pool that would be required if implemented in C.
262
257
  //---------------------------------------------------------------------------
263
- async getRows(numRows) {
258
+ async getRow() {
259
+ return await this._getRow.apply(this, arguments);
260
+ }
261
+
262
+ async _doGetRows(numRows) {
264
263
  let rowsNeeded;
265
264
 
266
265
  errors.assertArgCount(arguments, 0, 1);
@@ -282,7 +281,7 @@ class ResultSet {
282
281
  if (numRows == 0) {
283
282
  requestedRows = this._rowCache;
284
283
  const fetchArraySize = this._impl.fetchArraySize;
285
- while (true) { // eslint-disable-line
284
+ while (true) {
286
285
  const rows = await this._getRows(fetchArraySize);
287
286
  if (rows) {
288
287
  await this._processRows(rows, false);
@@ -312,6 +311,19 @@ class ResultSet {
312
311
  return requestedRows;
313
312
  }
314
313
 
314
+ //---------------------------------------------------------------------------
315
+ // getRows()
316
+ //
317
+ // Check to see if any rows are in the JS buffer (which could result from
318
+ // interspersed calls to getRow() and getRows()). If no rows are in the
319
+ // buffer, the call is just proxied to the implementation layer. Otherwise,
320
+ // rows are pulled from the buffer and potentially concatenated with rows
321
+ // from calls to the implementation's getRows().
322
+ //---------------------------------------------------------------------------
323
+ async getRows() {
324
+ return await this._doGetRows.apply(this, arguments);
325
+ }
326
+
315
327
  //---------------------------------------------------------------------------
316
328
  // metaData()
317
329
  //
@@ -358,6 +370,15 @@ class ResultSet {
358
370
  };
359
371
  }
360
372
 
373
+ //---------------------------------------------------------------------------
374
+ // Symbol.asyncDispose
375
+ //
376
+ // Implement explicit resource management.
377
+ //---------------------------------------------------------------------------
378
+ async [Symbol.asyncDispose]() {
379
+ await this.close();
380
+ }
381
+
361
382
  }
362
383
 
363
384
  nodbUtil.wrapFns(ResultSet.prototype, errors.ERR_BUSY_RS,
package/lib/settings.js CHANGED
@@ -62,6 +62,7 @@ class Settings {
62
62
  this.configProviderCacheTimeout = 86400;
63
63
  this.thin = true;
64
64
  this.thinDriverInitialized = false;
65
+ this.thickModeDSNPassthrough = true;
65
66
  this.createFetchTypeMap(this.fetchAsString, this.fetchAsBuffer);
66
67
  this.fetchTypeHandler = undefined;
67
68
  this.dbObjectTypeHandler = undefined;
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2018, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2018, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -34,6 +34,10 @@ const settings = require('./settings.js');
34
34
 
35
35
  class SodaCollection {
36
36
 
37
+ constructor(sodaDb) {
38
+ this._sodaDb = sodaDb;
39
+ }
40
+
37
41
  //---------------------------------------------------------------------------
38
42
  // createIndex()
39
43
  //
@@ -84,7 +88,7 @@ class SodaCollection {
84
88
  //---------------------------------------------------------------------------
85
89
  find() {
86
90
  errors.assertArgCount(arguments, 0, 0);
87
- const op = new SodaOperation();
91
+ const op = new SodaOperation(this._sodaDb);
88
92
  op._impl = this._impl.find();
89
93
  return op;
90
94
  }
@@ -111,7 +115,7 @@ class SodaCollection {
111
115
 
112
116
  const actualDocs = Array(docs.length);
113
117
  for (let i = 0; i < docs.length; i++) {
114
- actualDocs[i] = nodbUtil.verifySodaDoc(docs[i]);
118
+ actualDocs[i] = this._sodaDb._verifySodaDoc(docs[i]);
115
119
  }
116
120
 
117
121
  const options = {autoCommit: settings.autoCommit};
@@ -140,7 +144,7 @@ class SodaCollection {
140
144
 
141
145
  const actualDocs = Array(docs.length);
142
146
  for (let i = 0; i < docs.length; i++) {
143
- actualDocs[i] = nodbUtil.verifySodaDoc(docs[i]);
147
+ actualDocs[i] = this._sodaDb._verifySodaDoc(docs[i]);
144
148
  }
145
149
 
146
150
  const docImpls = await this._impl.insertManyAndGet(actualDocs, options);
@@ -159,7 +163,7 @@ class SodaCollection {
159
163
  //---------------------------------------------------------------------------
160
164
  async insertOne(content) {
161
165
  errors.assertArgCount(arguments, 1, 1);
162
- content = nodbUtil.verifySodaDoc(content);
166
+ content = this._sodaDb._verifySodaDoc(content);
163
167
  const options = {autoCommit: settings.autoCommit};
164
168
  await this._impl.insertOne(content, options);
165
169
  }
@@ -174,7 +178,7 @@ class SodaCollection {
174
178
  const options = {autoCommit: settings.autoCommit};
175
179
 
176
180
  errors.assertArgCount(arguments, 1, 2);
177
- content = nodbUtil.verifySodaDoc(content);
181
+ content = this._sodaDb._verifySodaDoc(content);
178
182
  if (arguments.length == 2) {
179
183
  errors.assertParamValue(nodbUtil.isObject(a2), 2);
180
184
  if (a2.hint !== undefined) {
@@ -223,7 +227,7 @@ class SodaCollection {
223
227
  //---------------------------------------------------------------------------
224
228
  async save(content) {
225
229
  errors.assertArgCount(arguments, 1, 1);
226
- content = nodbUtil.verifySodaDoc(content);
230
+ content = this._sodaDb._verifySodaDoc(content);
227
231
  const options = {autoCommit: settings.autoCommit};
228
232
  await this._impl.save(content, options);
229
233
  }
@@ -236,7 +240,7 @@ class SodaCollection {
236
240
  //---------------------------------------------------------------------------
237
241
  async saveAndGet(content, a2) {
238
242
  errors.assertArgCount(arguments, 1, 2);
239
- content = nodbUtil.verifySodaDoc(content);
243
+ content = this._sodaDb._verifySodaDoc(content);
240
244
  const options = {autoCommit: settings.autoCommit};
241
245
  if (arguments.length == 2) {
242
246
  errors.assertParamValue(nodbUtil.isObject(a2), 2);