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.
Files changed (67) hide show
  1. package/README.md +39 -3
  2. package/build/Release/oracledb-6.9.0-darwin-arm64.node +0 -0
  3. package/build/Release/oracledb-6.9.0-darwin-arm64.node-buildinfo.txt +1 -0
  4. package/build/Release/oracledb-6.9.0-darwin-x64.node +0 -0
  5. package/build/Release/oracledb-6.9.0-darwin-x64.node-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-6.9.0-js-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-6.9.0-linux-arm64.node +0 -0
  8. package/build/Release/oracledb-6.9.0-linux-arm64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-6.9.0-linux-x64.node +0 -0
  10. package/build/Release/oracledb-6.9.0-linux-x64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-6.9.0-win32-x64.node +0 -0
  12. package/build/Release/oracledb-6.9.0-win32-x64.node-buildinfo.txt +1 -0
  13. package/lib/aqMessage.js +10 -1
  14. package/lib/configProviders/file.js +72 -0
  15. package/lib/connection.js +87 -2
  16. package/lib/dbObject.js +9 -0
  17. package/lib/errors.js +56 -31
  18. package/lib/impl/connection.js +9 -0
  19. package/lib/impl/pool.js +10 -1
  20. package/lib/impl/resultset.js +5 -4
  21. package/lib/lob.js +9 -1
  22. package/lib/oracledb.js +129 -16
  23. package/lib/pool.js +12 -1
  24. package/lib/poolStatistics.js +3 -1
  25. package/lib/thin/connection.js +117 -10
  26. package/lib/thin/dbObject.js +8 -6
  27. package/lib/thin/lob.js +2 -3
  28. package/lib/thin/pool.js +105 -28
  29. package/lib/thin/protocol/capabilities.js +7 -3
  30. package/lib/thin/protocol/constants.js +29 -1
  31. package/lib/thin/protocol/messages/auth.js +13 -0
  32. package/lib/thin/protocol/messages/base.js +98 -9
  33. package/lib/thin/protocol/messages/dataType.js +2 -1
  34. package/lib/thin/protocol/messages/execute.js +46 -4
  35. package/lib/thin/protocol/messages/lobOp.js +5 -22
  36. package/lib/thin/protocol/messages/logOff.js +0 -1
  37. package/lib/thin/protocol/messages/sessionRelease.js +0 -1
  38. package/lib/thin/protocol/messages/transactionSwitch.js +12 -4
  39. package/lib/thin/protocol/protocol.js +2 -1
  40. package/lib/thin/sqlnet/connStrategy.js +2 -1
  41. package/lib/thin/sqlnet/navNodes.js +1 -1
  42. package/lib/thin/sqlnet/networkSession.js +9 -3
  43. package/lib/thin/sqlnet/packet.js +2 -1
  44. package/lib/thin/sqlnet/sessionAtts.js +2 -3
  45. package/lib/types.js +0 -5
  46. package/lib/util.js +76 -2
  47. package/lib/version.js +1 -1
  48. package/package/install.js +3 -3
  49. package/package/prunebinaries.js +6 -12
  50. package/package.json +2 -2
  51. package/{lib/configProviders/azure.js → plugins/configProviders/azure/index.js} +70 -63
  52. package/plugins/configProviders/azurevault/index.js +84 -0
  53. package/{lib → plugins}/configProviders/base.js +13 -3
  54. package/{lib/configProviders/ociobject.js → plugins/configProviders/ociobject/index.js} +43 -78
  55. package/plugins/configProviders/ocivault/index.js +100 -0
  56. package/plugins/token/extensionOci/index.js +71 -35
  57. package/build/Release/oracledb-6.8.0-darwin-arm64.node +0 -0
  58. package/build/Release/oracledb-6.8.0-darwin-arm64.node-buildinfo.txt +0 -1
  59. package/build/Release/oracledb-6.8.0-darwin-x64.node +0 -0
  60. package/build/Release/oracledb-6.8.0-darwin-x64.node-buildinfo.txt +0 -1
  61. package/build/Release/oracledb-6.8.0-js-buildinfo.txt +0 -1
  62. package/build/Release/oracledb-6.8.0-linux-arm64.node +0 -0
  63. package/build/Release/oracledb-6.8.0-linux-arm64.node-buildinfo.txt +0 -1
  64. package/build/Release/oracledb-6.8.0-linux-x64.node +0 -0
  65. package/build/Release/oracledb-6.8.0-linux-x64.node-buildinfo.txt +0 -1
  66. package/build/Release/oracledb-6.8.0-win32-x64.node +0 -0
  67. package/build/Release/oracledb-6.8.0-win32-x64.node-buildinfo.txt +0 -1
