oracledb 7.0.0 → 26.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 (84) hide show
  1. package/README.md +16 -6
  2. package/build/Release/oracledb-26.0.0-darwin-arm64.node +0 -0
  3. package/build/Release/oracledb-26.0.0-darwin-arm64.node-buildinfo.txt +1 -0
  4. package/build/Release/oracledb-26.0.0-js-buildinfo.txt +1 -0
  5. package/build/Release/oracledb-26.0.0-linux-arm64.node +0 -0
  6. package/build/Release/oracledb-26.0.0-linux-arm64.node-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-26.0.0-linux-x64.node +0 -0
  8. package/build/Release/oracledb-26.0.0-linux-x64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-26.0.0-win32-x64.node +0 -0
  10. package/build/Release/oracledb-26.0.0-win32-x64.node-buildinfo.txt +1 -0
  11. package/lib/aqDeqOptions.js +4 -4
  12. package/lib/aqEnqOptions.js +2 -2
  13. package/lib/aqQueue.js +11 -6
  14. package/lib/connection.js +73 -18
  15. package/lib/constants.js +4 -0
  16. package/lib/endUserSecurityContext.js +30 -25
  17. package/lib/errors.js +92 -5
  18. package/lib/impl/connection.js +36 -0
  19. package/lib/impl/datahandlers/buffer.js +91 -18
  20. package/lib/impl/datahandlers/oson.js +3 -2
  21. package/lib/impl/parserHelpers.js +6 -2
  22. package/lib/impl/resultset.js +32 -11
  23. package/lib/oracledb.js +34 -0
  24. package/lib/pool.js +6 -2
  25. package/lib/queryStream.js +13 -5
  26. package/lib/resultset.js +10 -3
  27. package/lib/securityContextProvider.js +206 -0
  28. package/lib/thin/aq.js +2 -3
  29. package/lib/thin/connection.js +380 -24
  30. package/lib/thin/dbObject.js +20 -11
  31. package/lib/thin/pool.js +4 -3
  32. package/lib/thin/protocol/capabilities.js +37 -4
  33. package/lib/thin/protocol/constants.js +49 -1
  34. package/lib/thin/protocol/messages/aqArray.js +2 -2
  35. package/lib/thin/protocol/messages/aqBase.js +2 -2
  36. package/lib/thin/protocol/messages/auth.js +17 -6
  37. package/lib/thin/protocol/messages/base.js +75 -17
  38. package/lib/thin/protocol/messages/dataType.js +46 -0
  39. package/lib/thin/protocol/messages/directPathLoadStream.js +3 -2
  40. package/lib/thin/protocol/messages/execute.js +10 -4
  41. package/lib/thin/protocol/messages/index.js +4 -0
  42. package/lib/thin/protocol/messages/notification.js +281 -0
  43. package/lib/thin/protocol/messages/subscribe.js +181 -0
  44. package/lib/thin/protocol/messages/withData.js +23 -10
  45. package/lib/thin/protocol/packet.js +2 -4
  46. package/lib/thin/protocol/protocol.js +7 -4
  47. package/lib/thin/resultSet.js +40 -20
  48. package/lib/thin/sqlnet/ezConnectResolver.js +9 -2
  49. package/lib/thin/sqlnet/navNodes.js +7 -5
  50. package/lib/thin/sqlnet/networkSession.js +17 -11
  51. package/lib/thin/sqlnet/ntTcp.js +24 -8
  52. package/lib/thin/sqlnet/packet.js +24 -4
  53. package/lib/thin/sqlnet/sessionAtts.js +2 -2
  54. package/lib/thin/statement.js +6 -1
  55. package/lib/thin/statementCache.js +34 -6
  56. package/lib/thin/subscr.js +363 -0
  57. package/lib/transformer.js +60 -17
  58. package/lib/util.js +46 -8
  59. package/lib/version.js +1 -1
  60. package/package.json +1 -1
  61. package/plugins/configProviders/gcpstorage/index.js +121 -0
  62. package/plugins/configProviders/ociobject/index.js +1 -1
  63. package/plugins/token/cache.js +154 -0
  64. package/plugins/token/endUserSecurityProvider/index.js +377 -0
  65. package/plugins/token/extensionAzure/index.js +160 -28
  66. package/plugins/token/extensionOci/index.js +221 -16
  67. package/plugins/vectorsdk/OracleVecDB.js +346 -0
  68. package/plugins/vectorsdk/index.js +49 -0
  69. package/plugins/vectorsdk/utils.js +370 -0
  70. package/plugins/vectorsdk/vectorEmbedding.js +223 -0
  71. package/plugins/vectorsdk/vectorIndex.js +177 -0
  72. package/plugins/vectorsdk/vectorRecords.js +406 -0
  73. package/plugins/vectorsdk/vectorSchema.js +408 -0
  74. package/plugins/vectorsdk/vectorSearch.js +538 -0
  75. package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
  76. package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +0 -1
  77. package/build/Release/oracledb-7.0.0-js-buildinfo.txt +0 -1
  78. package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
  79. package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +0 -1
  80. package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
  81. package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +0 -1
  82. package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
  83. package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +0 -1
  84. package/eslint.config.js +0 -39
