oracledb 6.8.0 → 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.
- package/README.md +39 -3
- package/build/Release/oracledb-6.9.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-6.9.0-darwin-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.9.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.9.0-darwin-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.9.0-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.9.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.9.0-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.9.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.9.0-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.9.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.9.0-win32-x64.node-buildinfo.txt +1 -0
- package/lib/aqMessage.js +10 -1
- package/lib/configProviders/file.js +72 -0
- package/lib/connection.js +87 -2
- package/lib/dbObject.js +9 -0
- package/lib/errors.js +56 -31
- package/lib/impl/connection.js +9 -0
- package/lib/impl/pool.js +10 -1
- package/lib/impl/resultset.js +5 -4
- package/lib/lob.js +9 -1
- package/lib/oracledb.js +129 -16
- package/lib/pool.js +12 -1
- package/lib/poolStatistics.js +3 -1
- package/lib/thin/connection.js +117 -10
- package/lib/thin/dbObject.js +8 -6
- package/lib/thin/lob.js +2 -3
- package/lib/thin/pool.js +105 -28
- package/lib/thin/protocol/capabilities.js +7 -3
- package/lib/thin/protocol/constants.js +29 -1
- package/lib/thin/protocol/messages/auth.js +13 -0
- package/lib/thin/protocol/messages/base.js +98 -9
- package/lib/thin/protocol/messages/dataType.js +2 -1
- package/lib/thin/protocol/messages/execute.js +46 -4
- package/lib/thin/protocol/messages/lobOp.js +5 -22
- package/lib/thin/protocol/messages/logOff.js +0 -1
- package/lib/thin/protocol/messages/sessionRelease.js +0 -1
- package/lib/thin/protocol/messages/transactionSwitch.js +12 -4
- package/lib/thin/protocol/protocol.js +2 -1
- package/lib/thin/sqlnet/connStrategy.js +2 -1
- package/lib/thin/sqlnet/navNodes.js +1 -1
- package/lib/thin/sqlnet/networkSession.js +9 -3
- package/lib/thin/sqlnet/packet.js +2 -1
- package/lib/thin/sqlnet/sessionAtts.js +2 -3
- package/lib/types.js +0 -5
- package/lib/util.js +76 -2
- package/lib/version.js +1 -1
- package/package/install.js +3 -3
- package/package/prunebinaries.js +6 -12
- package/package.json +2 -2
- package/{lib/configProviders/azure.js → plugins/configProviders/azure/index.js} +70 -63
- package/plugins/configProviders/azurevault/index.js +84 -0
- package/{lib → plugins}/configProviders/base.js +13 -3
- package/{lib/configProviders/ociobject.js → plugins/configProviders/ociobject/index.js} +43 -78
- package/plugins/configProviders/ocivault/index.js +100 -0
- package/plugins/token/extensionOci/index.js +71 -35
- package/build/Release/oracledb-6.8.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-6.8.0-darwin-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.8.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.8.0-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.8.0-js-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.8.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.8.0-linux-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.8.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.8.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.8.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.8.0-win32-x64.node-buildinfo.txt +0 -1
package/lib/thin/pool.js
CHANGED
|
@@ -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
|
//---------------------------------------------------------------------------
|
|
@@ -254,9 +255,13 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
254
255
|
// destroy connection when pool close operation is called
|
|
255
256
|
//---------------------------------------------------------------------------
|
|
256
257
|
async _destroy(connection) {
|
|
257
|
-
|
|
258
|
-
connection.
|
|
259
|
-
|
|
258
|
+
try {
|
|
259
|
+
if (connection.nscon.ntAdapter.connected) {
|
|
260
|
+
connection._dropSess = true;
|
|
261
|
+
await connection.close();
|
|
262
|
+
}
|
|
263
|
+
} catch (err) {
|
|
264
|
+
return;
|
|
260
265
|
}
|
|
261
266
|
}
|
|
262
267
|
|
|
@@ -290,7 +295,12 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
290
295
|
await this._destroy(conn);
|
|
291
296
|
}
|
|
292
297
|
|
|
293
|
-
|
|
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
|
+
});
|
|
294
304
|
}
|
|
295
305
|
|
|
296
306
|
//---------------------------------------------------------------------------
|
|
@@ -356,6 +366,13 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
356
366
|
return this._poolTimeout;
|
|
357
367
|
}
|
|
358
368
|
|
|
369
|
+
//---------------------------------------------------------------------------
|
|
370
|
+
// returns the pool max lifetime for a session (seconds)
|
|
371
|
+
//---------------------------------------------------------------------------
|
|
372
|
+
getMaxLifetimeSession() {
|
|
373
|
+
return this._maxLifetimeSession;
|
|
374
|
+
}
|
|
375
|
+
|
|
359
376
|
//---------------------------------------------------------------------------
|
|
360
377
|
// returns whether the SODA metadata cache is enabled or not
|
|
361
378
|
//---------------------------------------------------------------------------
|
|
@@ -399,10 +416,13 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
399
416
|
break;
|
|
400
417
|
}
|
|
401
418
|
|
|
402
|
-
this.
|
|
419
|
+
this._connsToDrop.push(conn);
|
|
403
420
|
this._freeConnectionList.shift();
|
|
404
421
|
}
|
|
405
422
|
|
|
423
|
+
// call cleaner for destroying connections added to _connsToDrop
|
|
424
|
+
if (this._bgCleaner)
|
|
425
|
+
this._bgCleaner();
|
|
406
426
|
this._schedulerJob = null;
|
|
407
427
|
this._setScheduler();
|
|
408
428
|
}
|
|
@@ -438,15 +458,6 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
438
458
|
|
|
439
459
|
// continue until a close request is received
|
|
440
460
|
while (!this._poolCloseWaiter) {
|
|
441
|
-
// eliminate connections that exceed the poolMax (this should only happen
|
|
442
|
-
// after a reconfiguration has taken place where the maximum number of
|
|
443
|
-
// connections is reduced)
|
|
444
|
-
let numToDestroy = this._poolMax - this.getConnectionsOpen();
|
|
445
|
-
while (numToDestroy > 0 && this._freeConnectionList.length > 0) {
|
|
446
|
-
const connToBeRemoved = this._freeConnectionList.shift();
|
|
447
|
-
this.eventEmitter.emit('_removePoolConnection', connToBeRemoved);
|
|
448
|
-
numToDestroy--;
|
|
449
|
-
}
|
|
450
461
|
|
|
451
462
|
const numToCreate = this._getNumConnsToCreate();
|
|
452
463
|
// connection creation is going on serially and not concurrently
|
|
@@ -459,6 +470,7 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
459
470
|
await conn.connect(config);
|
|
460
471
|
conn._newSession = true;
|
|
461
472
|
conn._dropSess = false;
|
|
473
|
+
conn._creationTime = Date.now();
|
|
462
474
|
conn._lastTimeUsed = Date.now();
|
|
463
475
|
this._freeConnectionList.push(conn);
|
|
464
476
|
} catch (err) {
|
|
@@ -491,7 +503,8 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
491
503
|
}
|
|
492
504
|
|
|
493
505
|
// if no pending requests, wait for pending requests to appear!
|
|
494
|
-
if (this._pendingRequests.length == 0 || this._bgErr)
|
|
506
|
+
if ((this._pendingRequests.length == 0 || this._bgErr) &&
|
|
507
|
+
this.getConnectionsOpen() >= this._poolMin) {
|
|
495
508
|
await new Promise((resolve) => {
|
|
496
509
|
this.bgWaiter = resolve;
|
|
497
510
|
});
|
|
@@ -503,6 +516,27 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
503
516
|
this._poolCloseWaiter();
|
|
504
517
|
}
|
|
505
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
|
+
|
|
506
540
|
//---------------------------------------------------------------------------
|
|
507
541
|
// acquire()
|
|
508
542
|
//
|
|
@@ -514,9 +548,10 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
514
548
|
while (this._freeConnectionList.length > 0) {
|
|
515
549
|
const conn = this._freeConnectionList.pop();
|
|
516
550
|
|
|
517
|
-
// if connection is unhealthy,
|
|
518
|
-
|
|
519
|
-
|
|
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);
|
|
520
555
|
continue;
|
|
521
556
|
}
|
|
522
557
|
|
|
@@ -595,11 +630,11 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
595
630
|
if (conn.nscon.connected) {
|
|
596
631
|
conn._lastTimeUsed = Date.now();
|
|
597
632
|
conn._newSession = false;
|
|
598
|
-
if ((this.
|
|
599
|
-
|
|
633
|
+
if ((this.getConnectionsOpen() < this._poolMax) &&
|
|
634
|
+
this._shouldRetainInPool(conn)) {
|
|
600
635
|
this._freeConnectionList.push(conn);
|
|
601
636
|
} else {
|
|
602
|
-
this.
|
|
637
|
+
this._invokeBgCleaner(conn);
|
|
603
638
|
}
|
|
604
639
|
}
|
|
605
640
|
|
|
@@ -652,9 +687,51 @@ class ThinPoolImpl extends PoolImpl {
|
|
|
652
687
|
this._poolMin = params.poolMin;
|
|
653
688
|
}
|
|
654
689
|
|
|
655
|
-
if (
|
|
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)
|
|
656
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;
|
|
657
733
|
}
|
|
734
|
+
return true;
|
|
658
735
|
}
|
|
659
736
|
}
|
|
660
737
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2022,
|
|
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,7 +120,8 @@ 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
126
|
constants.TNS_CCAP_VECTOR_FEATURE_BINARY | constants.TNS_CCAP_VECTOR_FEATURE_SPARSE;
|
|
123
127
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2022,
|
|
1
|
+
// Copyright (c) 2022, 2025, Oracle and/or its affiliates.
|
|
2
2
|
|
|
3
3
|
//-----------------------------------------------------------------------------
|
|
4
4
|
//
|
|
@@ -405,6 +405,7 @@ module.exports = {
|
|
|
405
405
|
TNS_DATA_TYPE_UD21: 639,
|
|
406
406
|
TNS_DATA_TYPE_TNP: 640,
|
|
407
407
|
TNS_DATA_TYPE_OAC: 646,
|
|
408
|
+
TNS_DATA_TYPE_SESSSIGN: 647,
|
|
408
409
|
TNS_DATA_TYPE_OER: 652,
|
|
409
410
|
TNS_DATA_TYPE_UDS: 663,
|
|
410
411
|
|
|
@@ -439,6 +440,7 @@ module.exports = {
|
|
|
439
440
|
// parameter keyword numbers,
|
|
440
441
|
TNS_KEYWORD_NUM_CURRENT_SCHEMA: 168,
|
|
441
442
|
TNS_KEYWORD_NUM_EDITION: 172,
|
|
443
|
+
TNS_KEYWORD_NUM_TRANSACTION_ID: 201,
|
|
442
444
|
|
|
443
445
|
// bind flags
|
|
444
446
|
TNS_BIND_USE_INDICATORS: 0x0001,
|
|
@@ -476,6 +478,7 @@ module.exports = {
|
|
|
476
478
|
TNS_SERVER_PIGGYBACK_LTXID: 7,
|
|
477
479
|
TNS_SERVER_PIGGYBACK_AC_REPLAY_CONTEXT: 8,
|
|
478
480
|
TNS_SERVER_PIGGYBACK_EXT_SYNC: 9,
|
|
481
|
+
TNS_SERVER_PIGGYBACK_SESS_SIGNATURE: 10,
|
|
479
482
|
|
|
480
483
|
// session return constants
|
|
481
484
|
TNS_SESSGET_SESSION_CHANGED: 4,
|
|
@@ -582,7 +585,9 @@ module.exports = {
|
|
|
582
585
|
TNS_CCAP_UB2_DTY: 27,
|
|
583
586
|
TNS_CCAP_OCI2: 31,
|
|
584
587
|
TNS_CCAP_CLIENT_FN: 34,
|
|
588
|
+
TNS_CCAP_OCI3: 35,
|
|
585
589
|
TNS_CCAP_TTC3: 37,
|
|
590
|
+
TNS_CCAP_SESS_SIGNATURE_VERSION: 39,
|
|
586
591
|
TNS_CCAP_TTC4: 40,
|
|
587
592
|
TNS_CCAP_LOB2: 42,
|
|
588
593
|
TNS_CCAP_TTC5: 44,
|
|
@@ -625,6 +630,7 @@ module.exports = {
|
|
|
625
630
|
TNS_CCAP_RPC_VERSION_MAX: 7,
|
|
626
631
|
TNS_CCAP_RPC_SIG_VALUE: 3,
|
|
627
632
|
TNS_CCAP_DBF_VERSION_MAX: 1,
|
|
633
|
+
TNS_CCAP_LTXID: 0x08,
|
|
628
634
|
TNS_CCAP_IMPLICIT_RESULTS: 0x10,
|
|
629
635
|
TNS_CCAP_BIG_CHUNK_CLR: 0x20,
|
|
630
636
|
TNS_CCAP_KEEP_OUT_ORDER: 0x80,
|
|
@@ -645,6 +651,8 @@ module.exports = {
|
|
|
645
651
|
TNS_CCAP_VECTOR_SUPPORT: 0x08,
|
|
646
652
|
TNS_CCAP_VECTOR_FEATURE_BINARY: 0x01,
|
|
647
653
|
TNS_CCAP_VECTOR_FEATURE_SPARSE: 0x02,
|
|
654
|
+
TNS_CCAP_TTC5_SESSIONLESS_TXNS: 0x20,
|
|
655
|
+
TNS_CCAP_OCI3_OCSSYNC: 0x20,
|
|
648
656
|
|
|
649
657
|
// runtime capability indices
|
|
650
658
|
TNS_RCAP_COMPAT: 0,
|
|
@@ -674,6 +682,7 @@ module.exports = {
|
|
|
674
682
|
// transaction switching op codes
|
|
675
683
|
TNS_TPC_TXN_START: 0x01,
|
|
676
684
|
TNS_TPC_TXN_DETACH: 0x02,
|
|
685
|
+
TNS_TPC_TXN_POST_DETACH: 0x04,
|
|
677
686
|
|
|
678
687
|
// transaction change state op codes
|
|
679
688
|
TNS_TPC_TXN_COMMIT: 0x01,
|
|
@@ -689,6 +698,25 @@ module.exports = {
|
|
|
689
698
|
TNS_TPC_TXN_STATE_READ_ONLY: 4,
|
|
690
699
|
TNS_TPC_TXN_STATE_FORGOTTEN: 5,
|
|
691
700
|
|
|
701
|
+
// sessionless transaction flag
|
|
702
|
+
TNS_TPC_TRANS_SESSIONLESS: 0x00000010,
|
|
703
|
+
|
|
704
|
+
// sessionless transaction format
|
|
705
|
+
TNS_TPC_TRANS_SESSIONLESS_FORMAT: 0x4e5c3e,
|
|
706
|
+
|
|
707
|
+
// sessionless sync version
|
|
708
|
+
TNS_TPC_TRANS_TRANSACTION_ID_SYNC_VERSION_1: 0x01,
|
|
709
|
+
|
|
710
|
+
// sessionless server states
|
|
711
|
+
TNS_TPC_TRANS_TRANSACTION_ID_SYNC_SET: 0x40,
|
|
712
|
+
TNS_TPC_TRANS_TRANSACTION_ID_SYNC_UNSET: 0x80,
|
|
713
|
+
|
|
714
|
+
// sessionless state reason
|
|
715
|
+
TNS_TPC_TRANS_TRANSACTION_ID_SYNC_SERVER: 0x01,
|
|
716
|
+
TNS_TPC_TRANS_TRANSACTION_ID_SYNC_CLIENT: 0x02,
|
|
717
|
+
TNS_TPC_TRANS_TRANSACTION_ID_SYNC_TXEND_XA: 0x03,
|
|
718
|
+
|
|
719
|
+
|
|
692
720
|
// other constants
|
|
693
721
|
TNS_ESCAPE_CHAR: 253,
|
|
694
722
|
TNS_LONG_LENGTH_INDICATOR: dataHandlerConstants.TNS_LONG_LENGTH_INDICATOR,
|
|
@@ -114,6 +114,7 @@ class AuthMessage extends Message {
|
|
|
114
114
|
} else if (process.env.ORA_EDITION) {
|
|
115
115
|
this.edition = process.env.ORA_EDITION;
|
|
116
116
|
}
|
|
117
|
+
this.appContext = config.appContext;
|
|
117
118
|
this.setAuthMode(config);
|
|
118
119
|
}
|
|
119
120
|
|
|
@@ -247,6 +248,9 @@ class AuthMessage extends Message {
|
|
|
247
248
|
if (this.edition) {
|
|
248
249
|
numPairs += 1;
|
|
249
250
|
}
|
|
251
|
+
if (this.appContext) {
|
|
252
|
+
numPairs += this.appContext.length * 3;
|
|
253
|
+
}
|
|
250
254
|
if (this.schemaUser.length !== 0) {
|
|
251
255
|
numPairs += 1;
|
|
252
256
|
}
|
|
@@ -319,6 +323,15 @@ class AuthMessage extends Message {
|
|
|
319
323
|
if (this.edition) {
|
|
320
324
|
buf.writeKeyValue("AUTH_ORA_EDITION", this.edition);
|
|
321
325
|
}
|
|
326
|
+
if (this.appContext) {
|
|
327
|
+
// NOTE: these keys require a trailing null character as the server
|
|
328
|
+
// expects it!
|
|
329
|
+
for (const entry of this.appContext) {
|
|
330
|
+
buf.writeKeyValue("AUTH_APPCTX_NSPACE\0", entry[0]);
|
|
331
|
+
buf.writeKeyValue("AUTH_APPCTX_ATTR\0", entry[1]);
|
|
332
|
+
buf.writeKeyValue("AUTH_APPCTX_VALUE\0", entry[2]);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
322
335
|
|
|
323
336
|
}
|
|
324
337
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2022,
|
|
1
|
+
// Copyright (c) 2022, 2025, Oracle and/or its affiliates.
|
|
2
2
|
|
|
3
3
|
//-----------------------------------------------------------------------------
|
|
4
4
|
//
|
|
@@ -30,6 +30,34 @@ const { Buffer } = require('buffer');
|
|
|
30
30
|
const constants = require("../constants.js");
|
|
31
31
|
const errors = require("../../../errors.js");
|
|
32
32
|
|
|
33
|
+
// List of recoverable errors
|
|
34
|
+
const RECOVERABLE_ERRORS = new Set([
|
|
35
|
+
28, // session has been terminated
|
|
36
|
+
31, // session marked for kill
|
|
37
|
+
376, // file %s cannot be read at this time
|
|
38
|
+
603, // ORACLE server session terminated
|
|
39
|
+
1012, // not logged on
|
|
40
|
+
1033, // ORACLE initialization or shutdown in progress
|
|
41
|
+
1034, // the Oracle instance is not available for use
|
|
42
|
+
1089, // immediate shutdown or close in progress
|
|
43
|
+
1090, // shutdown in progress
|
|
44
|
+
1092, // ORACLE instance terminated
|
|
45
|
+
1115, // IO error reading block from file %s (block # %s)
|
|
46
|
+
2396, // exceeded maximum idle time
|
|
47
|
+
3113, // end-of-file on communication channel
|
|
48
|
+
3114, // not connected to ORACLE
|
|
49
|
+
3135, // connection lost contact
|
|
50
|
+
12153, // TNS:not connected
|
|
51
|
+
12514, // Service %s is not registered with the listener
|
|
52
|
+
12537, // TNS:connection closed
|
|
53
|
+
12547, // TNS:lost contact
|
|
54
|
+
12570, // TNS:packet reader failure
|
|
55
|
+
12571, // TNS:packet writer failure
|
|
56
|
+
12583, // TNS:no reader
|
|
57
|
+
12757, // instance does not currently know of requested service
|
|
58
|
+
16456 // missing or invalid value
|
|
59
|
+
]);
|
|
60
|
+
|
|
33
61
|
/**
|
|
34
62
|
*
|
|
35
63
|
* Base class for all the RPC messages to support encode/decode functions
|
|
@@ -52,6 +80,7 @@ class Message {
|
|
|
52
80
|
async postProcess() { }
|
|
53
81
|
|
|
54
82
|
writeFunctionHeader(buf) {
|
|
83
|
+
this.writePiggybacks(buf);
|
|
55
84
|
buf.writeUInt8(this.messageType);
|
|
56
85
|
buf.writeUInt8(this.functionCode);
|
|
57
86
|
buf.writeSeqNum();
|
|
@@ -160,6 +189,8 @@ class Message {
|
|
|
160
189
|
*/
|
|
161
190
|
this.errorInfo.message = this.errorInfo.message.trim();
|
|
162
191
|
}
|
|
192
|
+
this.errorInfo.isRecoverable = RECOVERABLE_ERRORS.has(this.errorInfo.num);
|
|
193
|
+
|
|
163
194
|
this.endOfResponse = !this.connection.nscon.endOfRequestSupport;
|
|
164
195
|
}
|
|
165
196
|
|
|
@@ -220,7 +251,7 @@ class Message {
|
|
|
220
251
|
if (opcode === constants.TNS_SERVER_PIGGYBACK_LTXID) {
|
|
221
252
|
const num_bytes = buf.readUB4();
|
|
222
253
|
if (num_bytes > 0) {
|
|
223
|
-
buf.
|
|
254
|
+
this.connection._ltxid = Buffer.from(buf.readBytesWithLength());
|
|
224
255
|
}
|
|
225
256
|
} else if ((opcode === constants.TNS_SERVER_PIGGYBACK_QUERY_CACHE_INVALIDATION)
|
|
226
257
|
|| (opcode === constants.TNS_SERVER_PIGGYBACK_TRACE_EVENT)) {
|
|
@@ -235,15 +266,22 @@ class Message {
|
|
|
235
266
|
const num_elements = buf.readUB4();
|
|
236
267
|
buf.skipBytes(1); // skip length
|
|
237
268
|
for (let i = 0; i < num_elements; i++) {
|
|
238
|
-
let
|
|
239
|
-
|
|
240
|
-
|
|
269
|
+
let numBytes = buf.readUB2();
|
|
270
|
+
let keyTextValue, value;
|
|
271
|
+
if (numBytes > 0) { // read key
|
|
272
|
+
keyTextValue = buf.readStr(constants.CSFRM_IMPLICIT);
|
|
241
273
|
}
|
|
242
|
-
|
|
243
|
-
if (
|
|
244
|
-
buf.
|
|
274
|
+
numBytes = buf.readUB2();
|
|
275
|
+
if (numBytes > 0) // read value
|
|
276
|
+
value = buf.readBytesWithLength();
|
|
277
|
+
const keywordNum = buf.readUB2(); // read keyword number
|
|
278
|
+
if (keywordNum == constants.TNS_KEYWORD_NUM_TRANSACTION_ID) {
|
|
279
|
+
this._updateSessionlessTxnState(value);
|
|
280
|
+
} else if (keywordNum === constants.TNS_KEYWORD_NUM_CURRENT_SCHEMA) {
|
|
281
|
+
this.connection.currentSchema = keyTextValue;
|
|
282
|
+
} else if (keywordNum === constants.TNS_KEYWORD_NUM_EDITION) {
|
|
283
|
+
this.connection._edition = keyTextValue;
|
|
245
284
|
}
|
|
246
|
-
buf.skipUB2(); // skip flags
|
|
247
285
|
}
|
|
248
286
|
buf.skipUB4(); // skip overall flags
|
|
249
287
|
} else if (opcode === constants.TNS_SERVER_PIGGYBACK_EXT_SYNC) {
|
|
@@ -286,12 +324,22 @@ class Message {
|
|
|
286
324
|
this.connection._drcpEstablishSession = false;
|
|
287
325
|
buf.skipUB4(); // session id
|
|
288
326
|
buf.skipUB2(); // serial number
|
|
327
|
+
} else if (opcode === constants.TNS_SERVER_PIGGYBACK_SESS_SIGNATURE) {
|
|
328
|
+
buf.skipUB2(); // number of dtys
|
|
329
|
+
buf.skipUB1(); // length of dty
|
|
330
|
+
buf.skipUB8(); // signature flags
|
|
331
|
+
buf.skipUB8(); // client signature
|
|
332
|
+
buf.skipUB8(); // server signature
|
|
289
333
|
} else {
|
|
290
334
|
errors.throwErr(errors.ERR_UNKOWN_SERVER_SIDE_PIGGYBACK, opcode);
|
|
291
335
|
}
|
|
292
336
|
}
|
|
293
337
|
|
|
294
338
|
writePiggybacks(buf) {
|
|
339
|
+
// Don't write piggybacks if not authenticated yet
|
|
340
|
+
if (this.connection._protocol.connInProgress)
|
|
341
|
+
return;
|
|
342
|
+
|
|
295
343
|
if (this.connection._currentSchemaModified) {
|
|
296
344
|
this._writeCurrentSchemaPiggyback(buf);
|
|
297
345
|
}
|
|
@@ -313,6 +361,11 @@ class Message {
|
|
|
313
361
|
if (this.connection._tempLobsTotalSize > 0) {
|
|
314
362
|
this.writeCloseTempLobsPiggyback(buf);
|
|
315
363
|
}
|
|
364
|
+
if (this.connection._sessionlessData?.pending) {
|
|
365
|
+
this.connection._sessionlessData.pending = false;
|
|
366
|
+
this.connection._sessionlessData.piggyback.encode(buf);
|
|
367
|
+
this.connection._sessionlessData.piggyback = null;
|
|
368
|
+
}
|
|
316
369
|
}
|
|
317
370
|
|
|
318
371
|
writePiggybackHeader(buf, functionCode) {
|
|
@@ -474,6 +527,42 @@ class Message {
|
|
|
474
527
|
this.deferredErr = errors.getErr(...arguments);
|
|
475
528
|
}
|
|
476
529
|
}
|
|
530
|
+
|
|
531
|
+
_updateSessionlessTxnState(buf) {
|
|
532
|
+
const length = buf.length;
|
|
533
|
+
const sessionlessState = buf.readUInt8(length - 2);
|
|
534
|
+
const syncVersion = buf.readUInt8(length - 1);
|
|
535
|
+
let startedOnServer;
|
|
536
|
+
|
|
537
|
+
if (syncVersion == constants.TNS_TPC_TRANS_TRANSACTION_ID_SYNC_VERSION_1) {
|
|
538
|
+
// transactionId got unset for this session (txn ended or suspended)
|
|
539
|
+
if (sessionlessState &
|
|
540
|
+
constants.TNS_TPC_TRANS_TRANSACTION_ID_SYNC_UNSET) {
|
|
541
|
+
this.connection._sessionlessData = null;
|
|
542
|
+
this.connection._protocol.txnInProgress = false;
|
|
543
|
+
// transactionId got set for this session
|
|
544
|
+
} else if (sessionlessState &
|
|
545
|
+
constants.TNS_TPC_TRANS_TRANSACTION_ID_SYNC_SET) {
|
|
546
|
+
|
|
547
|
+
// transaction initialized by PL/SQL procedure
|
|
548
|
+
if (sessionlessState &
|
|
549
|
+
constants.TNS_TPC_TRANS_TRANSACTION_ID_SYNC_SERVER)
|
|
550
|
+
startedOnServer = true;
|
|
551
|
+
// transaction initialized by calling client API(RPC call)
|
|
552
|
+
else if (sessionlessState &
|
|
553
|
+
constants.TNS_TPC_TRANS_TRANSACTION_ID_SYNC_CLIENT)
|
|
554
|
+
startedOnServer = false;
|
|
555
|
+
|
|
556
|
+
this.connection._sessionlessData = {
|
|
557
|
+
startedOnServer: startedOnServer,
|
|
558
|
+
};
|
|
559
|
+
this.connection._protocol.txnInProgress = true;
|
|
560
|
+
}
|
|
561
|
+
} else
|
|
562
|
+
errors.throwErr(errors.ERR_INTERNAL, 'Unexpected TransactionId sync ' +
|
|
563
|
+
'version in session sync piggyback.');
|
|
564
|
+
}
|
|
565
|
+
|
|
477
566
|
}
|
|
478
567
|
|
|
479
568
|
module.exports = Message;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2022,
|
|
1
|
+
// Copyright (c) 2022, 2025, Oracle and/or its affiliates.
|
|
2
2
|
|
|
3
3
|
//-----------------------------------------------------------------------------
|
|
4
4
|
//
|
|
@@ -394,6 +394,7 @@ const dataTypes = [
|
|
|
394
394
|
[constants.TNS_DATA_TYPE_TNP, constants.TNS_DATA_TYPE_TNP, constants.TNS_TYPE_REP_UNIVERSAL],
|
|
395
395
|
[constants.TNS_DATA_TYPE_OER, constants.TNS_DATA_TYPE_OER, constants.TNS_TYPE_REP_UNIVERSAL],
|
|
396
396
|
[constants.TNS_DATA_TYPE_OAC, constants.TNS_DATA_TYPE_OAC, constants.TNS_TYPE_REP_UNIVERSAL],
|
|
397
|
+
[constants.TNS_DATA_TYPE_SESSSIGN, constants.TNS_DATA_TYPE_SESSSIGN, constants.TNS_TYPE_REP_UNIVERSAL],
|
|
397
398
|
[constants.TNS_DATA_TYPE_VECTOR, constants.TNS_DATA_TYPE_VECTOR, constants.TNS_TYPE_REP_UNIVERSAL]
|
|
398
399
|
];
|
|
399
400
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2022,
|
|
1
|
+
// Copyright (c) 2022, 2025, Oracle and/or its affiliates.
|
|
2
2
|
|
|
3
3
|
//-----------------------------------------------------------------------------
|
|
4
4
|
//
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
const MessageWithData = require("./withData.js");
|
|
30
30
|
const constants = require("../constants.js");
|
|
31
31
|
const errors = require("../../../errors.js");
|
|
32
|
+
const TransactionSwitchMessage = require("./transactionSwitch.js");
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
*
|
|
@@ -110,12 +111,18 @@ class ExecuteMessage extends MessageWithData {
|
|
|
110
111
|
options |= constants.TNS_EXEC_OPTION_BATCH_ERRORS;
|
|
111
112
|
}
|
|
112
113
|
if (this.arrayDmlRowCounts) {
|
|
113
|
-
dmlOptions
|
|
114
|
+
dmlOptions |= constants.TNS_EXEC_OPTION_DML_ROWCOUNTS;
|
|
114
115
|
}
|
|
115
116
|
if (this.options.autoCommit) {
|
|
116
117
|
options |= constants.TNS_EXEC_OPTION_COMMIT;
|
|
117
118
|
}
|
|
118
|
-
|
|
119
|
+
|
|
120
|
+
// In case SuspendOnSuccess is set for execute and a sessionless transaction
|
|
121
|
+
// is active we add a piggyback for post-call suspend
|
|
122
|
+
if (this.options.suspendOnSuccess) {
|
|
123
|
+
this._handleSuspendSessionless();
|
|
124
|
+
}
|
|
125
|
+
|
|
119
126
|
this.writeFunctionHeader(buf);
|
|
120
127
|
buf.writeUB4(options); // execute options
|
|
121
128
|
buf.writeUB4(stmt.cursorId); // cursor id
|
|
@@ -258,7 +265,13 @@ class ExecuteMessage extends MessageWithData {
|
|
|
258
265
|
}
|
|
259
266
|
numIters = this.numExecs;
|
|
260
267
|
}
|
|
261
|
-
|
|
268
|
+
|
|
269
|
+
// In case SuspendOnSuccess is set for execute and a sessionless transaction
|
|
270
|
+
// is active we add a piggyback for post-call suspend
|
|
271
|
+
if (this.options.suspendOnSuccess) {
|
|
272
|
+
this._handleSuspendSessionless();
|
|
273
|
+
}
|
|
274
|
+
|
|
262
275
|
this.writeFunctionHeader(buf);
|
|
263
276
|
buf.writeUB4(stmt.cursorId);
|
|
264
277
|
buf.writeUB4(numIters);
|
|
@@ -267,6 +280,35 @@ class ExecuteMessage extends MessageWithData {
|
|
|
267
280
|
return params;
|
|
268
281
|
}
|
|
269
282
|
|
|
283
|
+
//-------------------------------------------------------------------------
|
|
284
|
+
// _handleSuspendSessionless()
|
|
285
|
+
//
|
|
286
|
+
// Suspend the active sessionless transaction after DML execution if
|
|
287
|
+
// suspendOnSuccess option was given to execute()
|
|
288
|
+
//-------------------------------------------------------------------------
|
|
289
|
+
_handleSuspendSessionless() {
|
|
290
|
+
const sessionlessData = this.connection._sessionlessData;
|
|
291
|
+
// If sessionless transaction was started by server-call we cannot suspend
|
|
292
|
+
// using client API(RPC) calls
|
|
293
|
+
this.connection._validateSessionlessState();
|
|
294
|
+
|
|
295
|
+
if (!sessionlessData)
|
|
296
|
+
errors.throwErr(errors.ERR_SESSIONLESS_INACTIVE);
|
|
297
|
+
// If a piggybacks already exists it is most likely a start/resume for a
|
|
298
|
+
// sessionless transaction, add a post-call detach opcode to it
|
|
299
|
+
if (sessionlessData.piggyback) {
|
|
300
|
+
sessionlessData.piggyback.operation |=
|
|
301
|
+
constants.TNS_TPC_TXN_POST_DETACH;
|
|
302
|
+
} else {
|
|
303
|
+
const message = new TransactionSwitchMessage(this.connection);
|
|
304
|
+
message.operation = constants.TNS_TPC_TXN_POST_DETACH;
|
|
305
|
+
message.flags = constants.TNS_TPC_TRANS_SESSIONLESS;
|
|
306
|
+
message.messageType = constants.TNS_MSG_TYPE_PIGGYBACK;
|
|
307
|
+
sessionlessData.piggyback = message;
|
|
308
|
+
sessionlessData.pending = true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
270
312
|
//-------------------------------------------------------------------------
|
|
271
313
|
// encode()
|
|
272
314
|
//
|