oracledb 6.9.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.
- package/LICENSE.txt +1 -1
- package/NOTICE.txt +1 -1
- package/README.md +9 -9
- package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-7.0.0-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
- package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
- package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +1 -0
- package/eslint.config.js +39 -0
- package/lib/aqDeqOptions.js +18 -1
- package/lib/aqQueue.js +10 -6
- package/lib/connection.js +418 -39
- package/lib/dbObject.js +25 -4
- package/lib/endUserSecurityContext.js +176 -0
- package/lib/errors.js +99 -8
- package/lib/impl/aqDeqOptions.js +10 -1
- package/lib/impl/connection.js +76 -2
- package/lib/impl/datahandlers/buffer.js +68 -5
- package/lib/impl/datahandlers/oson.js +7 -2
- package/lib/impl/datahandlers/vector.js +25 -5
- package/lib/impl/lob.js +9 -0
- package/lib/impl/parserHelpers.js +443 -0
- package/lib/impl/resultset.js +1 -1
- package/lib/impl/sodaDocument.js +5 -14
- package/lib/lob.js +27 -14
- package/lib/obfuscation.js +97 -0
- package/lib/oracledb.js +163 -49
- package/lib/parser.js +204 -0
- package/lib/pipeline.js +113 -0
- package/lib/pool.js +203 -147
- package/lib/poolStatistics.js +1 -1
- package/lib/resultset.js +42 -21
- package/lib/settings.js +1 -0
- package/lib/sodaCollection.js +12 -8
- package/lib/sodaDatabase.js +35 -8
- package/lib/sodaDocument.js +24 -6
- package/lib/sodaOperation.js +7 -6
- package/lib/thin/aq.js +475 -0
- package/lib/thin/connection.js +518 -54
- package/lib/thin/dbObject.js +7 -1
- package/lib/thin/lob.js +20 -1
- package/lib/thin/pool.js +42 -62
- package/lib/thin/protocol/capabilities.js +21 -4
- package/lib/thin/protocol/constants.js +143 -2
- package/lib/thin/protocol/encryptDecrypt.js +45 -10
- package/lib/thin/protocol/messages/aqArray.js +238 -0
- package/lib/thin/protocol/messages/aqBase.js +305 -0
- package/lib/thin/protocol/messages/aqDeq.js +164 -0
- package/lib/thin/protocol/messages/aqEnq.js +135 -0
- package/lib/thin/protocol/messages/auth.js +18 -28
- package/lib/thin/protocol/messages/base.js +245 -4
- 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 -142
- package/lib/thin/protocol/packet.js +120 -28
- 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 +2 -2
- package/lib/thin/sqlnet/networkSession.js +63 -41
- package/lib/thin/sqlnet/ntTcp.js +24 -0
- package/lib/thin/sqlnet/nvStrToNvPair.js +50 -25
- package/lib/thin/sqlnet/packet.js +25 -4
- package/lib/thin/sqlnet/paramParser.js +10 -7
- package/lib/thin/statementCache.js +2 -4
- package/lib/traceHandler.js +59 -2
- package/lib/transformer.js +8 -3
- package/lib/types.js +36 -22
- package/lib/util.js +6 -19
- package/lib/version.js +3 -3
- 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 +7 -5
- package/plugins/token/extensionAzure/index.js +0 -1
- package/plugins/token/extensionOci/index.js +0 -1
- package/build/Release/oracledb-6.9.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-6.9.0-darwin-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.9.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.9.0-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.9.0-js-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.9.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.9.0-linux-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.9.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.9.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.9.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.9.0-win32-x64.node-buildinfo.txt +0 -1
|
@@ -372,12 +372,23 @@ class BaseBuffer {
|
|
|
372
372
|
//
|
|
373
373
|
// Reads a boolean value from the buffer and returns a Boolean.
|
|
374
374
|
//---------------------------------------------------------------------------
|
|
375
|
-
readBool() {
|
|
375
|
+
readBool(fromDBObject) {
|
|
376
376
|
const buf = this.readBytesWithLength();
|
|
377
377
|
if (!buf) {
|
|
378
378
|
return null;
|
|
379
379
|
}
|
|
380
|
-
|
|
380
|
+
if (fromDBObject) {
|
|
381
|
+
// inside a database object, the boolean is represented as a
|
|
382
|
+
// big endian 32-bit integer (either 0 or 1)
|
|
383
|
+
return (buf[buf.length - 1] === 1);
|
|
384
|
+
} else {
|
|
385
|
+
// outside a database object, the boolean is represented as an
|
|
386
|
+
// encoded integer (0x00 for false and 0x0101 for true); older
|
|
387
|
+
// versions like 12.1 also return 0x8101 (-1) for null values
|
|
388
|
+
// but this can safely be ignored as the null indicator is sent
|
|
389
|
+
// as a separate field afterwards.
|
|
390
|
+
return (buf[0] === 1);
|
|
391
|
+
}
|
|
381
392
|
}
|
|
382
393
|
|
|
383
394
|
//---------------------------------------------------------------------------
|
|
@@ -397,6 +408,18 @@ class BaseBuffer {
|
|
|
397
408
|
return buf;
|
|
398
409
|
}
|
|
399
410
|
|
|
411
|
+
//---------------------------------------------------------------------------
|
|
412
|
+
// readBytesAndLength()
|
|
413
|
+
//
|
|
414
|
+
// Reads the UB4 length from the buffer and then returns a Buffer containing
|
|
415
|
+
// the specified number of bytes.
|
|
416
|
+
//---------------------------------------------------------------------------
|
|
417
|
+
readBytesAndLength() {
|
|
418
|
+
const numBytes = this.readUB4();
|
|
419
|
+
if (numBytes > 0)
|
|
420
|
+
return this.readBytesWithLength();
|
|
421
|
+
}
|
|
422
|
+
|
|
400
423
|
//---------------------------------------------------------------------------
|
|
401
424
|
// readBytesWithLength()
|
|
402
425
|
//
|
|
@@ -548,6 +571,17 @@ class BaseBuffer {
|
|
|
548
571
|
return Buffer.from(buf).swap16().toString('utf16le');
|
|
549
572
|
}
|
|
550
573
|
|
|
574
|
+
//---------------------------------------------------------------------------
|
|
575
|
+
// readStrAndLength()
|
|
576
|
+
//
|
|
577
|
+
// Reads a length from the buffer and then, if the length is non-zero, reads
|
|
578
|
+
// string from the buffer and returns it.
|
|
579
|
+
//---------------------------------------------------------------------------
|
|
580
|
+
readStrAndLength() {
|
|
581
|
+
const buf = this.readBytesAndLength();
|
|
582
|
+
return buf ? buf.toString() : null;
|
|
583
|
+
}
|
|
584
|
+
|
|
551
585
|
//---------------------------------------------------------------------------
|
|
552
586
|
// readUB2()
|
|
553
587
|
//
|
|
@@ -897,7 +931,7 @@ class BaseBuffer {
|
|
|
897
931
|
//
|
|
898
932
|
// Writes the number (in string form) in Oracle Number format to the buffer.
|
|
899
933
|
//---------------------------------------------------------------------------
|
|
900
|
-
writeOracleNumber(value) {
|
|
934
|
+
writeOracleNumber(value, writeLength = true) {
|
|
901
935
|
|
|
902
936
|
// determine if number is negative
|
|
903
937
|
let isNegative = false;
|
|
@@ -961,8 +995,11 @@ class BaseBuffer {
|
|
|
961
995
|
} else if (value.length === 0 && exponent === 0) {
|
|
962
996
|
exponentOnWire = 128;
|
|
963
997
|
}
|
|
964
|
-
let pos = this.reserveBytes(numPairs +
|
|
965
|
-
|
|
998
|
+
let pos = this.reserveBytes(numPairs + 1 + writeLength + appendSentinel);
|
|
999
|
+
|
|
1000
|
+
if (writeLength) {
|
|
1001
|
+
this.buf[pos++] = numPairs + 1 + appendSentinel;
|
|
1002
|
+
}
|
|
966
1003
|
this.buf[pos++] = exponentOnWire;
|
|
967
1004
|
for (let i = 0; i < value.length; i += 2) {
|
|
968
1005
|
const base100Digit = Number(value.substring(i, i + 2));
|
|
@@ -997,6 +1034,32 @@ class BaseBuffer {
|
|
|
997
1034
|
this.buf.writeInt32BE(n, pos);
|
|
998
1035
|
}
|
|
999
1036
|
|
|
1037
|
+
//---------------------------------------------------------------------------
|
|
1038
|
+
// writeSB4()
|
|
1039
|
+
//
|
|
1040
|
+
// Writes a signed integer (up to 4 bytes in length) in variable length
|
|
1041
|
+
// format to the buffer.
|
|
1042
|
+
//---------------------------------------------------------------------------
|
|
1043
|
+
writeSB4(value) {
|
|
1044
|
+
let sign = 0;
|
|
1045
|
+
if (value < 0) {
|
|
1046
|
+
value = -value;
|
|
1047
|
+
sign = 0x80;
|
|
1048
|
+
}
|
|
1049
|
+
if (value === 0) {
|
|
1050
|
+
this.writeUInt8(0);
|
|
1051
|
+
} else if (value <= 0xff) {
|
|
1052
|
+
this.writeUInt8(1 | sign);
|
|
1053
|
+
this.writeUInt8(value);
|
|
1054
|
+
} else if (value <= 0xffff) {
|
|
1055
|
+
this.writeUInt8(2 | sign);
|
|
1056
|
+
this.writeUInt16BE(value);
|
|
1057
|
+
} else {
|
|
1058
|
+
this.writeUInt8(4 | sign);
|
|
1059
|
+
this.writeUInt32BE(value);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1000
1063
|
//---------------------------------------------------------------------------
|
|
1001
1064
|
// writeUB4()
|
|
1002
1065
|
//
|
|
@@ -549,8 +549,13 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
549
549
|
|
|
550
550
|
// handle numbers
|
|
551
551
|
} else if (typeof value === 'number') {
|
|
552
|
-
|
|
553
|
-
|
|
552
|
+
if (Number.isNaN(value)) {
|
|
553
|
+
this.writeUInt8(constants.TNS_JSON_TYPE_BINARY_DOUBLE);
|
|
554
|
+
this.writeBinaryDouble(value);
|
|
555
|
+
} else {
|
|
556
|
+
this.writeUInt8(constants.TNS_JSON_TYPE_NUMBER_LENGTH_UINT8);
|
|
557
|
+
this.writeOracleNumber(value.toString());
|
|
558
|
+
}
|
|
554
559
|
|
|
555
560
|
// handle strings
|
|
556
561
|
} else if (typeof value === 'string') {
|
|
@@ -37,6 +37,21 @@ const types = require("../../types.js");
|
|
|
37
37
|
*/
|
|
38
38
|
class VectorDecoder extends BaseBuffer {
|
|
39
39
|
|
|
40
|
+
//---------------------------------------------------------------------------
|
|
41
|
+
// _verifyVectorDataSize()
|
|
42
|
+
//
|
|
43
|
+
// Verifies that the VECTOR image contains enough bytes for the declared
|
|
44
|
+
// element count before allocating a typed array.
|
|
45
|
+
//---------------------------------------------------------------------------
|
|
46
|
+
_verifyVectorDataSize(numElements, elementSize) {
|
|
47
|
+
const numBytes = numElements * elementSize;
|
|
48
|
+
const numBytesLeft = this.numBytesLeft();
|
|
49
|
+
if (numBytes > numBytesLeft) {
|
|
50
|
+
errors.throwErr(errors.ERR_UNEXPECTED_END_OF_DATA, numBytes,
|
|
51
|
+
numBytesLeft);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
//---------------------------------------------------------------------------
|
|
41
56
|
// decode()
|
|
42
57
|
//
|
|
@@ -56,7 +71,7 @@ class VectorDecoder extends BaseBuffer {
|
|
|
56
71
|
const flags = this.readUInt16BE();
|
|
57
72
|
const vectorFormat = this.readUInt8();
|
|
58
73
|
let numElements = this.readUInt32BE();
|
|
59
|
-
let
|
|
74
|
+
let arrayType, elementSize, sparseValue;
|
|
60
75
|
|
|
61
76
|
// For binary vector, NORM will never be present but space for NORM is reserved to keep
|
|
62
77
|
// same header lengh across the different vector formats.
|
|
@@ -69,6 +84,7 @@ class VectorDecoder extends BaseBuffer {
|
|
|
69
84
|
sparseValue = {};
|
|
70
85
|
sparseValue.numDimensions = numElements; // vector dimensions.
|
|
71
86
|
numElements = this.readUInt16BE(); // actual elements.
|
|
87
|
+
this._verifyVectorDataSize(numElements, 4);
|
|
72
88
|
sparseValue.indices = new Uint32Array(numElements);
|
|
73
89
|
for (let i = 0; i < numElements; i++) {
|
|
74
90
|
sparseValue.indices[i] = this.readUInt32BE();
|
|
@@ -76,22 +92,26 @@ class VectorDecoder extends BaseBuffer {
|
|
|
76
92
|
}
|
|
77
93
|
|
|
78
94
|
if (vectorFormat === constants.VECTOR_FORMAT_FLOAT32) {
|
|
95
|
+
arrayType = Float32Array;
|
|
79
96
|
elementSize = 4;
|
|
80
|
-
value = new Float32Array(numElements);
|
|
81
97
|
} else if (vectorFormat === constants.VECTOR_FORMAT_FLOAT64) {
|
|
98
|
+
arrayType = Float64Array;
|
|
82
99
|
elementSize = 8;
|
|
83
|
-
value = new Float64Array(numElements);
|
|
84
100
|
} else if (vectorFormat === constants.VECTOR_FORMAT_INT8) {
|
|
101
|
+
arrayType = Int8Array;
|
|
85
102
|
elementSize = 1;
|
|
86
|
-
value = new Int8Array(numElements);
|
|
87
103
|
} else if (vectorFormat === constants.VECTOR_FORMAT_BINARY) {
|
|
104
|
+
arrayType = Uint8Array;
|
|
88
105
|
elementSize = 1;
|
|
89
106
|
// The number of dimensions are assumed to be multiple of 8.
|
|
107
|
+
if (numElements % 8 !== 0)
|
|
108
|
+
errors.throwErr(errors.ERR_UNEXPECTED_DATA, numElements);
|
|
90
109
|
numElements = numElements / 8;
|
|
91
|
-
value = new Uint8Array(numElements);
|
|
92
110
|
} else {
|
|
93
111
|
errors.throwErr(errors.ERR_VECTOR_FORMAT_NOT_SUPPORTED, vectorFormat);
|
|
94
112
|
}
|
|
113
|
+
this._verifyVectorDataSize(numElements, elementSize);
|
|
114
|
+
const value = new arrayType(numElements);
|
|
95
115
|
|
|
96
116
|
if (sparseFormat) {
|
|
97
117
|
sparseValue.values = value;
|
package/lib/impl/lob.js
CHANGED
|
@@ -81,6 +81,15 @@ class LobImpl extends BaseImpl {
|
|
|
81
81
|
errors.throwNotImplemented("getting directory and filename of LOB(BFILE)");
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
//---------------------------------------------------------------------------
|
|
85
|
+
// trim()
|
|
86
|
+
//
|
|
87
|
+
// trims non-BFILE LOBs to a smaller size.
|
|
88
|
+
//----------------------------------------------------------------------------
|
|
89
|
+
trim() {
|
|
90
|
+
errors.throwNotImplemented("trimming LOBs");
|
|
91
|
+
}
|
|
92
|
+
|
|
84
93
|
//---------------------------------------------------------------------------
|
|
85
94
|
// read()
|
|
86
95
|
//
|
|
@@ -0,0 +1,443 @@
|
|
|
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
|
+
const formatter = def.fmt || (v => v);
|
|
301
|
+
const value = formatter(raw);
|
|
302
|
+
configured.push({ def, value });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!configured.length) return;
|
|
306
|
+
|
|
307
|
+
for (const description of descriptions) {
|
|
308
|
+
for (const entry of configured) {
|
|
309
|
+
const def = entry.def;
|
|
310
|
+
const value = entry.value;
|
|
311
|
+
const { section, perAddress } = def;
|
|
312
|
+
if (perAddress) {
|
|
313
|
+
const addresses = findChildren(description, section[1]);
|
|
314
|
+
if (!addresses.length) continue;
|
|
315
|
+
for (const addr of addresses) {
|
|
316
|
+
const existingNode = findFirstChild(addr, section[2]);
|
|
317
|
+
if (!existingNode) {
|
|
318
|
+
ensureNode(addr, section[2]).setAtom = value;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
let node = description;
|
|
324
|
+
let existing = false;
|
|
325
|
+
for (let k = 1; k < section.length; k++) {
|
|
326
|
+
const child = findFirstChild(node, section[k]);
|
|
327
|
+
if (!child) break;
|
|
328
|
+
node = child;
|
|
329
|
+
if (k === section.length - 1) existing = true;
|
|
330
|
+
}
|
|
331
|
+
if (!existing) {
|
|
332
|
+
for (let k = 1; k < section.length; k++) {
|
|
333
|
+
node = ensureNode(node, section[k]);
|
|
334
|
+
}
|
|
335
|
+
node.setAtom = value;
|
|
336
|
+
if (section.length === RHS_LIST && section[0] === 'DESCRIPTION') {
|
|
337
|
+
placeDescriptionParam(description, node);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
options.connectString = root.toString();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Merges extended parameters from the EZConnect resolver into the options object.
|
|
348
|
+
* Only processes keys starting with 'njs.' and coerces values based on predefined sets.
|
|
349
|
+
* @param {Object} options - The options object to merge into.
|
|
350
|
+
* @param {Object} resolver - The EZConnect resolver containing connection properties.
|
|
351
|
+
* @returns {Object} The updated options object.
|
|
352
|
+
* @example
|
|
353
|
+
* const resolver = new EZConnectResolver('tcp://host:port/service?njs.stmtCacheSize=10&njs.events=true');
|
|
354
|
+
* const options = {};
|
|
355
|
+
* const merged = mergeExtendedParams(options, resolver);
|
|
356
|
+
* // merged = { stmtCacheSize: 10, events: true }
|
|
357
|
+
*/
|
|
358
|
+
function mergeExtendedParams(options, resolver) {
|
|
359
|
+
for (const [rawKey, rawValue] of resolver.connectionProps.entries()) {
|
|
360
|
+
let key = rawKey;
|
|
361
|
+
if (key.startsWith(NJS_PREFIX)) {
|
|
362
|
+
key = key.slice(NJS_PREFIX.length);
|
|
363
|
+
} else {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
let value = rawValue;
|
|
367
|
+
if (NUMBER_KEYS.has(key)) {
|
|
368
|
+
value = Number(rawValue);
|
|
369
|
+
if (isNaN(value) || !Number.isInteger(value)) continue;
|
|
370
|
+
} else if (BOOLEAN_KEYS.has(key)) {
|
|
371
|
+
const lower = rawValue.toLowerCase();
|
|
372
|
+
value = (lower === 'true' || lower === 'on' || lower === 'yes');
|
|
373
|
+
} else if (STRING_KEYS.has(key)) {
|
|
374
|
+
value = rawValue;
|
|
375
|
+
} else {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
options[key] = value;
|
|
379
|
+
}
|
|
380
|
+
return options;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Resolves an EZConnect string to a connect descriptor.
|
|
385
|
+
* @param {string} connStr - The EZConnect string.
|
|
386
|
+
* @returns {Object} Object containing connectString and resolver.
|
|
387
|
+
* @example
|
|
388
|
+
* const result = getConnectDescriptorFromEZConnect('tcp://host:1521/service');
|
|
389
|
+
* // result = { connectString: '(DESCRIPTION=...)' , resolver: EZConnectResolver instance }
|
|
390
|
+
*/
|
|
391
|
+
function getConnectDescriptorFromEZConnect(connStr) {
|
|
392
|
+
if (connStr && ((connStr.indexOf(')') == -1) || (connStr.indexOf('(') != 0))) {
|
|
393
|
+
if (connStr.indexOf(':') !== -1 || connStr.indexOf('/') !== -1) {
|
|
394
|
+
const resolver = new EZConnectResolver(connStr);
|
|
395
|
+
return { connectString: resolver.getResolvedUrl(), resolver };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return { connectString: connStr, resolver: null };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Resolves a connect string, handling EZConnect or TNS aliases.
|
|
403
|
+
* @param {string} connectString - The connect string to resolve.
|
|
404
|
+
* @param {string} configDir - Directory for tnsnames.ora.
|
|
405
|
+
* @returns {Promise<Object|string>} Resolved connect descriptor or string.
|
|
406
|
+
* @example
|
|
407
|
+
* const resolved = await resolveConnectStr('mydb', '/path/to/network/admin');
|
|
408
|
+
* // Resolves 'mydb' from tnsnames.ora in the given directory
|
|
409
|
+
*/
|
|
410
|
+
async function resolveConnectStr(connectString, configDir) {
|
|
411
|
+
const connStr = connectString.trim();
|
|
412
|
+
let resolvedVal = connStr;
|
|
413
|
+
if ((connStr.indexOf(')') === -1) || (connStr.indexOf('(') != 0)) {
|
|
414
|
+
if ((connStr.indexOf(':') != -1) || (connStr.indexOf('/') != -1)) {
|
|
415
|
+
const ezcnObj = new EZConnectResolver(connStr);
|
|
416
|
+
resolvedVal = ezcnObj.getResolvedUrl();
|
|
417
|
+
return resolvedVal;
|
|
418
|
+
} else {
|
|
419
|
+
//try tns alias
|
|
420
|
+
const namesFilePath = tnsnamesFilePath(configDir);
|
|
421
|
+
const p = await nlParamParser.initializeNlpa(namesFilePath);
|
|
422
|
+
resolvedVal = p.get(connStr.toUpperCase());
|
|
423
|
+
if (!resolvedVal)
|
|
424
|
+
errors.throwErr(errors.ERR_TNS_ENTRY_NOT_FOUND, connStr, configDir ? configDir + '/tnsnames.ora' : process.env.TNS_ADMIN + '/tnsnames.ora');
|
|
425
|
+
if (resolvedVal.rhsType == 1) {
|
|
426
|
+
const rString = resolvedVal.atom;
|
|
427
|
+
if ((rString.indexOf(':') != -1) || (rString.indexOf('/') != -1)) {
|
|
428
|
+
return new EZConnectResolver(rString).getResolvedUrl();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
resolvedVal = resolvedVal.getListElement(0);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
}
|
|
435
|
+
return resolvedVal;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
module.exports = {
|
|
439
|
+
applyConnectStringOverrides,
|
|
440
|
+
mergeExtendedParams,
|
|
441
|
+
getConnectDescriptorFromEZConnect,
|
|
442
|
+
resolveConnectStr
|
|
443
|
+
};
|
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) {
|