@@ -39,10 +39,20 @@ const sqlNetConstants = require('./sqlnet/constants.js');
39
39
  const constants = require('./protocol/constants.js');
40
40
  const process = require('process');
41
41
  const types = require('../types.js');
42
+ const pubConstants = require('../constants.js');
42
43
  const errors = require("../errors.js");
43
44
  const messages = require('./protocol/messages');
44
45
  const StatementCache = require('./statementCache.js');
46
+ const ThinSubscrImpl = require('./subscr.js');
45
47
  const { ThinQueueImpl } = require('./aq.js');
48
+ const parserHelpers = require('../impl/parserHelpers.js');
49
+ const { createNVPair, findNVPair } = require('./sqlnet/nvStrToNvPair.js');
50
+ const { ObfuscatedValue } = require('../obfuscation.js');
51
+
52
+ // Oracle can return a named UTC alias instead of the equivalent fixed offset
53
+ // +00:00. These aliases have no offset changes, so Thin can safely treat them
54
+ // as a zero fixed offset after the DBTIMEZONE fallback query identifies one.
55
+ const UTC_DB_TIME_ZONE_NAMES = new Set(['UTC', 'GMT', 'UNIVERSAL', 'ZULU']);
46
56
 
47
57
  const finalizationRegistry = new global.FinalizationRegistry((heldValue) => {
48
58
  heldValue.disconnect();
@@ -84,6 +94,7 @@ class ThinConnectionImpl extends ConnectionImpl {
84
94
  async close() {
85
95
  try {
86
96
  this.clearEndUserSecurityContext();
97
+ this._clearDatabaseOpenTelemetryTracing();
87
98
  if (this._protocol.txnInProgress) {
88
99
  if (this.tpcContext) {
89
100
  const message = this.createTpcRollbackMessage();
@@ -246,7 +257,9 @@ class ThinConnectionImpl extends ConnectionImpl {
246
257
  if (statement.isPlSql && (statement.cursorId === 0 ||
247
258
  statement.requiresFullExecute)) {
248
259
  message.numExecs = 1;
249
- message.noImplicitRelease = true;
260
+ // Suppress release only when another iteration follows.
261
+ message.noImplicitRelease = numIters > 1;
262
+
250
263
  await this._protocol._processMessage(message);
251
264
  statement.requiresFullExecute = false;
252
265
  message.numExecs = numIters - 1;
@@ -286,7 +299,6 @@ class ThinConnectionImpl extends ConnectionImpl {
286
299
  if (statement.isPlSql && (statement.cursorId === 0 ||
287
300
  statement.requiresFullExecute)) {
288
301
  message.numExecs = 1;
289
- message.noImplicitRelease = true;
290
302
  await this._protocol._encodeMessage(message);
291
303
  message.requiresSingleExecute = true;
292
304
  } else if (message.numExecs > 0) {
@@ -637,13 +649,19 @@ class ThinConnectionImpl extends ConnectionImpl {
637
649
  case constants.TNS_OBJ_TDS_TYPE_DATE:
638
650
  return types.DB_TYPE_DATE;
639
651
  case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP:
640
- buf.skipBytes(1); // precision
652
+ metaData.scale = buf.readInt8();
653
+ if (metaData.scale !== null && metaData.scale !== undefined)
654
+ metaData.precision = 0;
641
655
  return types.DB_TYPE_TIMESTAMP;
642
656
  case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_LTZ:
643
- buf.skipBytes(1); // precision
657
+ metaData.scale = buf.readInt8();
658
+ if (metaData.scale !== null && metaData.scale !== undefined)
659
+ metaData.precision = 0;
644
660
  return types.DB_TYPE_TIMESTAMP_LTZ;
645
661
  case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_TZ:
646
- buf.skipBytes(1); // precision
662
+ metaData.scale = buf.readInt8();
663
+ if (metaData.scale !== null && metaData.scale !== undefined)
664
+ metaData.precision = 0;
647
665
  return types.DB_TYPE_TIMESTAMP_TZ;
648
666
  case constants.TNS_OBJ_TDS_TYPE_BOOLEAN:
649
667
  return types.DB_TYPE_BOOLEAN;
@@ -750,8 +768,10 @@ class ThinConnectionImpl extends ConnectionImpl {
750
768
  case
751
769
  when data_precision is null and data_scale is null
752
770
  then 0
753
- when data_precision is null
771
+ when data_precision is null and data_type = 'NUMBER'
754
772
  then 38
773
+ when data_precision is null and data_type != 'NUMBER'
774
+ then 0
755
775
  else data_precision
756
776
  end,
757
777
  case
@@ -1019,9 +1039,12 @@ class ThinConnectionImpl extends ConnectionImpl {
1019
1039
  if (tempMaxSize)
1020
1040
  attr.maxSize = tempMaxSize;
1021
1041
  }
1022
- if (attr.type === types.DB_TYPE_NUMBER) {
1023
- attr.precision = Number(attrInfo.dataPrecision);
1024
- attr.scale = Number(attrInfo.dataScale);
1042
+ if (attr.type === types.DB_TYPE_NUMBER ||
1043
+ attr.type === types.DB_TYPE_TIMESTAMP ||
1044
+ attr.type === types.DB_TYPE_TIMESTAMP_TZ ||
1045
+ attr.type === types.DB_TYPE_TIMESTAMP_LTZ) {
1046
+ attr.precision = Number(attrInfo.dataPrecision ?? 0);
1047
+ attr.scale = Number(attrInfo.dataScale ?? 0);
1025
1048
  }
1026
1049
  }
1027
1050
  }
@@ -1147,12 +1170,16 @@ class ThinConnectionImpl extends ConnectionImpl {
1147
1170
  this._clientIdentifierModified = false;
1148
1171
  this._action = "";
1149
1172
  this._actionModified = false;
1173
+ this._ecid = "";
1174
+ this._ecidModified = false;
1150
1175
  this._dbOp = "";
1151
1176
  this._dbOpModified = false;
1152
1177
  this._clientInfo = "";
1153
1178
  this._clientInfoModified = false;
1154
1179
  this._module = "";
1155
1180
  this._moduleModified = false;
1181
+ this.databaseOpenTelemetryTracing = false;
1182
+ this._otelModified = false;
1156
1183
  this._drcpEnabled = false;
1157
1184
  this.serviceName = '';
1158
1185
  this.remoteAddress = '';
@@ -1160,6 +1187,13 @@ class ThinConnectionImpl extends ConnectionImpl {
1160
1187
  this.tpcContext = null;
1161
1188
  this._sessionlessData = null;
1162
1189
  this._nameSpaceMap = new Map();
1190
+ this._txnPriority = "";
1191
+ this._pendingTxnPriority = undefined;
1192
+ this._txnPriorityModified = false;
1193
+
1194
+ if (!this._isNotificationConnection) {
1195
+ this._setNotificationConnectParams(params);
1196
+ }
1163
1197
 
1164
1198
  this.nscon = new nsi();
1165
1199
  finalizationRegistry.register(this, this.nscon);
@@ -1234,15 +1268,22 @@ class ThinConnectionImpl extends ConnectionImpl {
1234
1268
  await this._protocol._processMessage(authMessage); // OAUTH
1235
1269
  }
1236
1270
  this._postConnect(authMessage);
1271
+ if (params.txnPriority === "") {
1272
+ this.setTxnPriority("");
1273
+ }
1274
+
1275
+ this.statementCache = new StatementCache(this.statementCacheSize);
1276
+ // maintain a list of partially populated database object types
1277
+ this._partialDbObjectTypes = [];
1278
+
1279
+ if (this._requiresDbTimeZoneInitialization()) {
1280
+ await this._initializeDbTimeZone();
1281
+ }
1237
1282
  } catch (err) {
1238
1283
  this.nscon.disconnect();
1239
1284
  throw err;
1240
1285
  }
1241
1286
 
1242
- this.statementCache = new StatementCache(this.statementCacheSize);
1243
- // maintain a list of partially populated database object types
1244
- this._partialDbObjectTypes = [];
1245
-
1246
1287
  if (params.debugJDWP) {
1247
1288
  this.jdwpData = Buffer.from(params.debugJDWP);
1248
1289
  } else if (process.env.ORA_DEBUG_JDWP) {
@@ -1251,6 +1292,68 @@ class ThinConnectionImpl extends ConnectionImpl {
1251
1292
  this._protocol.connInProgress = false;
1252
1293
  }
1253
1294
 
1295
+ //---------------------------------------------------------------------------
1296
+ // _requiresDbTimeZoneInitialization()
1297
+ //
1298
+ // The DBTIMEZONE fallback is an ordinary SQL query and must only be sent on
1299
+ // application connections. An EMON notification connection never fetches or
1300
+ // binds TIMESTAMP WITH LOCAL TIME ZONE values, and servers can close it when
1301
+ // sent this query.
1302
+ //---------------------------------------------------------------------------
1303
+ _requiresDbTimeZoneInitialization() {
1304
+ return !this._isNotificationConnection &&
1305
+ this._protocol.caps.dbTzOffset === null;
1306
+ }
1307
+
1308
+ //---------------------------------------------------------------------------
1309
+ // _initializeDbTimeZone()
1310
+ //
1311
+ // A TZ_EX response supplies a fixed DBTIMEZONE offset directly. Region
1312
+ // responses supply an Oracle region ID instead, and older servers do not
1313
+ // return a DBTIMEZONE value. In both cases, query DBTIMEZONE once to get
1314
+ // the offset, recognize a named UTC zero-offset alias, or identify a
1315
+ // region configuration unsupported for LTZ.
1316
+ //---------------------------------------------------------------------------
1317
+ async _initializeDbTimeZone() {
1318
+ const options = {
1319
+ connection: { _impl: this },
1320
+ keepInStmtCache: false
1321
+ };
1322
+
1323
+ // Use DBTIMEZONE so named regions are preserved. Using TZ_OFFSET(DBTIMEZONE)
1324
+ // would reduce a DST-aware region to a single offset.
1325
+ const result = await this.execute('SELECT DBTIMEZONE FROM DUAL', 1, [],
1326
+ options, false);
1327
+ let rows;
1328
+ try {
1329
+ rows = await result.resultSet.getRows(1, options);
1330
+ } finally {
1331
+ await result.resultSet.close();
1332
+ }
1333
+ const value = rows[0][0];
1334
+ const match = /^([+-])(\d{2}):(\d{2})$/.exec(value);
1335
+ if (match || UTC_DB_TIME_ZONE_NAMES.has(value.toUpperCase())) {
1336
+ let offset = 0;
1337
+ if (match) {
1338
+ offset = Number(match[2]) * 60 + Number(match[3]);
1339
+ if (match[1] === '-') {
1340
+ offset = -offset;
1341
+ }
1342
+ }
1343
+ this._protocol.caps.dbTzOffset = offset;
1344
+
1345
+ // A fixed DBTIMEZONE, including a UTC alias, does not need a region
1346
+ // name or Oracle timezone-rule processing.
1347
+ this._protocol.caps.dbTzRegion = null;
1348
+ } else {
1349
+ // The TTC response contains only an Oracle internal region ID. Do not
1350
+ // use Node.js ICU rules as a substitute for Oracle's timezone rules.
1351
+ // LTZ fetches and binds reject this configuration in BaseBuffer.
1352
+ this._protocol.caps.dbTzOffset = null;
1353
+ this._protocol.caps.dbTzRegion = value;
1354
+ }
1355
+ }
1356
+
1254
1357
  //---------------------------------------------------------------------------
1255
1358
  // Return the statement to the statement cache, if applicable
1256
1359
  //---------------------------------------------------------------------------
@@ -1258,6 +1361,35 @@ class ThinConnectionImpl extends ConnectionImpl {
1258
1361
  this.statementCache.returnStatement(statement);
1259
1362
  }
1260
1363
 
1364
+ //---------------------------------------------------------------------------
1365
+ // Synchronize query variables after result set setup recalculates fetch
1366
+ // types for the current execute. If a LOB-like column's requested define
1367
+ // type has changed, the cached cursor cannot be reused as-is and must be
1368
+ // cleared so that a fresh define round trip is performed.
1369
+ //---------------------------------------------------------------------------
1370
+ _syncQueryVars(statement, previousTypes) {
1371
+ if (!statement.isQuery || statement.queryVars.length === 0) {
1372
+ return;
1373
+ }
1374
+
1375
+ const defineTypeChanged = statement.queryVars.some((variable, i) => {
1376
+ const dbType = variable.fetchInfo.dbType;
1377
+ const requiresLobDefine = dbType === types.DB_TYPE_CLOB ||
1378
+ dbType === types.DB_TYPE_NCLOB ||
1379
+ dbType === types.DB_TYPE_BLOB ||
1380
+ dbType === types.DB_TYPE_JSON ||
1381
+ dbType === types.DB_TYPE_VECTOR;
1382
+ return requiresLobDefine &&
1383
+ previousTypes[i] !== variable.fetchInfo.fetchType;
1384
+ });
1385
+
1386
+ if (defineTypeChanged && statement.cursorId !== 0) {
1387
+ // Clearing the cursor forces a full TNS_FUNC_EXECUTE with fresh defines.
1388
+ this.statementCache._addCursorToClose(statement);
1389
+ statement._clearState();
1390
+ }
1391
+ }
1392
+
1261
1393
  //---------------------------------------------------------------------------
1262
1394
  // Parses the sql statement and puts it into cache if keepInStmtCache
1263
1395
  // option is true
@@ -1265,6 +1397,7 @@ class ThinConnectionImpl extends ConnectionImpl {
1265
1397
  _prepare(sql, options) {
1266
1398
  const forceNew = Boolean(options.fetchTypeMap);
1267
1399
  const statement = this._getStatement(sql, options.keepInStmtCache, forceNew);
1400
+ statement._registrationId = options._subscriptionRegId || 0n;
1268
1401
  statement.bufferRowIndex = 0;
1269
1402
  statement.bufferRowCount = 0;
1270
1403
  statement.lastRowIndex = 0;
@@ -1272,6 +1405,124 @@ class ThinConnectionImpl extends ConnectionImpl {
1272
1405
  return statement;
1273
1406
  }
1274
1407
 
1408
+ //---------------------------------------------------------------------------
1409
+ // _buildNotificationConnectString()
1410
+ //
1411
+ // Build a connect string that requests an EMON server connection.
1412
+ //---------------------------------------------------------------------------
1413
+ async _buildNotificationConnectString(connectString, configDir) {
1414
+ if (!connectString) {
1415
+ return connectString;
1416
+ }
1417
+
1418
+ // Resolve aliases before modifying CONNECT_DATA so every connection
1419
+ // string form, including tnsnames.ora aliases, requests EMON.
1420
+ const resolved = await parserHelpers.resolveConnectStr(connectString,
1421
+ configDir);
1422
+ const descriptor = typeof resolved === 'string' ? resolved :
1423
+ resolved.toString();
1424
+
1425
+ let root;
1426
+ try {
1427
+ root = createNVPair(descriptor);
1428
+ } catch {
1429
+ return connectString;
1430
+ }
1431
+
1432
+ const stack = [root];
1433
+ while (stack.length > 0) {
1434
+ const node = stack.pop();
1435
+ if (!node) {
1436
+ continue;
1437
+ }
1438
+
1439
+ if (String(node.name).toUpperCase() === "DESCRIPTION") {
1440
+ let connectData = findNVPair(node, "CONNECT_DATA");
1441
+ if (!connectData) {
1442
+ connectData = new node.constructor("CONNECT_DATA");
1443
+ node.addListElement(connectData);
1444
+ }
1445
+
1446
+ let serverNode = findNVPair(connectData, "SERVER");
1447
+ if (!serverNode) {
1448
+ serverNode = new connectData.constructor("SERVER");
1449
+ connectData.addListElement(serverNode);
1450
+ }
1451
+ serverNode.setAtom = "EMON";
1452
+ }
1453
+
1454
+ for (let i = 0; i < node.getListSize(); i++) {
1455
+ stack.push(node.getListElement(i));
1456
+ }
1457
+ }
1458
+
1459
+ return root.toString();
1460
+ }
1461
+
1462
+ //---------------------------------------------------------------------------
1463
+ // _setNotificationConnectParams()
1464
+ //
1465
+ // Retain only the connection attributes needed to establish the dedicated
1466
+ // notification connection. Sensitive values are obfuscated rather than kept
1467
+ // directly on the connection object.
1468
+ //---------------------------------------------------------------------------
1469
+ _setNotificationConnectParams(params) {
1470
+ this._connectParams = {...params};
1471
+
1472
+ // _connInfo contains pool-specific connection metadata. The EMON
1473
+ // connection must establish its own standalone connection.
1474
+ delete this._connectParams._connInfo;
1475
+ this._password = new ObfuscatedValue();
1476
+ this._walletPassword = new ObfuscatedValue();
1477
+ this._walletContent = new ObfuscatedValue();
1478
+ this._token = new ObfuscatedValue();
1479
+ this._privateKey = new ObfuscatedValue();
1480
+
1481
+ if (params.password !== undefined) {
1482
+ this._password.set(params.password);
1483
+ this._connectParams.password = null;
1484
+ }
1485
+ if (params.walletPassword !== undefined) {
1486
+ this._walletPassword.set(params.walletPassword);
1487
+ this._connectParams.walletPassword = null;
1488
+ }
1489
+ if (params.walletContent !== undefined) {
1490
+ this._walletContent.set(params.walletContent);
1491
+ this._connectParams.walletContent = null;
1492
+ }
1493
+ if (params.token !== undefined) {
1494
+ this._token.set(params.token);
1495
+ this._connectParams.token = null;
1496
+ }
1497
+ if (params.privateKey !== undefined) {
1498
+ this._privateKey.set(params.privateKey);
1499
+ this._connectParams.privateKey = null;
1500
+ }
1501
+ }
1502
+
1503
+ //---------------------------------------------------------------------------
1504
+ // _getNotificationConnectParams()
1505
+ //
1506
+ // Return connect parameters suitable for the dedicated notification channel.
1507
+ //---------------------------------------------------------------------------
1508
+ async _getNotificationConnectParams() {
1509
+ const params = {...this._connectParams};
1510
+ if (params.password === null)
1511
+ params.password = this._password.get();
1512
+ if (params.walletPassword === null)
1513
+ params.walletPassword = this._walletPassword.get();
1514
+ if (params.walletContent === null)
1515
+ params.walletContent = this._walletContent.get();
1516
+ if (params.token === null)
1517
+ params.token = this._token.get();
1518
+ if (params.privateKey === null)
1519
+ params.privateKey = this._privateKey.get();
1520
+ params.connectString = await this._buildNotificationConnectString(
1521
+ this._connectParams.connectString,
1522
+ this._connectParams.configDir);
1523
+ return params;
1524
+ }
1525
+
1275
1526
  //---------------------------------------------------------------------------
1276
1527
  // Binds the values by user to the statement object
1277
1528
  //---------------------------------------------------------------------------
@@ -1363,8 +1614,10 @@ class ThinConnectionImpl extends ConnectionImpl {
1363
1614
  }
1364
1615
  resultSet._resultSetNew(this, statement, options);
1365
1616
  if (statement.queryVars.length > 0) {
1617
+ const previousTypes = statement.queryVars.map(variable => variable.type);
1366
1618
  const metadata = thinUtil.getMetadataMany(statement.queryVars);
1367
1619
  resultSet._setup(options, metadata);
1620
+ this._syncQueryVars(statement, previousTypes);
1368
1621
  }
1369
1622
  return resultSet;
1370
1623
  }
@@ -1385,19 +1638,17 @@ class ThinConnectionImpl extends ConnectionImpl {
1385
1638
  //
1386
1639
  // Parses the SQL statement and returns information about the statement.
1387
1640
  //---------------------------------------------------------------------------
1388
- async getStatementInfo(sql) {
1389
- const options = {};
1641
+ async getStatementInfo(sql, options) {
1390
1642
  const result = {};
1391
1643
  const statement = this._prepare(sql, options);
1392
- options.connection = this;
1393
1644
  try {
1394
1645
  if (!statement.isDdl) {
1395
1646
  const message = new messages.ExecuteMessage(this, statement, options);
1396
1647
  message.parseOnly = true;
1397
1648
  await this._protocol._processMessage(message);
1398
- }
1399
- if (statement.numQueryVars > 0) {
1400
- result.metaData = thinUtil.getMetadataMany(statement.queryVars);
1649
+ if (message.resultSet) {
1650
+ result.metaData = message.resultSet.metaData;
1651
+ }
1401
1652
  }
1402
1653
  result.bindNames = Array.from(statement.bindInfoDict.keys());
1403
1654
  result.statementType = statement.statementType;
@@ -1407,6 +1658,66 @@ class ThinConnectionImpl extends ConnectionImpl {
1407
1658
  }
1408
1659
  }
1409
1660
 
1661
+ //---------------------------------------------------------------------------
1662
+ // subscribe()
1663
+ //
1664
+ // Creates or reuses a thin subscription and optionally registers a query for
1665
+ // DBCHANGE subscriptions.
1666
+ //---------------------------------------------------------------------------
1667
+ async subscribe(inSubscr, options) {
1668
+ if (!options.clientInitiated) {
1669
+ errors.throwNotImplemented("server initiated subscription");
1670
+ }
1671
+
1672
+ let subscr = inSubscr;
1673
+ if (subscr?._listenerStopped) {
1674
+ // Same-name subscribe can recover from an unavailable listener. First
1675
+ // deregister the previous server registration, then create a replacement.
1676
+ try {
1677
+ await subscr.unsubscribe(this);
1678
+ } catch (err) {
1679
+ if (err.code !== errors.getErr(errors.ERR_INVALID_SUBSCR).code) {
1680
+ throw err;
1681
+ }
1682
+ }
1683
+ subscr = null;
1684
+ }
1685
+ if (!subscr) {
1686
+ subscr = new ThinSubscrImpl(this, options);
1687
+ await subscr.subscribe(options);
1688
+ } else {
1689
+ if (!(subscr instanceof ThinSubscrImpl)) {
1690
+ errors.throwErr(errors.ERR_INVALID_SUBSCR);
1691
+ }
1692
+ if (subscr.namespace !== options.namespace) {
1693
+ errors.throwErr(errors.ERR_INVALID_SUBSCR);
1694
+ }
1695
+ if (options.namespace === pubConstants.SUBSCR_NAMESPACE_DBCHANGE) {
1696
+ await subscr.registerQuery(options);
1697
+ }
1698
+ }
1699
+
1700
+ if (options.namespace === pubConstants.SUBSCR_NAMESPACE_DBCHANGE) {
1701
+ return {
1702
+ regId: subscr.regId,
1703
+ subscription: subscr
1704
+ };
1705
+ }
1706
+ return subscr;
1707
+ }
1708
+
1709
+ //---------------------------------------------------------------------------
1710
+ // unsubscribe()
1711
+ //
1712
+ // Unregisters and closes a thin subscription.
1713
+ //---------------------------------------------------------------------------
1714
+ async unsubscribe(subscr) {
1715
+ if (!subscr || !(subscr instanceof ThinSubscrImpl)) {
1716
+ errors.throwErr(errors.ERR_INVALID_SUBSCR);
1717
+ }
1718
+ await subscr.unsubscribe(this);
1719
+ }
1720
+
1410
1721
  async _handleDefines(statement, message) {
1411
1722
  if (statement.requiresDefine && statement.sql) {
1412
1723
  statement.requiresFullExecute = true;
@@ -1501,6 +1812,13 @@ class ThinConnectionImpl extends ConnectionImpl {
1501
1812
  return this.currentSchema;
1502
1813
  }
1503
1814
 
1815
+ getDatabaseOpenTelemetryTracing() {
1816
+ if (!this._protocol.caps.supportsServerOtelTracing) {
1817
+ errors.throwErr(errors.ERR_UNSUPPORTED_DB_OTEL_FEATURE);
1818
+ }
1819
+ return this.databaseOpenTelemetryTracing;
1820
+ }
1821
+
1504
1822
  setClientId(clientId) {
1505
1823
  this._clientIdentifierModified = true;
1506
1824
  this._clientIdentifier = clientId;
@@ -1511,6 +1829,28 @@ class ThinConnectionImpl extends ConnectionImpl {
1511
1829
  this._dbOp = dbOp;
1512
1830
  }
1513
1831
 
1832
+ setDatabaseOpenTelemetryTracing(value) {
1833
+ if (!this._protocol.caps.supportsServerOtelTracing) {
1834
+ errors.throwErr(errors.ERR_UNSUPPORTED_DB_OTEL_FEATURE);
1835
+ }
1836
+ this._otelModified = true;
1837
+ this.databaseOpenTelemetryTracing = value;
1838
+ }
1839
+
1840
+ _clearDatabaseOpenTelemetryTracing() {
1841
+ if (this.databaseOpenTelemetryTracing) {
1842
+ this.databaseOpenTelemetryTracing = false;
1843
+ // the new connection acquired would send tracing
1844
+ // as disabled on server unless application explicitly enables it.
1845
+ this._otelModified = true;
1846
+ }
1847
+ }
1848
+
1849
+ setECID(ecid) {
1850
+ this._ecidModified = true;
1851
+ this._ecid = ecid;
1852
+ }
1853
+
1514
1854
  setExternalName(value) {
1515
1855
  this.externalName = value;
1516
1856
  }
@@ -1540,6 +1880,22 @@ class ThinConnectionImpl extends ConnectionImpl {
1540
1880
  this._action = action;
1541
1881
  }
1542
1882
 
1883
+ // Always queue the requested priority, even if it matches the last known
1884
+ // server value. _txnPriority contains only the last value synchronized from
1885
+ // the server, while _pendingTxnPriority contains only the last value
1886
+ // requested through this method. Neither can reliably account for changes
1887
+ // made through explicit ALTER SESSION.
1888
+ setTxnPriority(priority) {
1889
+ this._protocol.caps.assertTxnPrioritySupported();
1890
+ this._pendingTxnPriority = priority;
1891
+ this._txnPriorityModified = true;
1892
+ }
1893
+
1894
+ getTxnPriority() {
1895
+ this._protocol.caps.assertTxnPrioritySupported();
1896
+ return this._txnPriority;
1897
+ }
1898
+
1543
1899
  async changePassword(user, password, newPassword) {
1544
1900
  const config = {
1545
1901
  user: user,
@@ -1901,15 +2257,15 @@ class ThinConnectionImpl extends ConnectionImpl {
1901
2257
  errors.throwErr(errors.ERR_UNSUPPORTED_DEEP_DATA_SECURITY_FEATURE);
1902
2258
  }
1903
2259
 
1904
- if (this.securityContext && this.securityContext !== context) {
1905
- this.securityContext.clearEncodedPayload();
1906
- }
1907
2260
  this.securityContext = context;
1908
2261
  }
1909
2262
 
1910
2263
  clearEndUserSecurityContext() {
1911
- if (this.securityContext) {
1912
- this.securityContext.clearEncodedPayload();
2264
+ // A provider-created context belongs to this borrowed thin connection.
2265
+ // A pool release invokes close(), so reset resolution before this
2266
+ // physical connection is available to its next borrower.
2267
+ if (this._securityContextInitialized) {
2268
+ this._securityContextInitialized = false;
1913
2269
  }
1914
2270
  this.securityContext = null;
1915
2271
  }
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2023, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2023, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -208,7 +208,8 @@ class ThinDbObjectImpl extends DbObjectImpl {
208
208
  _getPackedData() {
209
209
  if (this.packedData)
210
210
  return this.packedData;
211
- const buf = new DbObjectPickleBuffer();
211
+ const caps = this._objType._connection._protocol.caps;
212
+ const buf = new DbObjectPickleBuffer(undefined, caps);
212
213
  buf.writeHeader(this);
213
214
  this._packData(buf);
214
215
  const size = buf.pos;
@@ -305,7 +306,7 @@ class ThinDbObjectImpl extends DbObjectImpl {
305
306
  }
306
307
  break;
307
308
  default:
308
- errors.throwErr(errors.ERR_NOT_IMPLEMENTED, type);
309
+ errors.throwNotImplemented(type);
309
310
  }
310
311
  }
311
312
  }
@@ -316,7 +317,8 @@ class ThinDbObjectImpl extends DbObjectImpl {
316
317
  // Unpacks the packed data into a map of JavaScript values.
317
318
  //---------------------------------------------------------------------------
318
319
  _unpackData() {
319
- const buf = new DbObjectPickleBuffer(this.packedData);
320
+ const caps = this._objType._connection._protocol.caps;
321
+ const buf = new DbObjectPickleBuffer(this.packedData, caps);
320
322
  buf.readHeader(this);
321
323
  this._unpackDataFromBuf(buf);
322
324
  this.packedData = undefined;
@@ -368,7 +370,7 @@ class ThinDbObjectImpl extends DbObjectImpl {
368
370
  // Unpacks a single value and returns it.
369
371
  //---------------------------------------------------------------------------
370
372
  _unpackValue(buf, type, typeClass) {
371
- let isNull, obj, value, isCollection;
373
+ let isCollection, obj, value;
372
374
  const caps = this._objType._connection._protocol.caps;
373
375
  switch (type) {
374
376
  case types.DB_TYPE_NUMBER:
@@ -396,6 +398,7 @@ class ThinDbObjectImpl extends DbObjectImpl {
396
398
  case types.DB_TYPE_TIMESTAMP:
397
399
  return buf.readOracleDate(true);
398
400
  case types.DB_TYPE_TIMESTAMP_LTZ:
401
+ return buf.readOracleDate(false, true);
399
402
  case types.DB_TYPE_TIMESTAMP_TZ:
400
403
  return buf.readOracleDate(false);
401
404
  case types.DB_TYPE_BOOLEAN:
@@ -403,14 +406,20 @@ class ThinDbObjectImpl extends DbObjectImpl {
403
406
  case types.DB_TYPE_OBJECT:
404
407
  case types.DB_TYPE_XMLTYPE:
405
408
  obj = new ThinDbObjectImpl(typeClass);
409
+
410
+ // check for XMLType
411
+ if (obj._objType.isXmlType) {
412
+ const xmlBytes = buf.readBytesWithLength();
413
+ if (!xmlBytes)
414
+ return null;
415
+ return readXML(obj._objType._connection, xmlBytes);
416
+ }
417
+
418
+ // check the type of object
406
419
  isCollection = obj._objType.isCollection ||
407
420
  this._objType.isCollection;
408
- isNull = buf.getIsAtomicNull(isCollection);
409
- if (isNull)
421
+ if (buf.getIsAtomicNull(isCollection))
410
422
  return null;
411
- if (obj._objType.isXmlType) {
412
- return readXML(obj._objType._connection, buf.readBytesWithLength());
413
- }
414
423
  if (isCollection) {
415
424
  obj.packedData = Buffer.from(buf.readBytesWithLength());
416
425
  } else {
@@ -418,7 +427,7 @@ class ThinDbObjectImpl extends DbObjectImpl {
418
427
  }
419
428
  return obj;
420
429
  default:
421
- errors.throwErr(errors.ERR_NOT_IMPLEMENTED, type);
430
+ errors.throwNotImplemented(type);
422
431
  }
423
432
  }
424
433
 
package/lib/thin/pool.js CHANGED
@@ -41,7 +41,7 @@ class ThinPoolImpl extends PoolImpl {
41
41
 
42
42
  _init(params) {
43
43
  if (!params.homogeneous) {
44
- errors.throwErr(errors.ERR_NOT_IMPLEMENTED, 'Heterogeneous Pooling');
44
+ errors.throwNotImplemented('Heterogeneous Pooling');
45
45
  }
46
46
  if (!params.connectString) {
47
47
  errors.throwErr(errors.ERR_EMPTY_CONNECT_STRING);
@@ -434,7 +434,7 @@ class ThinPoolImpl extends PoolImpl {
434
434
  // continue until a close request is received
435
435
  while (!this._poolCloseWaiter) {
436
436
 
437
- const numToCreate = this._getNumConnsToCreate();
437
+ let numToCreate = this._getNumConnsToCreate();
438
438
  // connection creation is going on serially and not concurrently
439
439
  for (let i = 0; i < numToCreate; i++) {
440
440
  try {
@@ -480,8 +480,9 @@ class ThinPoolImpl extends PoolImpl {
480
480
  break;
481
481
  }
482
482
 
483
+ numToCreate = this._getNumConnsToCreate();
483
484
  // if no pending requests, wait for pending requests to appear!
484
- if ((this._pendingRequests.length == 0 || this._bgErr) &&
485
+ if ((numToCreate <= 0 || this._bgErr) &&
485
486
  this.getConnectionsOpen() >= this._poolMin) {
486
487
  await new Promise((resolve) => {
487
488
  this.bgWaiter = resolve;