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.
Files changed (90) 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.1-darwin-arm64.node +0 -0
  5. package/build/Release/oracledb-7.0.1-darwin-arm64.node-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-7.0.1-js-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-7.0.1-linux-arm64.node +0 -0
  8. package/build/Release/oracledb-7.0.1-linux-arm64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-7.0.1-linux-x64.node +0 -0
  10. package/build/Release/oracledb-7.0.1-linux-x64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-7.0.1-win32-x64.node +0 -0
  12. package/build/Release/oracledb-7.0.1-win32-x64.node-buildinfo.txt +1 -0
  13. package/lib/aqDeqOptions.js +4 -4
  14. package/lib/aqEnqOptions.js +2 -2
  15. package/lib/aqQueue.js +11 -6
  16. package/lib/connection.js +407 -40
  17. package/lib/endUserSecurityContext.js +169 -0
  18. package/lib/errors.js +108 -5
  19. package/lib/impl/connection.js +76 -2
  20. package/lib/impl/datahandlers/buffer.js +19 -5
  21. package/lib/impl/datahandlers/vector.js +25 -5
  22. package/lib/impl/lob.js +9 -0
  23. package/lib/impl/parserHelpers.js +447 -0
  24. package/lib/impl/resultset.js +1 -1
  25. package/lib/lob.js +27 -14
  26. package/lib/obfuscation.js +97 -0
  27. package/lib/oracledb.js +148 -43
  28. package/lib/parser.js +204 -0
  29. package/lib/pipeline.js +113 -0
  30. package/lib/pool.js +16 -1
  31. package/lib/resultset.js +42 -21
  32. package/lib/settings.js +1 -0
  33. package/lib/thin/connection.js +461 -53
  34. package/lib/thin/dbObject.js +10 -4
  35. package/lib/thin/lob.js +20 -1
  36. package/lib/thin/pool.js +43 -63
  37. package/lib/thin/protocol/capabilities.js +18 -3
  38. package/lib/thin/protocol/constants.js +82 -2
  39. package/lib/thin/protocol/encryptDecrypt.js +43 -8
  40. package/lib/thin/protocol/messages/aqArray.js +2 -2
  41. package/lib/thin/protocol/messages/aqBase.js +2 -2
  42. package/lib/thin/protocol/messages/auth.js +6 -1
  43. package/lib/thin/protocol/messages/base.js +246 -5
  44. package/lib/thin/protocol/messages/dataType.js +8 -3
  45. package/lib/thin/protocol/messages/directPathLoadStream.js +377 -0
  46. package/lib/thin/protocol/messages/directPathOp.js +65 -0
  47. package/lib/thin/protocol/messages/directPathPrepare.js +158 -0
  48. package/lib/thin/protocol/messages/endPipeline.js +56 -0
  49. package/lib/thin/protocol/messages/execute.js +9 -7
  50. package/lib/thin/protocol/messages/index.js +9 -1
  51. package/lib/thin/protocol/messages/protocol.js +2 -2
  52. package/lib/thin/protocol/messages/withData.js +10 -143
  53. package/lib/thin/protocol/packet.js +115 -24
  54. package/lib/thin/protocol/protocol.js +110 -25
  55. package/lib/thin/protocol/utils.js +1 -26
  56. package/lib/thin/resultSet.js +13 -19
  57. package/lib/thin/sqlnet/ezConnectResolver.js +4 -4
  58. package/lib/thin/sqlnet/navNodes.js +4 -2
  59. package/lib/thin/sqlnet/networkSession.js +78 -50
  60. package/lib/thin/sqlnet/ntTcp.js +24 -0
  61. package/lib/thin/sqlnet/nvStrToNvPair.js +50 -25
  62. package/lib/thin/sqlnet/packet.js +49 -8
  63. package/lib/thin/sqlnet/paramParser.js +10 -7
  64. package/lib/thin/sqlnet/sessionAtts.js +2 -2
  65. package/lib/thin/statementCache.js +2 -4
  66. package/lib/traceHandler.js +59 -2
  67. package/lib/transformer.js +8 -3
  68. package/lib/types.js +35 -21
  69. package/lib/util.js +6 -6
  70. package/lib/version.js +4 -4
  71. package/package/install.js +5 -5
  72. package/package/prunebinaries.js +1 -1
  73. package/package.json +1 -1
  74. package/plugins/configProviders/awsCommon.js +125 -0
  75. package/plugins/configProviders/awss3/index.js +138 -0
  76. package/plugins/configProviders/awssecretsmanager/index.js +147 -0
  77. package/plugins/configProviders/azure/index.js +4 -5
  78. package/plugins/token/extensionAzure/index.js +0 -1
  79. package/plugins/token/extensionOci/index.js +0 -1
  80. package/build/Release/oracledb-6.10.0-darwin-arm64.node +0 -0
  81. package/build/Release/oracledb-6.10.0-darwin-arm64.node-buildinfo.txt +0 -1
  82. package/build/Release/oracledb-6.10.0-darwin-x64.node +0 -0
  83. package/build/Release/oracledb-6.10.0-darwin-x64.node-buildinfo.txt +0 -1
  84. package/build/Release/oracledb-6.10.0-js-buildinfo.txt +0 -1
  85. package/build/Release/oracledb-6.10.0-linux-arm64.node +0 -0
  86. package/build/Release/oracledb-6.10.0-linux-arm64.node-buildinfo.txt +0 -1
  87. package/build/Release/oracledb-6.10.0-linux-x64.node +0 -0
  88. package/build/Release/oracledb-6.10.0-linux-x64.node-buildinfo.txt +0 -1
  89. package/build/Release/oracledb-6.10.0-win32-x64.node +0 -0
  90. package/build/Release/oracledb-6.10.0-win32-x64.node-buildinfo.txt +0 -1
