oracledb 6.10.0 → 7.0.1
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/LICENSE.txt +1 -1
- package/NOTICE.txt +1 -1
- package/README.md +9 -9
- package/build/Release/oracledb-7.0.1-darwin-arm64.node +0 -0
- package/build/Release/oracledb-7.0.1-darwin-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-7.0.1-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-7.0.1-linux-arm64.node +0 -0
- package/build/Release/oracledb-7.0.1-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-7.0.1-linux-x64.node +0 -0
- package/build/Release/oracledb-7.0.1-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-7.0.1-win32-x64.node +0 -0
- package/build/Release/oracledb-7.0.1-win32-x64.node-buildinfo.txt +1 -0
- package/lib/aqDeqOptions.js +4 -4
- package/lib/aqEnqOptions.js +2 -2
- package/lib/aqQueue.js +11 -6
- package/lib/connection.js +407 -40
- package/lib/endUserSecurityContext.js +169 -0
- package/lib/errors.js +108 -5
- package/lib/impl/connection.js +76 -2
- package/lib/impl/datahandlers/buffer.js +19 -5
- package/lib/impl/datahandlers/vector.js +25 -5
- package/lib/impl/lob.js +9 -0
- package/lib/impl/parserHelpers.js +447 -0
- package/lib/impl/resultset.js +1 -1
- package/lib/lob.js +27 -14
- package/lib/obfuscation.js +97 -0
- package/lib/oracledb.js +148 -43
- package/lib/parser.js +204 -0
- package/lib/pipeline.js +113 -0
- package/lib/pool.js +16 -1
- package/lib/resultset.js +42 -21
- package/lib/settings.js +1 -0
- package/lib/thin/connection.js +461 -53
- package/lib/thin/dbObject.js +10 -4
- package/lib/thin/lob.js +20 -1
- package/lib/thin/pool.js +43 -63
- package/lib/thin/protocol/capabilities.js +18 -3
- package/lib/thin/protocol/constants.js +82 -2
- package/lib/thin/protocol/encryptDecrypt.js +43 -8
- package/lib/thin/protocol/messages/aqArray.js +2 -2
- package/lib/thin/protocol/messages/aqBase.js +2 -2
- package/lib/thin/protocol/messages/auth.js +6 -1
- package/lib/thin/protocol/messages/base.js +246 -5
- package/lib/thin/protocol/messages/dataType.js +8 -3
- package/lib/thin/protocol/messages/directPathLoadStream.js +377 -0
- package/lib/thin/protocol/messages/directPathOp.js +65 -0
- package/lib/thin/protocol/messages/directPathPrepare.js +158 -0
- package/lib/thin/protocol/messages/endPipeline.js +56 -0
- package/lib/thin/protocol/messages/execute.js +9 -7
- package/lib/thin/protocol/messages/index.js +9 -1
- package/lib/thin/protocol/messages/protocol.js +2 -2
- package/lib/thin/protocol/messages/withData.js +10 -143
- package/lib/thin/protocol/packet.js +115 -24
- package/lib/thin/protocol/protocol.js +110 -25
- package/lib/thin/protocol/utils.js +1 -26
- package/lib/thin/resultSet.js +13 -19
- package/lib/thin/sqlnet/ezConnectResolver.js +4 -4
- package/lib/thin/sqlnet/navNodes.js +4 -2
- package/lib/thin/sqlnet/networkSession.js +78 -50
- package/lib/thin/sqlnet/ntTcp.js +24 -0
- package/lib/thin/sqlnet/nvStrToNvPair.js +50 -25
- package/lib/thin/sqlnet/packet.js +49 -8
- package/lib/thin/sqlnet/paramParser.js +10 -7
- package/lib/thin/sqlnet/sessionAtts.js +2 -2
- package/lib/thin/statementCache.js +2 -4
- package/lib/traceHandler.js +59 -2
- package/lib/transformer.js +8 -3
- package/lib/types.js +35 -21
- package/lib/util.js +6 -6
- package/lib/version.js +4 -4
- package/package/install.js +5 -5
- package/package/prunebinaries.js +1 -1
- package/package.json +1 -1
- package/plugins/configProviders/awsCommon.js +125 -0
- package/plugins/configProviders/awss3/index.js +138 -0
- package/plugins/configProviders/awssecretsmanager/index.js +147 -0
- package/plugins/configProviders/azure/index.js +4 -5
- package/plugins/token/extensionAzure/index.js +0 -1
- package/plugins/token/extensionOci/index.js +0 -1
- package/build/Release/oracledb-6.10.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-6.10.0-darwin-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.10.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.10.0-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.10.0-js-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.10.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.10.0-linux-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.10.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.10.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.10.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.10.0-win32-x64.node-buildinfo.txt +0 -1
package/lib/oracledb.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2015,
|
|
1
|
+
// Copyright (c) 2015, 2026, Oracle and/or its affiliates.
|
|
2
2
|
|
|
3
3
|
//-----------------------------------------------------------------------------
|
|
4
4
|
//
|
|
@@ -28,11 +28,14 @@
|
|
|
28
28
|
|
|
29
29
|
const constants = require('./constants.js');
|
|
30
30
|
const nodbUtil = require('./util.js');
|
|
31
|
+
const { ObfuscatedValue } = require('./obfuscation.js');
|
|
31
32
|
const errors = require('./errors.js');
|
|
32
33
|
const types = require('./types.js');
|
|
33
34
|
const impl = require('./impl');
|
|
34
35
|
const process = require('process');
|
|
35
36
|
const util = require('util');
|
|
37
|
+
const parser = require('./parser.js');
|
|
38
|
+
const parserHelpers = require('./impl/parserHelpers.js');
|
|
36
39
|
|
|
37
40
|
// This version of node-oracledb works with Node.js 14.17 or later.
|
|
38
41
|
// Note: the checked version is the minimum required for Node-API
|
|
@@ -47,7 +50,6 @@ errors.assert(vs[0] > 14 || (vs[0] === 14 && vs[1] >= 17),
|
|
|
47
50
|
const AqDeqOptions = require('./aqDeqOptions.js');
|
|
48
51
|
const AqEnqOptions = require('./aqEnqOptions.js');
|
|
49
52
|
const AqMessage = require('./aqMessage.js');
|
|
50
|
-
const protocolUtil = require('./thin/protocol/utils.js');
|
|
51
53
|
const AqQueue = require('./aqQueue.js');
|
|
52
54
|
const future = require('./future.js');
|
|
53
55
|
const traceHandler = require('./traceHandler.js');
|
|
@@ -57,12 +59,14 @@ const Lob = require('./lob.js');
|
|
|
57
59
|
const Pool = require('./pool.js');
|
|
58
60
|
const PoolStatistics = require('./poolStatistics.js');
|
|
59
61
|
const ResultSet = require('./resultset.js');
|
|
62
|
+
const Pipeline = require('./pipeline.js');
|
|
60
63
|
const settings = require('./settings.js');
|
|
61
64
|
const SodaDatabase = require('./sodaDatabase.js');
|
|
62
65
|
const SodaCollection = require('./sodaCollection.js');
|
|
63
66
|
const SodaDocCursor = require('./sodaDocCursor.js');
|
|
64
67
|
const SodaDocument = require('./sodaDocument.js');
|
|
65
68
|
const SodaOperation = require('./sodaOperation.js');
|
|
69
|
+
const EndUserSecurityContext = require('./endUserSecurityContext.js');
|
|
66
70
|
|
|
67
71
|
const poolCache = {};
|
|
68
72
|
const tempUsedPoolAliases = {};
|
|
@@ -131,6 +135,27 @@ function _initializeThinDriver() {
|
|
|
131
135
|
require('./thin');
|
|
132
136
|
}
|
|
133
137
|
|
|
138
|
+
// validate external authentication credentials for token and non-token Authentications
|
|
139
|
+
function _validateExternalAuthCredentials(outOptions, inCreatePool, isTokenAuth = false) {
|
|
140
|
+
if (!outOptions.externalAuth) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (outOptions.password !== undefined) {
|
|
144
|
+
errors.throwErr(isTokenAuth ? errors.ERR_TOKEN_BASED_AUTH
|
|
145
|
+
: errors.ERR_WRONG_CRED_FOR_EXTAUTH);
|
|
146
|
+
}
|
|
147
|
+
if (outOptions.user) {
|
|
148
|
+
if (inCreatePool && !settings.thin) {
|
|
149
|
+
// Thick mode currently does not allow proxy users to be specified
|
|
150
|
+
// during pool creation.
|
|
151
|
+
errors.throwErr(errors.ERR_WRONG_CRED_FOR_EXTAUTH);
|
|
152
|
+
} else if (outOptions.user[0] !== '[' || outOptions.user.slice(-1) !== ']') {
|
|
153
|
+
// username is not enclosed in [].
|
|
154
|
+
errors.throwErr(errors.ERR_WRONG_USER_FORMAT_EXTAUTH_PROXY);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
134
159
|
//-----------------------------------------------------------------------------
|
|
135
160
|
// _verifyOptions()
|
|
136
161
|
//
|
|
@@ -532,10 +557,7 @@ async function _verifyOptions(options, inCreatePool) {
|
|
|
532
557
|
|
|
533
558
|
// check access token
|
|
534
559
|
if (options.accessToken !== undefined) {
|
|
535
|
-
|
|
536
|
-
// cannot set username or password for token based authentication
|
|
537
|
-
errors.assert(outOptions.user === undefined &&
|
|
538
|
-
outOptions.password === undefined, errors.ERR_TOKEN_BASED_AUTH);
|
|
560
|
+
_validateExternalAuthCredentials(outOptions, inCreatePool, true);
|
|
539
561
|
|
|
540
562
|
// homogenous (for pool) and externalAuth (both) must be set
|
|
541
563
|
if (inCreatePool) {
|
|
@@ -581,20 +603,7 @@ async function _verifyOptions(options, inCreatePool) {
|
|
|
581
603
|
// Check external Auth config.
|
|
582
604
|
// Allow Session User enclosed in [] for proxy authentication.
|
|
583
605
|
if (outOptions.token === undefined && outOptions.externalAuth) {
|
|
584
|
-
|
|
585
|
-
errors.throwErr(errors.ERR_WRONG_CRED_FOR_EXTAUTH);
|
|
586
|
-
}
|
|
587
|
-
if (outOptions.user) {
|
|
588
|
-
|
|
589
|
-
if (inCreatePool && !settings.thin) {
|
|
590
|
-
// Thick mode currently does not allow proxy users to specified
|
|
591
|
-
// during pool creation.
|
|
592
|
-
errors.throwErr(errors.ERR_WRONG_CRED_FOR_EXTAUTH);
|
|
593
|
-
} else if (outOptions.user[0] !== '[' || outOptions.user.slice(-1) !== ']') {
|
|
594
|
-
// username is not enclosed in [].
|
|
595
|
-
errors.throwErr(errors.ERR_WRONG_USER_FORMAT_EXTAUTH_PROXY);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
606
|
+
_validateExternalAuthCredentials(outOptions, inCreatePool);
|
|
598
607
|
}
|
|
599
608
|
|
|
600
609
|
return outOptions;
|
|
@@ -616,15 +625,13 @@ async function createPool(options) {
|
|
|
616
625
|
// this will invoke hookFn() written in application side.
|
|
617
626
|
// for token based auth it will generate access token and save it in options.
|
|
618
627
|
for (const hookFn of registeredHooks) {
|
|
619
|
-
// eslint-disable-next-line no-useless-catch
|
|
620
628
|
try {
|
|
621
629
|
await hookFn(options);
|
|
622
630
|
} catch (error) {
|
|
623
631
|
errors.throwWrapErr(error, errors.ERR_CALLOUT_FN);
|
|
624
632
|
}
|
|
625
633
|
}
|
|
626
|
-
|
|
627
|
-
options = await _verifyOptions(options, true);
|
|
634
|
+
options = await _updateUserOptionsFromConnectString(options, true);
|
|
628
635
|
const sessionCallback = options.sessionCallback;
|
|
629
636
|
if (typeof sessionCallback === 'function')
|
|
630
637
|
delete options.sessionCallback;
|
|
@@ -687,6 +694,15 @@ async function createPool(options) {
|
|
|
687
694
|
pool._impl._connectString = options.connectString;
|
|
688
695
|
pool._impl._user = options.user;
|
|
689
696
|
try {
|
|
697
|
+
// registers a callback poolEventReg having 'eventName' and 'arguments of the
|
|
698
|
+
// event' as its arguments. It is passed as part of options while creating impl
|
|
699
|
+
// pool, so that events emitted in impl pool can access the lib pool instance.
|
|
700
|
+
// For now, no argument is passed as args, but can be used later for future metrics.
|
|
701
|
+
options.poolEventReg = ((eventName, ...args) => {
|
|
702
|
+
if (traceHandler.isEnabled()) {
|
|
703
|
+
traceHandler.getTraceInstance()?.[eventName](pool, ...args);
|
|
704
|
+
}
|
|
705
|
+
});
|
|
690
706
|
await pool._impl.create(options);
|
|
691
707
|
} finally {
|
|
692
708
|
if (poolAlias) {
|
|
@@ -759,16 +775,14 @@ async function getConnection(a1) {
|
|
|
759
775
|
// this will invoke hookFn() written in application side.
|
|
760
776
|
// for token based auth it will generate access token and save it in options.
|
|
761
777
|
for (const hookFn of registeredHooks) {
|
|
762
|
-
// eslint-disable-next-line no-useless-catch
|
|
763
778
|
try {
|
|
764
779
|
await hookFn(options);
|
|
765
780
|
} catch (error) {
|
|
766
781
|
errors.throwWrapErr(error, errors.ERR_CALLOUT_FN);
|
|
767
782
|
}
|
|
768
783
|
}
|
|
769
|
-
|
|
770
784
|
// create a standalone connection
|
|
771
|
-
options = await
|
|
785
|
+
options = await _updateUserOptionsFromConnectString(options, false);
|
|
772
786
|
settings.addToOptions(options,
|
|
773
787
|
"connectionClass",
|
|
774
788
|
"driverName",
|
|
@@ -909,13 +923,9 @@ async function shutdown(a1, a2) {
|
|
|
909
923
|
}
|
|
910
924
|
|
|
911
925
|
async function _setConfigParameters(obj, credential, configProvider) {
|
|
912
|
-
const configObject = {};
|
|
913
926
|
const pmSection = 'njs';
|
|
914
927
|
const params = obj[pmSection];
|
|
915
|
-
|
|
916
|
-
const val = params[key];
|
|
917
|
-
configObject[key] = val;
|
|
918
|
-
}
|
|
928
|
+
const configObject = params ? { ...params } : {};
|
|
919
929
|
configObject.connectString = obj.connect_descriptor;
|
|
920
930
|
configObject.configTTL = obj.config_time_to_live;
|
|
921
931
|
if (!configObject.connectString)
|
|
@@ -951,11 +961,11 @@ async function _retrieveParamValueFromVault(paramObj, credential, configProvider
|
|
|
951
961
|
console.log("WARNING: Base64 Encoding in a JSON Password should only be used in development environments");
|
|
952
962
|
return Buffer.from(paramObj.value, "base64").toString("utf-8");
|
|
953
963
|
} else if (paramObj.type == "text") {
|
|
954
|
-
if (configProvider == 'azurevault' || configProvider == 'ocivault') {
|
|
964
|
+
if (configProvider == 'azurevault' || configProvider == 'ocivault' || configProvider == 'awssecretsmanager') {
|
|
955
965
|
console.log("WARNING: Plain Text in a JSON Password should only be used in development environments");
|
|
956
966
|
return paramObj.value;
|
|
957
967
|
} else
|
|
958
|
-
errors.throwErr(errors.ERR_CONFIG_PROVIDER_PARAM_TYPE, 'password type text is only allowed in ocivault and
|
|
968
|
+
errors.throwErr(errors.ERR_CONFIG_PROVIDER_PARAM_TYPE, 'password type text is only allowed in ocivault, azurevault and awssecretsmanager');
|
|
959
969
|
} else if (paramObj.type == "azurevault") {
|
|
960
970
|
paramMap.set('azuresecreturl', paramObj.value);
|
|
961
971
|
if (!(configProvider == 'azure' || configProvider == 'azurevault'))
|
|
@@ -981,9 +991,8 @@ async function _retrieveParamValueFromVault(paramObj, credential, configProvider
|
|
|
981
991
|
args.credential = credential;
|
|
982
992
|
args.paramMap = paramMap;
|
|
983
993
|
const vaultReturn = await hookFn(args);
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
} else {
|
|
994
|
+
return vaultReturn[0];
|
|
995
|
+
} else {
|
|
987
996
|
errors.throwErr(errors.ERR_CONFIG_PROVIDER_PARAM_TYPE, 'password/wallet_location');
|
|
988
997
|
}
|
|
989
998
|
}
|
|
@@ -1012,12 +1021,14 @@ async function _checkConfigProvider(options) {
|
|
|
1012
1021
|
secondOpts = { ...config};
|
|
1013
1022
|
|
|
1014
1023
|
//deobfuscate password
|
|
1015
|
-
if (secondOpts.password)
|
|
1016
|
-
secondOpts.password =
|
|
1024
|
+
if (secondOpts.password instanceof ObfuscatedValue) {
|
|
1025
|
+
secondOpts.password = secondOpts.password.get();
|
|
1026
|
+
}
|
|
1017
1027
|
|
|
1018
1028
|
//deobfuscate walletContent
|
|
1019
|
-
if (secondOpts.walletContent)
|
|
1020
|
-
secondOpts.walletContent =
|
|
1029
|
+
if (secondOpts.walletContent instanceof ObfuscatedValue) {
|
|
1030
|
+
secondOpts.walletContent = secondOpts.walletContent.get();
|
|
1031
|
+
}
|
|
1021
1032
|
}
|
|
1022
1033
|
}
|
|
1023
1034
|
if (!secondOpts) {
|
|
@@ -1056,9 +1067,9 @@ async function _checkConfigProvider(options) {
|
|
|
1056
1067
|
// obfuscate password & walletcontent and store config in the cache
|
|
1057
1068
|
const cacheOpts = { ...secondOpts};
|
|
1058
1069
|
if (secondOpts.password)
|
|
1059
|
-
cacheOpts.password =
|
|
1070
|
+
cacheOpts.password = new ObfuscatedValue(secondOpts.password);
|
|
1060
1071
|
if (secondOpts.walletContent)
|
|
1061
|
-
cacheOpts.walletContent =
|
|
1072
|
+
cacheOpts.walletContent = new ObfuscatedValue(secondOpts.walletContent);
|
|
1062
1073
|
configProviderCache.set(url, { cacheOpts, timeAdded: Date.now()});
|
|
1063
1074
|
} catch (err) {
|
|
1064
1075
|
errors.throwErr(errors.ERR_CONFIG_PROVIDER_FAILED_TO_RETRIEVE_CONFIG, err.message);
|
|
@@ -1110,6 +1121,30 @@ function modifyOptionsPrecedence(cloudConfig, userConfig) {
|
|
|
1110
1121
|
return userConfig;
|
|
1111
1122
|
|
|
1112
1123
|
}
|
|
1124
|
+
async function _updateUserOptionsFromConnectString(options, inCreatePool) {
|
|
1125
|
+
let tempOptions;
|
|
1126
|
+
const hasConnectString = options.connectString || options.connectionString;
|
|
1127
|
+
if (!settings.thickModeDSNPassthrough && hasConnectString) {
|
|
1128
|
+
tempOptions = Object.create(Object.getPrototypeOf(options));
|
|
1129
|
+
Object.defineProperties(tempOptions, Object.getOwnPropertyDescriptors(options));
|
|
1130
|
+
const { connectString, resolver } = parserHelpers.getConnectDescriptorFromEZConnect(tempOptions.connectString || tempOptions.connectionString);
|
|
1131
|
+
if (tempOptions.connectString !== undefined) {
|
|
1132
|
+
tempOptions.connectString = connectString;
|
|
1133
|
+
} else if (tempOptions.connectionString != undefined) {
|
|
1134
|
+
tempOptions.connectionString = connectString;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if (resolver) parserHelpers.mergeExtendedParams(tempOptions, resolver);
|
|
1138
|
+
} else {
|
|
1139
|
+
tempOptions = options;
|
|
1140
|
+
}
|
|
1141
|
+
options = await _verifyOptions(tempOptions, inCreatePool);
|
|
1142
|
+
if (!settings.thickModeDSNPassthrough && options.connectString) {
|
|
1143
|
+
await parserHelpers.applyConnectStringOverrides(options);
|
|
1144
|
+
}
|
|
1145
|
+
return options;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1113
1148
|
//-----------------------------------------------------------------------------
|
|
1114
1149
|
// startup()
|
|
1115
1150
|
//
|
|
@@ -1178,6 +1213,62 @@ function registerConfigurationProviderHook(configProvider, fn) {
|
|
|
1178
1213
|
registeredConfigProviderHooks[configProvider] = fn;
|
|
1179
1214
|
}
|
|
1180
1215
|
|
|
1216
|
+
//-----------------------------------------------------------------------------
|
|
1217
|
+
// enquoteLiteral()
|
|
1218
|
+
//
|
|
1219
|
+
// Returns a quoted SQL literal with embedded single quotes doubled.
|
|
1220
|
+
//-----------------------------------------------------------------------------
|
|
1221
|
+
function enquoteLiteral(value) {
|
|
1222
|
+
errors.assertArgCount(arguments, 1, 1);
|
|
1223
|
+
errors.assertParamValue(typeof value === 'string', 1);
|
|
1224
|
+
return parser.enquoteLiteral(value);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
//-----------------------------------------------------------------------------
|
|
1228
|
+
// enquoteName()
|
|
1229
|
+
//
|
|
1230
|
+
// Returns a quoted SQL identifier. Embedded double quotes are rejected and the
|
|
1231
|
+
// value is converted to uppercase using locale-independent Unicode rules when
|
|
1232
|
+
// capitalize is true.
|
|
1233
|
+
//-----------------------------------------------------------------------------
|
|
1234
|
+
function enquoteName(name, capitalize = true) {
|
|
1235
|
+
errors.assertArgCount(arguments, 1, 2);
|
|
1236
|
+
errors.assertParamValue(typeof name === 'string', 1);
|
|
1237
|
+
if (arguments.length > 1)
|
|
1238
|
+
errors.assertParamValue(typeof capitalize === 'boolean', 2);
|
|
1239
|
+
|
|
1240
|
+
if (name.includes('"')) {
|
|
1241
|
+
errors.throwErr(errors.ERR_INVALID_SQL_NAME);
|
|
1242
|
+
}
|
|
1243
|
+
return `"${capitalize ? name.toUpperCase() : name}"`;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
//-----------------------------------------------------------------------------
|
|
1247
|
+
// isQualifiedSqlName()
|
|
1248
|
+
//
|
|
1249
|
+
// Returns whether the trimmed value is a qualified SQL name. Qualified names
|
|
1250
|
+
// are dotted simple SQL names with an optional trailing @ database link, and
|
|
1251
|
+
// the database link name can itself be dotted.
|
|
1252
|
+
//-----------------------------------------------------------------------------
|
|
1253
|
+
function isQualifiedSqlName(name) {
|
|
1254
|
+
errors.assertArgCount(arguments, 1, 1);
|
|
1255
|
+
errors.assertParamValue(typeof name === 'string', 1);
|
|
1256
|
+
const text = name.trim();
|
|
1257
|
+
return parser.parseQualifiedSqlName(text, 0) === text.length;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
//-----------------------------------------------------------------------------
|
|
1261
|
+
// isSimpleSqlName()
|
|
1262
|
+
//
|
|
1263
|
+
// Returns whether the trimmed value is a single simple SQL name.
|
|
1264
|
+
//-----------------------------------------------------------------------------
|
|
1265
|
+
function isSimpleSqlName(name) {
|
|
1266
|
+
errors.assertArgCount(arguments, 1, 1);
|
|
1267
|
+
errors.assertParamValue(typeof name === 'string', 1);
|
|
1268
|
+
const text = name.trim();
|
|
1269
|
+
return parser.parseSimpleSqlName(text, 0) === text.length;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1181
1272
|
// module exports
|
|
1182
1273
|
module.exports = {
|
|
1183
1274
|
|
|
@@ -1191,6 +1282,7 @@ module.exports = {
|
|
|
1191
1282
|
JsonId: types.JsonId,
|
|
1192
1283
|
Lob,
|
|
1193
1284
|
Pool,
|
|
1285
|
+
Pipeline,
|
|
1194
1286
|
PoolStatistics,
|
|
1195
1287
|
ResultSet,
|
|
1196
1288
|
SodaDatabase,
|
|
@@ -1201,13 +1293,18 @@ module.exports = {
|
|
|
1201
1293
|
SparseVector: types.SparseVector,
|
|
1202
1294
|
IntervalYM: types.IntervalYM,
|
|
1203
1295
|
IntervalDS: types.IntervalDS,
|
|
1296
|
+
EndUserSecurityContext,
|
|
1204
1297
|
|
|
1205
1298
|
// top-level functions
|
|
1299
|
+
enquoteLiteral,
|
|
1300
|
+
enquoteName,
|
|
1206
1301
|
getConnection: nodbUtil.callbackify(nodbUtil.wrapFn(getConnection)),
|
|
1207
1302
|
createPool: nodbUtil.callbackify(nodbUtil.wrapFn(createPool)),
|
|
1208
1303
|
getNetworkServiceNames: nodbUtil.callbackify(nodbUtil.wrapFn(getNetworkServiceNames)),
|
|
1209
1304
|
getPool,
|
|
1210
1305
|
initOracleClient,
|
|
1306
|
+
isQualifiedSqlName,
|
|
1307
|
+
isSimpleSqlName,
|
|
1211
1308
|
registerProcessConfigurationHook,
|
|
1212
1309
|
registerConfigurationProviderHook,
|
|
1213
1310
|
shutdown: nodbUtil.callbackify(nodbUtil.wrapFn(shutdown)),
|
|
@@ -1711,6 +1808,14 @@ module.exports = {
|
|
|
1711
1808
|
errors.assertPropValue(Number.isInteger(value) && value >= 0,
|
|
1712
1809
|
"configProviderCacheTimeout");
|
|
1713
1810
|
settings.configProviderCacheTimeout = value;
|
|
1714
|
-
}
|
|
1811
|
+
},
|
|
1715
1812
|
|
|
1813
|
+
get thickModeDSNPassthrough() {
|
|
1814
|
+
return settings.thickModeDSNPassthrough;
|
|
1815
|
+
},
|
|
1816
|
+
|
|
1817
|
+
set thickModeDSNPassthrough(value) {
|
|
1818
|
+
errors.assertPropValue(typeof value === 'boolean', "thickModeDSNPassthrough");
|
|
1819
|
+
settings.thickModeDSNPassthrough = value;
|
|
1820
|
+
},
|
|
1716
1821
|
};
|
package/lib/parser.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Copyright (c) 2026, Oracle and/or its affiliates.
|
|
2
|
+
|
|
3
|
+
//-----------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// This software is dual-licensed to you under the Universal Permissive License
|
|
6
|
+
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
|
7
|
+
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
|
|
8
|
+
// either license.
|
|
9
|
+
//
|
|
10
|
+
// If you elect to accept the software under the Apache License, Version 2.0,
|
|
11
|
+
// the following applies:
|
|
12
|
+
//
|
|
13
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
// you may not use this file except in compliance with the License.
|
|
15
|
+
// You may obtain a copy of the License at
|
|
16
|
+
//
|
|
17
|
+
// https://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
//
|
|
19
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
// See the License for the specific language governing permissions and
|
|
23
|
+
// limitations under the License.
|
|
24
|
+
//
|
|
25
|
+
//-----------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
function isUnicodeLetter(ch) {
|
|
30
|
+
return /^\p{L}$/u.test(ch);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isUnicodeDigit(ch) {
|
|
34
|
+
return /^\p{Nd}$/u.test(ch);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isUnicodeMark(ch) {
|
|
38
|
+
return /^\p{M}$/u.test(ch);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isWhitespace(ch) {
|
|
42
|
+
return /^\s$/u.test(ch);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isUnquotedIdentifierChar(ch) {
|
|
46
|
+
return isUnicodeLetter(ch) || isUnicodeMark(ch) || isUnicodeDigit(ch) ||
|
|
47
|
+
ch === '_' || ch === '$' || ch === '#';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//-----------------------------------------------------------------------------
|
|
51
|
+
// enquoteLiteral()
|
|
52
|
+
//
|
|
53
|
+
// Returns a quoted SQL literal with embedded single quotes doubled.
|
|
54
|
+
//-----------------------------------------------------------------------------
|
|
55
|
+
function enquoteLiteral(value) {
|
|
56
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//-----------------------------------------------------------------------------
|
|
60
|
+
// readChar()
|
|
61
|
+
//
|
|
62
|
+
// Returns the Unicode character at the specified UTF-16 index together with the
|
|
63
|
+
// number of UTF-16 code units consumed. This keeps parser indexing correct for
|
|
64
|
+
// supplementary characters.
|
|
65
|
+
//-----------------------------------------------------------------------------
|
|
66
|
+
function readChar(str, index) {
|
|
67
|
+
if (index >= str.length) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// Read one complete Unicode code point so surrogate pairs are handled as a
|
|
71
|
+
// single character by the parser.
|
|
72
|
+
const cp = str.codePointAt(index);
|
|
73
|
+
const ch = String.fromCodePoint(cp);
|
|
74
|
+
return { ch, size: ch.length };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
//-----------------------------------------------------------------------------
|
|
78
|
+
// skipSpaces()
|
|
79
|
+
//
|
|
80
|
+
// Advances past any Unicode whitespace characters and returns the next
|
|
81
|
+
// non-whitespace index.
|
|
82
|
+
//-----------------------------------------------------------------------------
|
|
83
|
+
function skipSpaces(str, index) {
|
|
84
|
+
while (index < str.length) {
|
|
85
|
+
const next = readChar(str, index);
|
|
86
|
+
if (!isWhitespace(next.ch)) {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
// Advance by the width of the full code point rather than a single code
|
|
90
|
+
// unit.
|
|
91
|
+
index += next.size;
|
|
92
|
+
}
|
|
93
|
+
return index;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
//-----------------------------------------------------------------------------
|
|
97
|
+
// parseQuotedSimpleSqlName()
|
|
98
|
+
//
|
|
99
|
+
// Parses a quoted identifier. The opening quote is assumed to be at the
|
|
100
|
+
// supplied index. Quoted identifiers must be non-empty and cannot contain a
|
|
101
|
+
// raw double quote or a \u0000 character. The returned index points just after
|
|
102
|
+
// the closing quote.
|
|
103
|
+
//-----------------------------------------------------------------------------
|
|
104
|
+
function parseQuotedSimpleSqlName(str, index) {
|
|
105
|
+
index += 1;
|
|
106
|
+
let hasContent = false;
|
|
107
|
+
|
|
108
|
+
while (index < str.length) {
|
|
109
|
+
const cur = readChar(str, index);
|
|
110
|
+
if (cur.ch === '"') {
|
|
111
|
+
// Reject empty quoted identifiers; otherwise consume the closing quote.
|
|
112
|
+
return hasContent ? index + cur.size : undefined;
|
|
113
|
+
}
|
|
114
|
+
if (cur.ch === '\u0000') {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Any non-quote, non-NUL character is accepted inside quoted names.
|
|
119
|
+
hasContent = true;
|
|
120
|
+
index += cur.size;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
//-----------------------------------------------------------------------------
|
|
125
|
+
// parseUnquotedSimpleSqlName()
|
|
126
|
+
//
|
|
127
|
+
// Parses an unquoted identifier. The first character must be a Unicode letter.
|
|
128
|
+
// Remaining characters may be Unicode letters, combining marks, decimal
|
|
129
|
+
// digits, or _, $, #. Parsing stops before the first character that is not part
|
|
130
|
+
// of the identifier.
|
|
131
|
+
//-----------------------------------------------------------------------------
|
|
132
|
+
function parseUnquotedSimpleSqlName(str, index) {
|
|
133
|
+
const first = readChar(str, index);
|
|
134
|
+
if (!first || !isUnicodeLetter(first.ch)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Consume the required leading Unicode letter first.
|
|
139
|
+
index += first.size;
|
|
140
|
+
while (index < str.length) {
|
|
141
|
+
const cur = readChar(str, index);
|
|
142
|
+
if (!isUnquotedIdentifierChar(cur.ch)) {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
index += cur.size;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return index;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
//-----------------------------------------------------------------------------
|
|
152
|
+
// parseSimpleSqlName()
|
|
153
|
+
//
|
|
154
|
+
// Parses one simple identifier component, quoted or unquoted, and returns the
|
|
155
|
+
// index just after the parsed component.
|
|
156
|
+
//-----------------------------------------------------------------------------
|
|
157
|
+
function parseSimpleSqlName(str, index) {
|
|
158
|
+
const cur = readChar(str, index);
|
|
159
|
+
if (!cur) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
return cur.ch === '"' ? parseQuotedSimpleSqlName(str, index)
|
|
163
|
+
: parseUnquotedSimpleSqlName(str, index);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
//-----------------------------------------------------------------------------
|
|
167
|
+
// parseQualifiedSqlName()
|
|
168
|
+
//
|
|
169
|
+
// Parses a qualified SQL name made up of one or more simple SQL names
|
|
170
|
+
// separated by dots, optionally followed by an @ database link name that can
|
|
171
|
+
// itself contain dotted simple SQL names. The returned index points just
|
|
172
|
+
// after the parsed qualified name; undefined is returned if parsing fails.
|
|
173
|
+
//-----------------------------------------------------------------------------
|
|
174
|
+
function parseQualifiedSqlName(str, index) {
|
|
175
|
+
let foundDbLink = false;
|
|
176
|
+
|
|
177
|
+
while (true) {
|
|
178
|
+
const parsedIndex = parseSimpleSqlName(str, index);
|
|
179
|
+
if (parsedIndex === undefined) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
index = skipSpaces(str, parsedIndex);
|
|
184
|
+
|
|
185
|
+
if (index === str.length) {
|
|
186
|
+
return index;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const separator = readChar(str, index);
|
|
190
|
+
index = skipSpaces(str, index + separator.size);
|
|
191
|
+
|
|
192
|
+
if (!foundDbLink && separator.ch === '@') {
|
|
193
|
+
foundDbLink = true;
|
|
194
|
+
} else if (separator.ch !== '.') {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = {
|
|
201
|
+
enquoteLiteral,
|
|
202
|
+
parseQualifiedSqlName,
|
|
203
|
+
parseSimpleSqlName
|
|
204
|
+
};
|
package/lib/pipeline.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Copyright (c) 2026, Oracle and/or its affiliates.
|
|
2
|
+
|
|
3
|
+
//-----------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// This software is dual-licensed to you under the Universal Permissive License
|
|
6
|
+
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
|
7
|
+
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
|
|
8
|
+
// either license.
|
|
9
|
+
//
|
|
10
|
+
// If you elect to accept the software under the Apache License, Version 2.0,
|
|
11
|
+
// the following applies:
|
|
12
|
+
//
|
|
13
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
// you may not use this file except in compliance with the License.
|
|
15
|
+
// You may obtain a copy of the License at
|
|
16
|
+
//
|
|
17
|
+
// https://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
//
|
|
19
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
// See the License for the specific language governing permissions and
|
|
23
|
+
// limitations under the License.
|
|
24
|
+
//
|
|
25
|
+
//-----------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
const errors = require('./errors.js');
|
|
30
|
+
const nodbUtil = require('./util.js');
|
|
31
|
+
const settings = require('./settings.js');
|
|
32
|
+
|
|
33
|
+
class Pipeline {
|
|
34
|
+
constructor() {
|
|
35
|
+
this.operations = []; // operations in pipeline.
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_validExecOptions = [
|
|
39
|
+
'autoCommit', 'fetchArraySize', 'maxRows', 'outFormat', 'prefetchRows'
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
_validExecManyOptions = [...this._validExecOptions, 'bindDefs'];
|
|
43
|
+
|
|
44
|
+
_checkOptions(opts, validOptions) {
|
|
45
|
+
if (opts && typeof opts === 'object') {
|
|
46
|
+
for (const [k,] of Object.entries(opts)) {
|
|
47
|
+
if (!validOptions.includes(k))
|
|
48
|
+
errors.throwErr(errors.ERR_PIPELINE_EXEC_OPTION_NOT_SUPPORTED, k);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_addOperation(opType, data = {}) {
|
|
54
|
+
if (['execute', 'fetchOne', 'fetchMany', 'fetchAll'].includes(opType))
|
|
55
|
+
this._checkOptions(data.options, this._validExecOptions);
|
|
56
|
+
else if (opType === 'executeMany')
|
|
57
|
+
this._checkOptions(data.options, this._validExecManyOptions);
|
|
58
|
+
this.operations.push({ opType, ...data });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
addExecute(statement, parameters = [], options = {}) {
|
|
62
|
+
errors.assertParamValue(nodbUtil.isObject(options), 3);
|
|
63
|
+
this._addOperation('execute', { statement, parameters, options });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
addExecuteMany(statement, parameters, options = {}) {
|
|
67
|
+
errors.assertParamValue(nodbUtil.isObject(options), 3);
|
|
68
|
+
this._addOperation('executeMany', { statement, parameters, options });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
addFetchOne(statement, parameters = [], options = {}, fetchLobs = true) {
|
|
72
|
+
errors.assertParamValue(nodbUtil.isObject(options), 3);
|
|
73
|
+
options.prefetchRows = options.prefetchRows ?? 1;
|
|
74
|
+
options.fetchArraySize = options.fetchArraySize ?? 1;
|
|
75
|
+
errors.assertParamValue(typeof fetchLobs === 'boolean', 4);
|
|
76
|
+
this._addOperation('fetchOne', { statement, parameters, options,
|
|
77
|
+
fetchLobs});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
addFetchMany(statement, parameters = [], options = {},
|
|
81
|
+
numRows = settings.fetchArraySize, fetchLobs = true) {
|
|
82
|
+
errors.assertParamValue(nodbUtil.isObject(options), 3);
|
|
83
|
+
options.prefetchRows = options.prefetchRows ?? numRows;
|
|
84
|
+
if (numRows > 0)
|
|
85
|
+
options.fetchArraySize = options.fetchArraySize ?? numRows;
|
|
86
|
+
errors.assertParamValue(Number.isInteger(numRows) && numRows >= 0, 4);
|
|
87
|
+
errors.assertParamValue(typeof fetchLobs === 'boolean', 5);
|
|
88
|
+
this._addOperation('fetchMany', { statement, parameters, options,
|
|
89
|
+
numRows, fetchLobs});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
addFetchAll(statement, parameters = [], options = {},
|
|
93
|
+
fetchArraySize, fetchLobs) {
|
|
94
|
+
errors.assertParamValue(nodbUtil.isObject(options), 3);
|
|
95
|
+
fetchArraySize = fetchArraySize ?? options.fetchArraySize ??
|
|
96
|
+
settings.fetchArraySize;
|
|
97
|
+
options.prefetchRows = options.prefetchRows ?? fetchArraySize;
|
|
98
|
+
options.fetchArraySize = fetchArraySize;
|
|
99
|
+
fetchLobs = fetchLobs ?? true;
|
|
100
|
+
errors.assertParamValue(Number.isInteger(fetchArraySize) &&
|
|
101
|
+
fetchArraySize > 0, 4);
|
|
102
|
+
errors.assertParamValue(typeof fetchLobs === 'boolean', 5);
|
|
103
|
+
this._addOperation('fetchAll', { statement, parameters, options,
|
|
104
|
+
fetchArraySize, fetchLobs});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
addCommit() {
|
|
108
|
+
this._addOperation('commit', {});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = Pipeline;
|