package/lib/oracledb.js CHANGED
@@ -34,15 +34,15 @@ const impl = require('./impl');
34
34
  const process = require('process');
35
35
  const util = require('util');
36
36
 
37
- // This version of node-oracledb works with Node.js 14.6 or later.
37
+ // This version of node-oracledb works with Node.js 14.17 or later.
38
38
  // Note: the checked version is the minimum required for Node-API
39
39
  // compatibility. When new Node.js versions are released, older Node.js
40
40
  // versions are dropped from the node-oracledb test plan.
41
41
  //
42
42
  // Keep this code in sync with package/install.js
43
43
  const vs = process.version.substring(1).split(".").map(Number);
44
- errors.assert(vs[0] > 14 || (vs[0] === 14 && vs[1] >= 6),
45
- errors.ERR_NODE_TOO_OLD, nodbUtil.PACKAGE_JSON_VERSION, "14.6");
44
+ errors.assert(vs[0] > 14 || (vs[0] === 14 && vs[1] >= 17),
45
+ errors.ERR_NODE_TOO_OLD, nodbUtil.PACKAGE_JSON_VERSION, "14.17");
46
46
 
47
47
  const AqDeqOptions = require('./aqDeqOptions.js');
48
48
  const AqEnqOptions = require('./aqEnqOptions.js');
@@ -68,6 +68,7 @@ const poolCache = {};
68
68
  const tempUsedPoolAliases = {};
69
69
  const defaultPoolAlias = 'default';
70
70
  const registeredHooks = [];
71
+ const registeredConfigProviderHooks = new Map();
71
72
  let configProviderCache;
72
73
 
73
74
  // save arguments for call to initOracleClient()
@@ -382,6 +383,15 @@ async function _verifyOptions(options, inCreatePool) {
382
383
  outOptions.useSNI = options.useSNI;
383
384
  }
384
385
 
386
+ // appContext must be an array of array values. The element arrays should
387
+ // have 3 string values (namespace, name and value).
388
+ if (options.appContext !== undefined) {
389
+ const value = options.appContext;
390
+ errors.assertParamPropValue(nodbUtil.isAppContext(value), 1,
391
+ "appContext");
392
+ outOptions.appContext = options.appContext;
393
+ }
394
+
385
395
  // check pool specific options
