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
|
@@ -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
|
+
};
|
package/lib/impl/resultset.js
CHANGED
|
@@ -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) {
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
299
|
+
return this._impl?.getPieceSize();
|
|
287
300
|
}
|
|
288
301
|
|
|
289
302
|
set pieceSize(value) {
|
|
290
|
-
errors.assertPropValue(Number.
|
|
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
|
+
};
|