@@ -0,0 +1,447 @@
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 { createNVPair } = require('../thin/sqlnet/nvStrToNvPair.js');
30
+ const EZConnectResolver = require('../thin/sqlnet/ezConnectResolver.js');
31
+ const { NLParamParser, tnsnamesFilePath } = require('../thin/sqlnet/paramParser.js');
32
+ const errors = require('../errors.js');
33
+ const process = require('process');
34
+
35
+ const NJS_PREFIX = 'njs.';
36
+ const RHS_LIST = 2;
37
+ const NUMBER_KEYS = new Set([
38
+ 'poolIncrement', 'poolMax', 'poolMin', 'poolTimeout', 'queueTimeout',
39
+ 'maxLifetimeSession', 'poolMaxPerShard', 'poolPingInterval',
40
+ 'poolPingTimeout', 'stmtCacheSize', 'mode', 'getMode'
41
+ ]);
42
+
43
+ const BOOLEAN_KEYS = new Set([
44
+ 'events', 'externalAuth', 'homogeneous', 'useTcpFastOpen',
45
+ 'disableOob', 'sodaMetaDataCache'
46
+ ]);
47
+
48
+ const STRING_KEYS = new Set([
49
+ 'driverName', 'edition', 'machine', 'osUser', 'program', 'terminal'
50
+ ]);
51
+
52
+ const nlParamParser = new NLParamParser();
53
+ const childLookupCache = new WeakMap();
54
+
55
+ /**
56
+ * Formats a boolean value as a string 'true' or 'false'.
57
+ * @param {boolean} value - The boolean value to format.
58
+ * @returns {string} 'true' if value is true, 'false' otherwise.
59
+ */
60
+ function formatBooleanTrueFalse(value) {
61
+ return value ? 'true' : 'false';
62
+ }
63
+
64
+ const CONNECT_OVERRIDES = [
65
+ // DESCRIPTION-level parameters
66
+ { opt: 'sdu', section: ['DESCRIPTION', 'SDU'], fmt: String },
67
+ { opt: 'failover', section: ['DESCRIPTION', 'FAILOVER'] },
68
+ { opt: 'loadBalance', section: ['DESCRIPTION', 'LOAD_BALANCE'] },
69
+ { opt: 'retryCount', section: ['DESCRIPTION', 'RETRY_COUNT'], fmt: String },
70
+ { opt: 'retryDelay', section: ['DESCRIPTION', 'RETRY_DELAY'], fmt: String },
71
+ { opt: 'sourceRoute', section: ['DESCRIPTION', 'SOURCE_ROUTE'] },
72
+ { opt: 'transportConnectTimeout', section: ['DESCRIPTION', 'TRANSPORT_CONNECT_TIMEOUT'], fmt: String },
73
+ { opt: 'useSNI', section: ['DESCRIPTION', 'USE_SNI'], fmt: formatBooleanTrueFalse },
74
+
75
+ // CONNECT_DATA parameters
76
+ { opt: 'poolBoundary', section: ['DESCRIPTION', 'CONNECT_DATA', 'POOL_BOUNDARY'] },
77
+ { opt: 'poolConnectionClass', section: ['DESCRIPTION', 'CONNECT_DATA', 'POOL_CONNECTION_CLASS'] },
78
+ { opt: 'poolName', section: ['DESCRIPTION', 'CONNECT_DATA', 'POOL_NAME'] },
79
+ { opt: 'poolPurity', section: ['DESCRIPTION', 'CONNECT_DATA', 'POOL_PURITY'] },
80
+ { opt: 'expireTime', section: ['DESCRIPTION', 'CONNECT_DATA', 'EXPIRE_TIME'], fmt: String },
81
+
82
+ // SECURITY parameters
83
+ { opt: 'walletLocation', section: ['DESCRIPTION', 'SECURITY', 'MY_WALLET_DIRECTORY'] },
84
+ { opt: 'sslServerCertDN', section: ['DESCRIPTION', 'SECURITY', 'SSL_SERVER_CERT_DN'] },
85
+ { opt: 'sslServerDNMatch', section: ['DESCRIPTION', 'SECURITY', 'SSL_SERVER_DN_MATCH'] },
86
+
87
+ // ADDRESS parameters
88
+ { opt: 'httpsProxy', section: ['DESCRIPTION', 'ADDRESS', 'HTTPS_PROXY'], perAddress: true },
89
+ { opt: 'httpsProxyPort', section: ['DESCRIPTION', 'ADDRESS', 'HTTPS_PROXY_PORT'], perAddress: true, fmt: String }
90
+ ];
91
+
92
+ /**
93
+ * Returns the uppercase name of the node if it exists and is a string.
94
+ * @param {Object} node - The node to get the name from.
95
+ * @returns {string|undefined} Uppercase name or undefined.
96
+ */
97
+ function getUpperName(node) {
98
+ if (!node || typeof node.name !== 'string') {
99
+ return undefined;
100
+ }
101
+ return node.name.toUpperCase();
102
+ }
103
+ // Drops the cached child-name lookup for a node so the next search rebuilds it after structural changes.
104
+ function invalidateChildLookup(node) {
105
+ if (node) {
106
+ childLookupCache.delete(node);
107
+ }
108
+ }
109
+ // Builds (or reuses) a per-parent map from uppercase child names to their node arrays for quick lookups.
110
+ function getChildLookup(parent) {
111
+ if (!parent || !Array.isArray(parent.list) || parent.list.length === 0) {
112
+ return null;
113
+ }
114
+ let lookup = childLookupCache.get(parent);
115
+ if (!lookup) {
116
+ lookup = Object.create(null);
117
+ for (const child of parent.list) {
118
+ const key = getUpperName(child);
119
+ if (!key) {
120
+ continue;
121
+ }
122
+ if (lookup[key]) {
123
+ lookup[key].push(child);
124
+ } else {
125
+ lookup[key] = [child];
126
+ }
127
+ }
128
+ childLookupCache.set(parent, lookup);
129
+ }
130
+ return lookup;
131
+ }
132
+ // Uses the cached lookup to return the first child whose name matches the provided string, or undefined if absent.
133
+ function findFirstChild(parent, name) {
134
+ if (typeof name !== 'string') {
135
+ return undefined;
136
+ }
137
+ const lookup = getChildLookup(parent);
138
+ if (!lookup) {
139
+ return undefined;
140
+ }
141
+ const key = name.toUpperCase();
142
+ const list = lookup[key];
143
+ return list ? list[0] : undefined;
144
+ }
145
+
146
+ /**
147
+ * Collects all DESCRIPTION nodes from the root NVPair tree.
148
+ * Traverses the tree using a stack to find DESCRIPTION nodes.
149
+ * @param {Object} root - The root NVPair node.
150
+ * @returns {Array} Array of DESCRIPTION nodes.
151
+ */
152
+ function collectDescriptions(root) {
153
+ if (!root || typeof root.name !== 'string') {
154
+ return [];
155
+ }
156
+ const stack = [root];
157
+ const descriptions = [];
158
+ while (stack.length) {
159
+ const node = stack.pop();
160
+ if (!node || typeof node.name !== 'string') {
161
+ continue;
162
+ }
163
+ const name = node.name.toUpperCase();
164
+ if (name === 'DESCRIPTION' && node.rhsType === RHS_LIST) {
165
+ descriptions.push(node);
166
+ continue;
167
+ }
168
+ if (name === 'DESCRIPTION_LIST' && node.list) {
169
+ for (let i = 0; i < node.list.length; i++) {
170
+ stack.push(node.list[i]);
171
+ }
172
+ }
173
+ }
174
+ return descriptions;
175
+ }
176
+
177
+ /**
178
+ * Ensures a child node with the given name exists under the parent.
179
+ * Creates it if necessary.
180
+ * @param {Object} parent - The parent NVPair node.
181
+ * @param {string} name - The name of the child node.
182
+ * @returns {Object} The child node.
183
+ */
184
+ function ensureNode(parent, name) {
185
+ const key = name.toUpperCase();
186
+ if (parent.rhsType === RHS_LIST && Array.isArray(parent.list)) {
187
+ const cached = findFirstChild(parent, key);
188
+ if (cached) {
189
+ return cached;
190
+ }
191
+ }
192
+ const created = new parent.constructor(key);
193
+ parent.addListElement(created);
194
+ invalidateChildLookup(parent);
195
+ return created;
196
+ }
197
+
198
+ /**
199
+ * Finds all child nodes with the given name under the parent.
200
+ * @param {Object} parent - The parent NVPair node.
201
+ * @param {string} name - The name to search for.
202
+ * @returns {Array} Array of matching child nodes.
203
+ */
204
+ function findChildren(parent, name) {
205
+ if (typeof name !== 'string') {
206
+ return [];
207
+ }
208
+ const lookup = getChildLookup(parent);
209
+ if (!lookup) {
210
+ return [];
211
+ }
212
+ const key = name.toUpperCase();
213
+ const matches = lookup[key];
214
+ if (!matches || matches.length === 0) {
215
+ return [];
216
+ }
217
+ return matches.slice();
218
+ }
219
+
220
+ /**
221
+ * Places a parameter node in the correct position within a DESCRIPTION list.
222
+ * Positions before ADDRESS, ADDRESS_LIST, or CONNECT_DATA.
223
+ * @param {Object} description - The DESCRIPTION NVPair node.
224
+ * @param {Object} node - The parameter node to place.
225
+ */
226
+ function placeDescriptionParam(description, node) {
227
+ const list = description.list;
228
+ const idx = list.indexOf(node);
229
+ if (idx > -1) {
230
+ list.splice(idx, 1);
231
+ }
232
+ let pos = 0;
233
+ while (pos < list.length) {
234
+ const childName = getUpperName(list[pos]);
235
+ if (childName === 'ADDRESS' || childName === 'ADDRESS_LIST' || childName === 'CONNECT_DATA') {
236
+ break;
237
+ }
238
+ pos++;
239
+ }
240
+ list.splice(pos, 0, node);
241
+ invalidateChildLookup(description);
242
+ }
243
+
244
+ /**
245
+ * Applies overrides from the options object to the connectString.
246
+ * Handles NVPair format, TNS alias resolution, and updates the options.connectString in place.
247
+ * Only adds parameters if not already present in the descriptor.
248
+ * Supports EZConnect strings, TNS aliases (via tnsnames.ora), and full descriptors.
249
+ *
250
+ * @param {Object} options - The connection options object containing connectString and override parameters.
251
+ * @example
252
+ * // Example with full descriptor
253
+ * const options = {
254
+ * connectString: '(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=host)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=service)))',
255
+ * sdu: 8192,
256
+ * retryCount: 3
257
+ * };
258
+ * await applyConnectStringOverrides(options);
259
+ * // options.connectString now includes (SDU=8192) and (RETRY_COUNT=3) if not already present
260
+ *
261
+ * // Example with TNS alias
262
+ * const options = {
263
+ * connectString: 'mydb',
264
+ * configDir: '/path/to/network/admin',
265
+ * sdu: 8192
266
+ * };
267
+ * await applyConnectStringOverrides(options);
268
+ * // Resolves 'mydb' from tnsnames.ora and applies SDU if not present
269
+ *
270
+ * // Example with EZConnect
271
+ * const options = {
272
+ * connectString: 'tcps://host:1521/service',
273
+ * walletLocation: '/path/to/wallet'
274
+ * };
275
+ * await applyConnectStringOverrides(options);
276
+ * // Converts to descriptor and adds (SECURITY=(MY_WALLET_DIRECTORY=/path/to/wallet))
277
+ */
278
+ async function applyConnectStringOverrides(options) {
279
+ const connectString = options.connectString;
280
+ const trimmed = connectString.trim();
281
+ let root;
282
+ if (trimmed.startsWith('(')) {
283
+ // Parse direct NVPair format
284
+ root = createNVPair(trimmed);
285
+ } else {
286
+ const configDir = options.configDir || process.env.TNS_ADMIN || '';
287
+ const resolved = await resolveConnectStr(trimmed, configDir);
288
+ root = (typeof resolved === 'string') ? createNVPair(resolved) : resolved;
289
+ }
290
+
291
+ const descriptions = collectDescriptions(root);
292
+ if (!descriptions.length) return;
293
+
294
+ const configured = [];
295
+ for (const def of CONNECT_OVERRIDES) {
296
+ const raw = options[def.opt];
297
+ if (raw === undefined) {
298
+ continue;
299
+ }
300
+ // Treat walletLocation=SYSTEM as "use system certs" -> do not inject into descriptor
301
+ if (def.opt === 'walletLocation' && typeof raw === 'string' && raw.trim().toUpperCase() === 'SYSTEM') {
302
+ continue;
303
+ }
304
+ const formatter = def.fmt || (v => v);
305
+ const value = formatter(raw);
306
+ configured.push({ def, value });
307
+ }
308
+
309
+ if (!configured.length) return;
310
+
311
+ for (const description of descriptions) {
312
+ for (const entry of configured) {
313
+ const def = entry.def;
314
+ const value = entry.value;
315
+ const { section, perAddress } = def;
316
+ if (perAddress) {
317
+ const addresses = findChildren(description, section[1]);
318
+ if (!addresses.length) continue;
319
+ for (const addr of addresses) {
320
+ const existingNode = findFirstChild(addr, section[2]);
321
+ if (!existingNode) {
322
+ ensureNode(addr, section[2]).setAtom = value;
323
+ }
324
+ }
325
+ continue;
326
+ }
327
+ let node = description;
328
+ let existing = false;
329
+ for (let k = 1; k < section.length; k++) {
330
+ const child = findFirstChild(node, section[k]);
331
+ if (!child) break;
332
+ node = child;
333
+ if (k === section.length - 1) existing = true;
334
+ }
335
+ if (!existing) {
336
+ for (let k = 1; k < section.length; k++) {
337
+ node = ensureNode(node, section[k]);
338
+ }
339
+ node.setAtom = value;
340
+ if (section.length === RHS_LIST && section[0] === 'DESCRIPTION') {
341
+ placeDescriptionParam(description, node);
342
+ }
343
+ }
344
+ }
345
+ }
346
+
347
+ options.connectString = root.toString();
348
+ }
349
+
350
+ /**
351
+ * Merges extended parameters from the EZConnect resolver into the options object.
352
+ * Only processes keys starting with 'njs.' and coerces values based on predefined sets.
353
+ * @param {Object} options - The options object to merge into.
354
+ * @param {Object} resolver - The EZConnect resolver containing connection properties.
355
+ * @returns {Object} The updated options object.
356
+ * @example
357
+ * const resolver = new EZConnectResolver('tcp://host:port/service?njs.stmtCacheSize=10&njs.events=true');
358
+ * const options = {};
359
+ * const merged = mergeExtendedParams(options, resolver);
360
+ * // merged = { stmtCacheSize: 10, events: true }
361
+ */
362
+ function mergeExtendedParams(options, resolver) {
363
+ for (const [rawKey, rawValue] of resolver.connectionProps.entries()) {
364
+ let key = rawKey;
365
+ if (key.startsWith(NJS_PREFIX)) {
366
+ key = key.slice(NJS_PREFIX.length);
367
+ } else {
368
+ continue;
369
+ }
370
+ let value = rawValue;
371
+ if (NUMBER_KEYS.has(key)) {
372
+ value = Number(rawValue);
373
+ if (isNaN(value) || !Number.isInteger(value)) continue;
374
+ } else if (BOOLEAN_KEYS.has(key)) {
375
+ const lower = rawValue.toLowerCase();
376
+ value = (lower === 'true' || lower === 'on' || lower === 'yes');
377
+ } else if (STRING_KEYS.has(key)) {
378
+ value = rawValue;
379
+ } else {
380
+ continue;
381
+ }
382
+ options[key] = value;
383
+ }
384
+ return options;
385
+ }
386
+
387
+ /**
388
+ * Resolves an EZConnect string to a connect descriptor.
389
+ * @param {string} connStr - The EZConnect string.
390
+ * @returns {Object} Object containing connectString and resolver.
391
+ * @example
392
+ * const result = getConnectDescriptorFromEZConnect('tcp://host:1521/service');
393
+ * // result = { connectString: '(DESCRIPTION=...)' , resolver: EZConnectResolver instance }
394
+ */
395
+ function getConnectDescriptorFromEZConnect(connStr) {
396
+ if (connStr && ((connStr.indexOf(')') == -1) || (connStr.indexOf('(') != 0))) {
397
+ if (connStr.indexOf(':') !== -1 || connStr.indexOf('/') !== -1) {
398
+ const resolver = new EZConnectResolver(connStr);
399
+ return { connectString: resolver.getResolvedUrl(), resolver };
400
+ }
401
+ }
402
+ return { connectString: connStr, resolver: null };
403
+ }
404
+
405
+ /**
406
+ * Resolves a connect string, handling EZConnect or TNS aliases.
407
+ * @param {string} connectString - The connect string to resolve.
408
+ * @param {string} configDir - Directory for tnsnames.ora.
409
+ * @returns {Promise<Object|string>} Resolved connect descriptor or string.
410
+ * @example
411
+ * const resolved = await resolveConnectStr('mydb', '/path/to/network/admin');
412
+ * // Resolves 'mydb' from tnsnames.ora in the given directory
413
+ */
414
+ async function resolveConnectStr(connectString, configDir) {
415
+ const connStr = connectString.trim();
416
+ let resolvedVal = connStr;
417
+ if ((connStr.indexOf(')') === -1) || (connStr.indexOf('(') != 0)) {
418
+ if ((connStr.indexOf(':') != -1) || (connStr.indexOf('/') != -1)) {
419
+ const ezcnObj = new EZConnectResolver(connStr);
420
+ resolvedVal = ezcnObj.getResolvedUrl();
421
+ return resolvedVal;
422
+ } else {
423
+ //try tns alias
424
+ const namesFilePath = tnsnamesFilePath(configDir);
425
+ const p = await nlParamParser.initializeNlpa(namesFilePath);
426
+ resolvedVal = p.get(connStr.toUpperCase());
427
+ if (!resolvedVal)
428
+ errors.throwErr(errors.ERR_TNS_ENTRY_NOT_FOUND, connStr, configDir ? configDir + '/tnsnames.ora' : process.env.TNS_ADMIN + '/tnsnames.ora');
429
+ if (resolvedVal.rhsType == 1) {
430
+ const rString = resolvedVal.atom;
431
+ if ((rString.indexOf(':') != -1) || (rString.indexOf('/') != -1)) {
432
+ return new EZConnectResolver(rString).getResolvedUrl();
433
+ }
434
+ }
435
+ resolvedVal = resolvedVal.getListElement(0);
436
+ }
437
+
438
+ }
439
+ return resolvedVal;
440
+ }
441
+
442
+ module.exports = {
443
+ applyConnectStringOverrides,
444
+ mergeExtendedParams,
445
+ getConnectDescriptorFromEZConnect,
446
+ resolveConnectStr
447
+ };
@@ -287,7 +287,7 @@ class ResultSetImpl extends BaseImpl {
287
287
  const fetchArraySize = 100;
288
288
  // fetch all rows
289
289
  let rowsFetched = [];
290
- while (true) { // eslint-disable-line
290
+ while (true) {
291
291
  // constant default value for fetchArraySize
292
292
  const rows = await this.getRows(fetchArraySize, {});
293
293
  if (rows) {
package/lib/lob.js CHANGED
@@ -117,12 +117,6 @@ class Lob extends Duplex {
117
117
  // called to associate a LOB implementation with this user facing object
118
118
  _setup(lobImpl, autoCloseLob) {
119
119
  this._impl = lobImpl;
120
- // chunk size is not defined for BFILE LOBs
121
- if (this.type !== types.DB_TYPE_BFILE) {
122
- this._chunkSize = lobImpl.getChunkSize();
123
- }
124
- this._pieceSize = lobImpl.getPieceSize();
125
- this._length = lobImpl.getLength();
126
120
  this._type = lobImpl.getType();
127
121
  if (typeof this._type === 'number') {
128
122
  this._type = types.getTypeByNum(this._type);
@@ -170,10 +164,11 @@ class Lob extends Duplex {
170
164
  // chunkSize
171
165
  //
172
166
  // Property for the chunk size of the LOB.
167
+ // returns undefined if the LOB is closed.
173
168
  //---------------------------------------------------------------------------
174
169
  get chunkSize() {
175
170
  this._checkNotBfile();
176
- return this._chunkSize;
171
+ return this._impl?.getChunkSize();
177
172
  }
178
173
 
179
174
  //---------------------------------------------------------------------------
@@ -218,12 +213,12 @@ class Lob extends Duplex {
218
213
  if (offset === undefined) {
219
214
  offset = 1;
220
215
  } else {
221
- errors.assertParamValue(Number.isInteger(offset) && offset > 0, 1);
216
+ errors.assertParamValue(Number.isSafeInteger(offset) && offset > 0, 1);
222
217
  }
223
218
  if (amount === undefined) {
224
219
  amount = 0;
225
220
  } else {
226
- errors.assertParamValue(Number.isInteger(amount) && amount > 0, 2);
221
+ errors.assertParamValue(Number.isSafeInteger(amount) && amount > 0, 2);
227
222
  }
228
223
  errors.assert(this._impl, errors.ERR_INVALID_LOB);
229
224
  return await this._impl.getData(offset, amount);
@@ -257,6 +252,22 @@ class Lob extends Duplex {
257
252
  this._impl.setDirFileName(a1);
258
253
  }
259
254
 
255
+ //---------------------------------------------------------------------------
256
+ // trim()
257
+ // trims non-BFILE LOBs to a smaller size as specified by the parameter.
258
+ //---------------------------------------------------------------------------
259
+ async trim(newSize) {
260
+ this._checkNotBfile();
261
+ errors.assertArgCount(arguments, 0, 1);
262
+ if (newSize === undefined) {
263
+ newSize = 0;
264
+ } else {
265
+ errors.assertParamValue(Number.isInteger(newSize) && newSize >= 0 &&
266
+ newSize < 2 ** 32, 1);
267
+ }
268
+ await this._impl.trim(newSize);
269
+ }
270
+
260
271
  //---------------------------------------------------------------------------
261
272
  // fileExists
262
273
  //
@@ -271,9 +282,10 @@ class Lob extends Duplex {
271
282
  // length
272
283
  //
273
284
  // Property for the length of the LOB.
285
+ // returns undefined if the LOB is closed.
274
286
  //---------------------------------------------------------------------------
275
287
  get length() {
276
- return this._length;
288
+ return this._impl?.getLength();
277
289
  }
278
290
 
279
291
  //---------------------------------------------------------------------------
@@ -281,16 +293,16 @@ class Lob extends Duplex {
281
293
  //
282
294
  // Property for the size to use for each piece that is transferred when
283
295
  // reading from the LOB.
296
+ // returns undefined if the LOB is closed.
284
297
  //---------------------------------------------------------------------------
285
298
  get pieceSize() {
286
- return this._pieceSize;
299
+ return this._impl?.getPieceSize();
287
300
  }
288
301
 
289
302
  set pieceSize(value) {
290
- errors.assertPropValue(Number.isInteger(value) && value >= 0, "pieceSize");
303
+ errors.assertPropValue(Number.isSafeInteger(value) && value >= 0, "pieceSize");
291
304
  errors.assert(this._impl, errors.ERR_INVALID_LOB);
292
305
  this._impl.setPieceSize(value);
293
- this._pieceSize = value;
294
306
  }
295
307
 
296
308
  //---------------------------------------------------------------------------
@@ -307,7 +319,8 @@ class Lob extends Duplex {
307
319
  nodbUtil.wrapFns(Lob.prototype, errors.ERR_BUSY_LOB,
308
320
  "close",
309
321
  "fileExists",
310
- "getData");
322
+ "getData",
323
+ "trim");
311
324
  Lob.prototype._serializedRead = nodbUtil.wrapFn(Lob.prototype._readData, true);
312
325
  Lob.prototype._serializedWrite =
313
326
  nodbUtil.wrapFn(Lob.prototype._writeData, true);
@@ -0,0 +1,97 @@
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 { Buffer } = require('buffer');
30
+ const crypto = require('crypto');
31
+
32
+ class ObfuscatedValue {
33
+
34
+ constructor(value, options = {}) {
35
+ this._valueBuffer = null;
36
+ this._maskBuffer = null;
37
+ this._defaultReturnBuffer =
38
+ options.returnBuffer ??
39
+ (value !== undefined && value !== null && Buffer.isBuffer(value));
40
+ if (value !== undefined && value !== null) {
41
+ this.set(value, options);
42
+ }
43
+ }
44
+
45
+ set(value, options = {}) {
46
+ if (value === undefined || value === null) {
47
+ this.clear();
48
+ return;
49
+ }
50
+ const returnBuffer = options.returnBuffer ??
51
+ (Buffer.isBuffer(value) || this._defaultReturnBuffer === true);
52
+ const source = Buffer.isBuffer(value)
53
+ ? Buffer.from(value)
54
+ : Buffer.from(String(value));
55
+ const mask = crypto.randomBytes(source.length);
56
+ for (let i = 0; i < source.length; i++) {
57
+ source[i] = source[i] ^ mask[i];
58
+ }
59
+ this.clear();
60
+ this._valueBuffer = source;
61
+ this._maskBuffer = mask;
62
+ this._defaultReturnBuffer = returnBuffer;
63
+ }
64
+
65
+ get(options = {}) {
66
+ if (!this._valueBuffer || !this._maskBuffer) {
67
+ return null;
68
+ }
69
+ const returnBuffer = options.returnBuffer ??
70
+ (this._defaultReturnBuffer === true);
71
+ const result = Buffer.allocUnsafe(this._valueBuffer.length);
72
+ for (let i = 0; i < this._valueBuffer.length; i++) {
73
+ result[i] = this._valueBuffer[i] ^ this._maskBuffer[i];
74
+ }
75
+ if (returnBuffer) {
76
+ return result;
77
+ }
78
+ const text = result.toString();
79
+ result.fill(0);
80
+ return text;
81
+ }
82
+
83
+ clear() {
84
+ if (this._valueBuffer) {
85
+ this._valueBuffer.fill(0);
86
+ this._valueBuffer = null;
87
+ }
88
+ if (this._maskBuffer) {
89
+ this._maskBuffer.fill(0);
90
+ this._maskBuffer = null;
91
+ }
92
+ }
93
+ }
94
+
95
+ module.exports = {
96
+ ObfuscatedValue
97
+ };