386
396
  if (inCreatePool) {
387
397
 
@@ -435,6 +445,13 @@ async function _verifyOptions(options, inCreatePool) {
435
445
  outOptions.poolPingTimeout = options.poolPingTimeout;
436
446
  }
437
447
 
448
+ // maxLifeTime must be an unsigned integer
449
+ if (options.maxLifetimeSession !== undefined) {
450
+ errors.assertParamPropValue(Number.isInteger(options.maxLifetimeSession) &&
451
+ options.maxLifetimeSession >= 0, 1, "maxLifetimeSession");
452
+ outOptions.maxLifetimeSession = options.maxLifetimeSession;
453
+ }
454
+
438
455
  // homogeneous must be a boolean (and defaults to True)
439
456
  outOptions.homogeneous = true;
440
457
  if (options.homogeneous !== undefined) {
@@ -888,6 +905,84 @@ async function shutdown(a1, a2) {
888
905
  await conn.close();
889
906
  }
890
907
 
908
+ async function _setConfigParameters(obj, credential, configProvider) {
909
+ const configObject = {};
910
+ const pmSection = 'njs';
911
+ const params = obj[pmSection];
912
+ for (const key in params) {
913
+ const val = params[key];
914
+ configObject[key] = val;
915
+ }
916
+ configObject.connectString = obj.connect_descriptor;
917
+ if (!configObject.connectString)
918
+ errors.throwErr(errors.ERR_CONFIG_PROVIDER_FAILED_TO_RETRIEVE_CONFIG, 'connect_descriptor must be set');
919
+
920
+ configObject.user = obj.user;
921
+ if (obj.password) {
922
+ configObject.password = await _retrieveParamValueFromVault(obj['password'], credential, configProvider);
923
+ }
924
+ if (obj.wallet_location) {
925
+ // retrieve wallet_location
926
+ configObject.walletContent = await _retrieveParamValueFromVault(obj['wallet_location'], credential, configProvider);
927
+ //only Pem file supported
928
+ if (!nodbUtil.isPemFile(configObject.walletContent))
929
+ errors.throwErr(errors.ERR_WALLET_TYPE_NOT_SUPPORTED);
930
+ }
931
+ return configObject;
932
+ }
933
+
934
+ async function _retrieveParamValueFromVault(paramObj, credential, configProvider) {
935
+ const paramMap = new Map();
936
+ const auth = paramObj.authentication;
937
+ if (auth) {
938
+ for (const key in auth) {
939
+ if (key == 'method')
940
+ paramMap.set('authentication', auth[key]);
941
+ const val = auth[key];
942
+ paramMap.set(key.toLowerCase(), val);
943
+ }
944
+ }
945
+ const args = {};
946
+ if (paramObj.type == "base64") {
947
+ console.log("WARNING: Base64 Encoding in a JSON Password should only be used in development environments");
948
+ return Buffer.from(paramObj.value, "base64").toString("utf-8");
949
+ } else if (paramObj.type == "text") {
950
+ if (configProvider == 'azurevault' || configProvider == 'ocivault') {
951
+ console.log("WARNING: Plain Text in a JSON Password should only be used in development environments");
952
+ return paramObj.value;
953
+ } else
954
+ errors.throwErr(errors.ERR_CONFIG_PROVIDER_PARAM_TYPE, 'password type text is only allowed in ocivault and azurevault');
955
+ } else if (paramObj.type == "azurevault") {
956
+ paramMap.set('azuresecreturl', paramObj.value);
957
+ if (!(configProvider == 'azure' || configProvider == 'azurevault'))
958
+ credential = null;
959
+ const hookFn = registeredConfigProviderHooks['azurevault'];
960
+ if (hookFn == undefined)
961
+ errors.throwErr(errors.ERR_REGISTER_HOOKFN_CONFIGPROVIDER, 'azurevault');
962
+ args.credential = credential;
963
+ args.paramMap = paramMap;
964
+ const vaultReturn = await hookFn(args);
965
+ const paramValue = vaultReturn[0];
966
+ return paramValue;
967
+
968
+ } else if (paramObj.type == "ocivault") {
969
+
970
+ paramMap.set('ocidvault', paramObj.value);
971
+ if (!(configProvider == 'ociobject' || configProvider == 'ocivault'))
972
+ credential = null;
973
+ const hookFn = registeredConfigProviderHooks['ocivault'];
974
+ if (hookFn == undefined)
975
+ errors.throwErr(errors.ERR_REGISTER_HOOKFN_CONFIGPROVIDER, 'ocivault');
976
+ const args = {};
977
+ args.credential = credential;
978
+ args.paramMap = paramMap;
979
+ const vaultReturn = await hookFn(args);
980
+ const paramValue = vaultReturn[0];
981
+ return paramValue;
982
+ } else {
983
+ errors.throwErr(errors.ERR_CONFIG_PROVIDER_PARAM_TYPE, 'password/wallet_location');
984
+ }
985
+ }
891
986
  //-----------------------------------------------------------------------------
892
987
  // _checkConfigProvider()
893
988
  //
@@ -928,23 +1023,27 @@ async function _checkConfigProvider(options) {
928
1023
  if (match) {
929
1024
  const provider = match.groups.provider;
930
1025
  const provider_arg = match.groups.provider_arg;
931
- let configPckg;
932
- try {
933
- configPckg = require('./configProviders/' + provider);
934
- } catch (err) {
935
- errors.throwErr(errors.ERR_CONFIG_PROVIDER_NOT_SUPPORTED, provider);
936
- }
937
- const configProvider = new configPckg(provider_arg, urlExtendedPart);
938
- try {
939
- configProvider.init();
940
- } catch (err) {
941
- errors.throwErr(errors.ERR_CONFIG_PROVIDER_LOAD_FAILED, err.message);
942
- }
1026
+ const hookFn = registeredConfigProviderHooks[provider];
1027
+ if (hookFn == undefined)
1028
+ errors.throwErr(errors.ERR_REGISTER_HOOKFN_CONFIGPROVIDER, provider);
1029
+ const args = {};
1030
+ args.provider_arg = provider_arg;
1031
+ args.urlExtendedPart = urlExtendedPart;
1032
+
943
1033
  try {
1034
+ // hookFn returns an array with first element as the config stored in the configProvider
1035
+ // second being the credential to the configProvider
1036
+ const configProviderReturn = await hookFn(args);
1037
+ secondOpts = configProviderReturn[0];
944
1038
  if (!configProviderCache) {
945
1039
  configProviderCache = new Map();
946
1040
  }
947
- secondOpts = await configProvider.returnConfig();
1041
+ if (!secondOpts) {
1042
+ errors.throwErr(errors.ERR_CONFIG_PROVIDER_FAILED_TO_RETRIEVE_CONFIG, 'no configuration found in ' + provider);
1043
+ }
1044
+ if (provider != 'azure')
1045
+ secondOpts = await _setConfigParameters(secondOpts, configProviderReturn[1], provider);
1046
+
948
1047
  // obfuscate password & walletcontent and store config in the cache
949
1048
  const cacheOpts = { ...secondOpts};
950
1049
  if (secondOpts.password)
@@ -1057,6 +1156,19 @@ function registerProcessConfigurationHook(fn) {
1057
1156
  registeredHooks.push(fn);
1058
1157
  }
1059
1158
 
1159
+ //-----------------------------------------------------------------------------
1160
+ // registerConfigurationProviderHook()
1161
+ //
1162
+ // Registers Configuration Provider plugin modules and registered modules will
1163
+ // be called and executed during pool and standalone connection creation.
1164
+ //-----------------------------------------------------------------------------
1165
+ function registerConfigurationProviderHook(configProvider, fn) {
1166
+ errors.assertArgCount(arguments, 2, 2);
1167
+ errors.assertParamValue(typeof fn === 'function', 1);
1168
+ errors.assertParamValue(typeof configProvider === 'string', 1);
1169
+ registeredConfigProviderHooks[configProvider] = fn;
1170
+ }
1171
+
1060
1172
  // module exports
1061
1173
  module.exports = {
1062
1174
 
@@ -1088,6 +1200,7 @@ module.exports = {
1088
1200
  getPool,
1089
1201
  initOracleClient,
1090
1202
  registerProcessConfigurationHook,
1203
+ registerConfigurationProviderHook,
1091
1204
  shutdown: nodbUtil.callbackify(nodbUtil.wrapFn(shutdown)),
1092
1205
  startup: nodbUtil.callbackify(nodbUtil.wrapFn(startup)),
1093
1206
 
package/lib/pool.js CHANGED
@@ -653,6 +653,15 @@ class Pool extends EventEmitter {
653
653
  return this._impl.getPoolTimeout();
654
654
  }
655
655
 
656
+ //---------------------------------------------------------------------------
657
+ // maxLifetimeSession
658
+ //
659
+ // Property for the max allowed lifetime of a session in the pool.
660
+ //---------------------------------------------------------------------------
661
+ get maxLifetimeSession() {
662
+ return this._impl.getMaxLifetimeSession();
663
+ }
664
+
656
665
  //---------------------------------------------------------------------------
657
666
  // queueMax
658
667
  //
@@ -693,6 +702,7 @@ class Pool extends EventEmitter {
693
702
  errors.assertParamPropUnsignedInt(options, 1, "poolIncrement");
694
703
  errors.assertParamPropInt(options, 1, "poolPingInterval");
695
704
  errors.assertParamPropUnsignedInt(options, 1, "poolTimeout");
705
+ errors.assertParamPropUnsignedInt(options, 1, "maxLifetimeSession");
696
706
  errors.assertParamPropUnsignedInt(options, 1, "stmtCacheSize");
697
707
  errors.assertParamPropBool(options, 1, "sodaMetaDataCache");
698
708
 
@@ -708,7 +718,8 @@ class Pool extends EventEmitter {
708
718
  this._status = constants.POOL_STATUS_RECONFIGURING;
709
719
  try {
710
720
  // poolMin/poolMax/poolIncrement/poolPingInterval/poolTimeout/
711
- // poolMaxPerShard/stmtCacheSize/sodaMetaDataCache parameters
721
+ // poolMaxPerShard/stmtCacheSize/sodaMetaDataCache/maxLifetimeSession
722
+ // parameters
712
723
  await this._impl.reconfigure(options);
713
724
 
714
725
  // pool JS parameters: queueMax, queueTimeout, enableStatistics,
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2021, 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2021, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -74,6 +74,7 @@ class PoolStatistics {
74
74
  this.poolPingInterval = pool.poolPingInterval;
75
75
  this.poolPingTimeout = pool.poolPingTimeout;
76
76
  this.poolTimeout = pool.poolTimeout;
77
+ this.maxLifetimeSession = pool.maxLifetimeSession;
77
78
  this.queueMax = pool.queueMax;
78
79
  this.queueTimeout = pool.queueTimeout;
79
80
  this.sodaMetaDataCache = pool.sodaMetaDataCache;
@@ -127,6 +128,7 @@ class PoolStatistics {
127
128
  console.log('...poolPingInterval (seconds):', this.poolPingInterval);
128
129
  console.log('...poolPingTimeout (milliseconds):', this.poolPingTimeout);
129
130
  console.log('...poolTimeout (seconds):', this.poolTimeout);
131
+ console.log('...maxLifetimeSession (seconds):', this.maxLifetimeSession);
130
132
  console.log('...queueMax:', this.queueMax);
131
133
  console.log('...queueTimeout (milliseconds):', this.queueTimeout);
132
134
  console.log('...sessionCallback:', this.sessionCallback);
@@ -85,6 +85,9 @@ class ThinConnectionImpl extends ConnectionImpl {
85
85
  // immediate close of open socket on failure
86
86
  // exception won't be thrown to user
87
87
  this.nscon.disconnect(sqlNetConstants.NSFIMM);
88
+ // If connection is associated with a pool, we release it
89
+ if (this._pool)
90
+ await this._pool.release(this);
88
91
  }
89
92
  }
90
93
 
@@ -299,13 +302,20 @@ class ThinConnectionImpl extends ConnectionImpl {
299
302
  }
300
303
 
301
304
  // process the type code.
305
+ let tempPrecision, tempScale;
302
306
  switch (attrType) {
303
307
  case constants.TNS_OBJ_TDS_TYPE_NUMBER:
304
- metaData.precision = buf.readInt8();
305
- metaData.scale = buf.readInt8();
308
+ tempPrecision = buf.readInt8();
309
+ tempScale = buf.readInt8();
310
+ if (tempPrecision || tempScale) {
311
+ metaData.precision = tempPrecision;
312
+ metaData.scale = tempScale;
313
+ }
306
314
  return types.DB_TYPE_NUMBER;
307
315
  case constants.TNS_OBJ_TDS_TYPE_FLOAT:
308
- metaData.precision = buf.readInt8();
316
+ tempPrecision = buf.readInt8();
317
+ if (tempPrecision)
318
+ metaData.precision = tempPrecision;
309
319
  return types.DB_TYPE_NUMBER;
310
320
  case constants.TNS_OBJ_TDS_TYPE_VARCHAR:
311
321
  case constants.TNS_OBJ_TDS_TYPE_CHAR:
@@ -433,11 +443,24 @@ class ThinConnectionImpl extends ConnectionImpl {
433
443
  case
434
444
  when data_type in
435
445
  ('CHAR', 'NCHAR', 'VARCHAR2', 'NVARCHAR2', 'RAW')
436
- then data_length
446
+ then data_length
437
447
  else 0
438
448
  end,
439
- nvl(data_precision, 0),
440
- nvl(data_scale, 0)
449
+ case
450
+ when data_precision is null and data_scale is null
451
+ then 0
452
+ when data_precision is null
453
+ then 38
454
+ else data_precision
455
+ end,
456
+ case
457
+ when data_precision is null and data_scale is null
458
+ and data_type = 'NUMBER'
459
+ then -127
460
+ when data_scale is null
461
+ then 0
462
+ else data_scale
463
+ end
441
464
  from all_tab_cols
442
465
  where owner = :owner
443
466
  and table_name = :name
@@ -670,10 +693,36 @@ class ThinConnectionImpl extends ConnectionImpl {
670
693
  this._partialDbObjectTypes.push(attr.typeClass);
671
694
  }
672
695
  } else {
673
- attr.type = types.getTypeByColumnTypeName(attrInfo.dataType);
674
- attr.maxSize = attrInfo.maxSize;
675
- attr.precision = attrInfo.dataPrecision;
676
- attr.scale = attrInfo.dataScale;
696
+ if (attrInfo.dataType === 'INTEGER' || attrInfo.dataType === 'SMALLINT') {
697
+ attr.type = types.DB_TYPE_NUMBER;
698
+ attr.precision = 38;
699
+ attr.scale = 0;
700
+ } else if (attrInfo.dataType === 'REAL') {
701
+ attr.type = types.DB_TYPE_NUMBER;
702
+ attr.precision = 63;
703
+ attr.scale = -127;
704
+ } else if (attrInfo.dataType === 'DOUBLE PRECISION' || attrInfo.dataType === 'FLOAT') {
705
+ attr.type = types.DB_TYPE_NUMBER;
706
+ // the database sends type name "FLOAT" instead of type name "REAL"
707
+ // when looking at table metadata but not when examining database
708
+ // object attribute metadata so account for that here
709
+ if (attrInfo.dataPrecision != null || attrInfo.dataPrecision != undefined)
710
+ attr.precision = Number(attrInfo.dataPrecision);
711
+ else
712
+ attr.precision = 126;
713
+ attr.scale = -127;
714
+ } else {
715
+ attr.type = types.getTypeByColumnTypeName(attrInfo.dataType);
716
+ if (attrInfo.maxSize != null || attrInfo.maxSize != undefined) {
717
+ const tempMaxSize = Number(attrInfo.maxSize);
718
+ if (tempMaxSize)
719
+ attr.maxSize = tempMaxSize;
720
+ }
721
+ if (attr.type === types.DB_TYPE_NUMBER) {
722
+ attr.precision = Number(attrInfo.dataPrecision);
723
+ attr.scale = Number(attrInfo.dataScale);
724
+ }
725
+ }
677
726
  }
678
727
  attributes.push(attr);
679
728
  }
@@ -772,6 +821,7 @@ class ThinConnectionImpl extends ConnectionImpl {
772
821
  this.remoteAddress = '';
773
822
  this.comboKey = null; // used in changePassword API
774
823
  this.tpcContext = null;
824
+ this._sessionlessData = null;
775
825
 
776
826
  this.nscon = new nsi();
777
827
  finalizationRegistry.register(this, this.nscon);
@@ -1233,6 +1283,55 @@ class ThinConnectionImpl extends ConnectionImpl {
1233
1283
  errors.throwErr(errors.ERR_UNKNOWN_TRANSACTION_STATE, message.state);
1234
1284
  }
1235
1285
  }
1286
+ //---------------------------------------------------------------------------
1287
+ // Ensure no sessionless transaction was started through server procedure
1288
+ //---------------------------------------------------------------------------
1289
+ _validateSessionlessState() {
1290
+ if (this._sessionlessData?.startedOnServer) {
1291
+ errors.throwErr(errors.ERR_SESSIONLESS_DIFFERING_METHODS);
1292
+ }
1293
+ }
1294
+
1295
+ //---------------------------------------------------------------------------
1296
+ // Begin/Resume a sessionless transaction with provided transactionId
1297
+ //---------------------------------------------------------------------------
1298
+ async startSessionlessTransaction(transactionId, timeout, flags,
1299
+ deferRoundTrip) {
1300
+ this._validateSessionlessState();
1301
+ if (this._sessionlessData)
1302
+ errors.throwErr(errors.ERR_SESSIONLESS_ALREADY_ACTIVE);
1303
+ const message = new messages.TransactionSwitchMessage(this);
1304
+ message.xid = {
1305
+ globalTransactionId: transactionId,
1306
+ branchQualifier: "",
1307
+ formatId: constants.TNS_TPC_TRANS_SESSIONLESS_FORMAT
1308
+ };
1309
+ message.timeout = timeout;
1310
+ message.operation = constants.TNS_TPC_TXN_START;
1311
+ message.flags = constants.TNS_TPC_TRANS_SESSIONLESS | flags;
1312
+ if (deferRoundTrip) {
1313
+ message.messageType = constants.TNS_MSG_TYPE_PIGGYBACK;
1314
+ this._sessionlessData = {
1315
+ piggyback: message,
1316
+ pending: true
1317
+ };
1318
+ } else {
1319
+ await this._protocol._processMessage(message);
1320
+ }
1321
+ }
1322
+
1323
+ //---------------------------------------------------------------------------
1324
+ // Suspend the active sessionless transaction
1325
+ //---------------------------------------------------------------------------
1326
+ async suspendSessionlessTransaction() {
1327
+ this._validateSessionlessState();
1328
+ if (!this._sessionlessData)
1329
+ errors.throwErr(errors.ERR_SESSIONLESS_INACTIVE);
1330
+ const message = new messages.TransactionSwitchMessage(this);
1331
+ message.operation = constants.TNS_TPC_TXN_DETACH;
1332
+ message.flags = constants.TNS_TPC_TRANS_SESSIONLESS;
1333
+ await this._protocol._processMessage(message);
1334
+ }
1236
1335
 
1237
1336
 
1238
1337
  //---------------------------------------------------------------------------
@@ -1273,6 +1372,14 @@ class ThinConnectionImpl extends ConnectionImpl {
1273
1372
  return this.internalName;
1274
1373
  }
1275
1374
 
1375
+ //---------------------------------------------------------------------------
1376
+ // Returns the Logical Transaction ID (ltxid) associated with the connection.
1377
+ // Used with Oracle Database Transaction Guard feature.
1378
+ //---------------------------------------------------------------------------
1379
+ getLTXID() {
1380
+ return this._ltxid;
1381
+ }
1382
+
1276
1383
  //---------------------------------------------------------------------------
1277
1384
  // Returns the Oracle Database domain name associated with the connection.
1278
1385
  //---------------------------------------------------------------------------
@@ -55,10 +55,10 @@ class DbObjectPickleBuffer extends GrowableBuffer {
55
55
  // Reads the next byte and checks to see if the value is atomically null. If
56
56
  // not, the byte is returned to the buffer for further processing.
57
57
  //---------------------------------------------------------------------------
58
- getIsAtomicNull() {
58
+ getIsAtomicNull(isCollection) {
59
59
  const value = this.readUInt8();
60
60
  if (value === constants.TNS_OBJ_ATOMIC_NULL ||
61
- value === constants.TNS_NULL_LENGTH_INDICATOR) {
61
+ (isCollection && value === constants.TNS_NULL_LENGTH_INDICATOR)) {
62
62
  return true;
63
63
  }
64
64
  this.pos -= 1;
@@ -365,7 +365,7 @@ class ThinDbObjectImpl extends DbObjectImpl {
365
365
  // Unpacks a single value and returns it.
366
366
  //---------------------------------------------------------------------------
367
367
  _unpackValue(buf, type, typeClass) {
368
- let isNull, obj, value;
368
+ let isNull, obj, value, isCollection;
369
369
  switch (type) {
370
370
  case types.DB_TYPE_NUMBER:
371
371
  return buf.readOracleNumber();
@@ -396,14 +396,16 @@ class ThinDbObjectImpl extends DbObjectImpl {
396
396
  return buf.readBool();
397
397
  case types.DB_TYPE_OBJECT:
398
398
  case types.DB_TYPE_XMLTYPE:
399
- isNull = buf.getIsAtomicNull();
399
+ obj = new ThinDbObjectImpl(typeClass);
400
+ isCollection = obj._objType.isCollection ||
401
+ this._objType.isCollection;
402
+ isNull = buf.getIsAtomicNull(isCollection);
400
403
  if (isNull)
401
404
  return null;
402
- obj = new ThinDbObjectImpl(typeClass);
403
405
  if (obj._objType.isXmlType) {
404
406
  return readXML(obj._objType._connection, buf.readBytesWithLength());
405
407
  }
406
- if (obj._objType.isCollection || this._objType.isCollection) {
408
+ if (isCollection) {
407
409
  obj.packedData = Buffer.from(buf.readBytesWithLength());
408
410
  } else {
409
411
  obj._unpackDataFromBuf(buf);
package/lib/thin/lob.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -220,10 +220,9 @@ class ThinLobImpl extends LobImpl {
220
220
  const options = {
221
221
  operation: constants.TNS_LOB_OP_CREATE_TEMP,
222
222
  sourceLobImpl: this,
223
- amount: constants.TNS_DURATION_SESSION,
224
223
  destOffset: dbType._oraTypeNum,
225
224
  sourceOffset: dbType._csfrm,
226
- sendAmount: true
225
+ destLength: constants.TNS_DURATION_SESSION
227
226
  };
228
227
  await this._sendMessage(options);
229
228
  await this._getChunkSizeAsync();