oracledb 6.10.0 → 7.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/LICENSE.txt +1 -1
  2. package/NOTICE.txt +1 -1
  3. package/README.md +9 -9
  4. package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
  5. package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-7.0.0-js-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
  8. package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
  10. package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
  12. package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +1 -0
  13. package/eslint.config.js +39 -0
  14. package/lib/connection.js +412 -37
  15. package/lib/endUserSecurityContext.js +176 -0
  16. package/lib/errors.js +86 -5
  17. package/lib/impl/connection.js +76 -2
  18. package/lib/impl/datahandlers/buffer.js +19 -5
  19. package/lib/impl/datahandlers/vector.js +25 -5
  20. package/lib/impl/lob.js +9 -0
  21. package/lib/impl/parserHelpers.js +443 -0
  22. package/lib/impl/resultset.js +1 -1
  23. package/lib/lob.js +27 -14
  24. package/lib/obfuscation.js +97 -0
  25. package/lib/oracledb.js +148 -43
  26. package/lib/parser.js +204 -0
  27. package/lib/pipeline.js +113 -0
  28. package/lib/pool.js +16 -1
  29. package/lib/resultset.js +42 -21
  30. package/lib/settings.js +1 -0
  31. package/lib/thin/connection.js +467 -53
  32. package/lib/thin/dbObject.js +7 -1
  33. package/lib/thin/lob.js +20 -1
  34. package/lib/thin/pool.js +42 -62
  35. package/lib/thin/protocol/capabilities.js +18 -3
  36. package/lib/thin/protocol/constants.js +82 -2
  37. package/lib/thin/protocol/encryptDecrypt.js +43 -8
  38. package/lib/thin/protocol/messages/auth.js +6 -1
  39. package/lib/thin/protocol/messages/base.js +245 -4
  40. package/lib/thin/protocol/messages/dataType.js +8 -3
  41. package/lib/thin/protocol/messages/directPathLoadStream.js +377 -0
  42. package/lib/thin/protocol/messages/directPathOp.js +65 -0
  43. package/lib/thin/protocol/messages/directPathPrepare.js +158 -0
  44. package/lib/thin/protocol/messages/endPipeline.js +56 -0
  45. package/lib/thin/protocol/messages/execute.js +9 -7
  46. package/lib/thin/protocol/messages/index.js +9 -1
  47. package/lib/thin/protocol/messages/protocol.js +2 -2
  48. package/lib/thin/protocol/messages/withData.js +8 -140
  49. package/lib/thin/protocol/packet.js +115 -24
  50. package/lib/thin/protocol/protocol.js +110 -25
  51. package/lib/thin/protocol/utils.js +1 -26
  52. package/lib/thin/resultSet.js +13 -19
  53. package/lib/thin/sqlnet/ezConnectResolver.js +2 -2
  54. package/lib/thin/sqlnet/networkSession.js +62 -40
  55. package/lib/thin/sqlnet/ntTcp.js +24 -0
  56. package/lib/thin/sqlnet/nvStrToNvPair.js +50 -25
  57. package/lib/thin/sqlnet/packet.js +25 -4
  58. package/lib/thin/sqlnet/paramParser.js +10 -7
  59. package/lib/thin/statementCache.js +2 -4
  60. package/lib/traceHandler.js +59 -2
  61. package/lib/transformer.js +8 -3
  62. package/lib/types.js +35 -21
  63. package/lib/util.js +6 -6
  64. package/lib/version.js +3 -3
  65. package/package/install.js +5 -5
  66. package/package/prunebinaries.js +1 -1
  67. package/package.json +1 -1
  68. package/plugins/configProviders/awsCommon.js +125 -0
  69. package/plugins/configProviders/awss3/index.js +138 -0
  70. package/plugins/configProviders/awssecretsmanager/index.js +147 -0
  71. package/plugins/configProviders/azure/index.js +4 -5
  72. package/plugins/token/extensionAzure/index.js +0 -1
  73. package/plugins/token/extensionOci/index.js +0 -1
  74. package/build/Release/oracledb-6.10.0-darwin-arm64.node +0 -0
  75. package/build/Release/oracledb-6.10.0-darwin-arm64.node-buildinfo.txt +0 -1
  76. package/build/Release/oracledb-6.10.0-darwin-x64.node +0 -0
  77. package/build/Release/oracledb-6.10.0-darwin-x64.node-buildinfo.txt +0 -1
  78. package/build/Release/oracledb-6.10.0-js-buildinfo.txt +0 -1
  79. package/build/Release/oracledb-6.10.0-linux-arm64.node +0 -0
  80. package/build/Release/oracledb-6.10.0-linux-arm64.node-buildinfo.txt +0 -1
  81. package/build/Release/oracledb-6.10.0-linux-x64.node +0 -0
  82. package/build/Release/oracledb-6.10.0-linux-x64.node-buildinfo.txt +0 -1
  83. package/build/Release/oracledb-6.10.0-win32-x64.node +0 -0
  84. 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, 2025, Oracle and/or its affiliates.
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
- if (outOptions.password) {
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 _verifyOptions(options, false);
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
- for (const key in params) {
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 azurevault');
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
- const paramValue = vaultReturn[0];
985
- return paramValue;
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 = protocolUtil.getDeobfuscatedValue(secondOpts.password.value, secondOpts.password.obfuscatedValue);
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 = protocolUtil.getDeobfuscatedValue(secondOpts.walletContent.value, secondOpts.walletContent.obfuscatedValue);
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 = protocolUtil.setObfuscatedValue(secondOpts.password);
1070
+ cacheOpts.password = new ObfuscatedValue(secondOpts.password);
1060
1071
  if (secondOpts.walletContent)
1061
- cacheOpts.walletContent = protocolUtil.setObfuscatedValue(secondOpts.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
+ };
@@ -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;