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/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.
|
|
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] >=
|
|
45
|
-
errors.ERR_NODE_TOO_OLD, nodbUtil.PACKAGE_JSON_VERSION, "14.
|
|
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
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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
|
-
|
|
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
|
|
721
|
+
// poolMaxPerShard/stmtCacheSize/sodaMetaDataCache/maxLifetimeSession
|
|
722
|
+
// parameters
|
|
712
723
|
await this._impl.reconfigure(options);
|
|
713
724
|
|
|
714
725
|
// pool JS parameters: queueMax, queueTimeout, enableStatistics,
|
package/lib/poolStatistics.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2021,
|
|
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);
|
package/lib/thin/connection.js
CHANGED
|
@@ -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
|
-
|
|
305
|
-
|
|
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
|
-
|
|
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
|
-
|
|
446
|
+
then data_length
|
|
437
447
|
else 0
|
|
438
448
|
end,
|
|
439
|
-
|
|
440
|
-
|
|
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
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
//---------------------------------------------------------------------------
|
package/lib/thin/dbObject.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 (
|
|
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,
|
|
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
|
-
|
|
225
|
+
destLength: constants.TNS_DURATION_SESSION
|
|
227
226
|
};
|
|
228
227
|
await this._sendMessage(options);
|
|
229
228
|
await this._getChunkSizeAsync();
|