oracledb 6.5.0 → 6.6.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/README.md +2 -2
- package/build/Release/oracledb-6.6.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-6.6.0-darwin-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.6.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.6.0-darwin-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.6.0-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.6.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.6.0-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.6.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.6.0-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.6.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.6.0-win32-x64.node-buildinfo.txt +1 -0
- package/examples/README.md +4 -0
- package/lib/configProviders/azure.js +167 -0
- package/lib/configProviders/base.js +99 -0
- package/lib/configProviders/ociobject.js +203 -0
- package/lib/connection.js +28 -12
- package/lib/constants.js +1 -0
- package/lib/errors.js +68 -1
- package/lib/impl/datahandlers/buffer.js +2 -2
- package/lib/impl/datahandlers/constants.js +3 -1
- package/lib/impl/datahandlers/vector.js +24 -9
- package/lib/impl/lob.js +27 -0
- package/lib/impl/resultset.js +26 -1
- package/lib/lob.js +52 -1
- package/lib/oracledb.js +61 -34
- package/lib/pool.js +7 -0
- package/lib/thin/connection.js +232 -16
- package/lib/thin/dbObject.js +4 -0
- package/lib/thin/lob.js +193 -1
- package/lib/thin/pool.js +15 -0
- package/lib/thin/protocol/capabilities.js +2 -0
- package/lib/thin/protocol/constants.js +30 -1
- package/lib/thin/protocol/messages/index.js +5 -1
- package/lib/thin/protocol/messages/lobOp.js +17 -5
- package/lib/thin/protocol/messages/transactionChangeState.js +99 -0
- package/lib/thin/protocol/messages/transactionSwitch.js +139 -0
- package/lib/thin/protocol/messages/withData.js +37 -8
- package/lib/thin/protocol/protocol.js +4 -0
- package/lib/thin/sqlnet/networkSession.js +2 -1
- package/lib/thin/sqlnet/packet.js +3 -1
- package/lib/thin/sqlnet/paramParser.js +87 -50
- package/lib/thin/sqlnet/sessionAtts.js +13 -1
- package/lib/thin/statement.js +4 -0
- package/lib/thin/statementCache.js +5 -0
- package/lib/transformer.js +1 -2
- package/lib/types.js +1 -1
- package/lib/util.js +62 -2
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/build/Release/oracledb-6.5.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.5.0-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.5.0-js-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.5.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.5.0-linux-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.5.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.5.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.5.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.5.0-win32-x64.node-buildinfo.txt +0 -1
|
@@ -32,6 +32,7 @@ const process = require('process');
|
|
|
32
32
|
const readline = require('readline');
|
|
33
33
|
const errors = require("../../errors.js");
|
|
34
34
|
|
|
35
|
+
const MAX_IFILE_DEPTH = 4;
|
|
35
36
|
/**
|
|
36
37
|
* Returns File path of the tnsnames.ora if it exists.
|
|
37
38
|
*/
|
|
@@ -59,9 +60,12 @@ function tnsnamesFilePath(configDir) {
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
let prevmtime = 0;
|
|
63
|
-
|
|
64
63
|
class NLParamParser {
|
|
64
|
+
|
|
65
|
+
constructor() {
|
|
66
|
+
this.waiters = [];
|
|
67
|
+
this.readInProgress = false;
|
|
68
|
+
}
|
|
65
69
|
/**
|
|
66
70
|
* Reads the given file line by line and stores the
|
|
67
71
|
* network service names mapped to connect descriptors in the hashtable.
|
|
@@ -69,13 +73,29 @@ class NLParamParser {
|
|
|
69
73
|
* @returns {Promise}
|
|
70
74
|
*/
|
|
71
75
|
async initializeNlpa(file_path) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
if (this.readInProgress) {
|
|
77
|
+
await new Promise((resolve) => {
|
|
78
|
+
this.waiters.push(resolve);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (!this.checkModfTime()) {
|
|
82
|
+
/* No File has been modified */
|
|
76
83
|
return this.ht;
|
|
77
84
|
}
|
|
85
|
+
this.ht = new Map();
|
|
86
|
+
this.modTime = new Map(); //stores modified time of each IFile
|
|
87
|
+
this.readInProgress = true;
|
|
88
|
+
await this.start(file_path, 0); //start with 0 depth (tnsnames.ora)
|
|
89
|
+
return this.ht;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async start(file_path, depth) {
|
|
78
93
|
|
|
94
|
+
if (depth > MAX_IFILE_DEPTH)
|
|
95
|
+
return; //ignore after max depth
|
|
96
|
+
const stat = fs.statSync(file_path);
|
|
97
|
+
// store file path and its modified time.
|
|
98
|
+
this.modTime.set(file_path, stat.mtime);
|
|
79
99
|
// Creating a readable stream from file
|
|
80
100
|
// readline module reads line by line
|
|
81
101
|
// but from a readable stream only.
|
|
@@ -84,52 +104,49 @@ class NLParamParser {
|
|
|
84
104
|
output: process.stdout,
|
|
85
105
|
terminal: false
|
|
86
106
|
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
107
|
+
let nvElem = "";
|
|
108
|
+
for await (let line of file) {
|
|
109
|
+
if (line.length == 0) { // ignore empty lines
|
|
110
|
+
continue;
|
|
111
|
+
} else if (line[0] == '#') { // comment line
|
|
112
|
+
continue;
|
|
113
|
+
} else if ((line[0] == ' ') || // continued input on new line
|
|
114
|
+
(line[0] == '\t') ||
|
|
115
|
+
(line[0] == ')') ||
|
|
116
|
+
(line[0] == '(')) {
|
|
117
|
+
line = line.replace(/\s+/g, '');
|
|
118
|
+
line = this.checkNLPforComments(line);
|
|
119
|
+
if (line.length == 0)
|
|
95
120
|
continue;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
(line[0] == '(')) {
|
|
100
|
-
line = line.replace(/\s+/g, '');
|
|
101
|
-
line = this.checkNLPforComments(line);
|
|
102
|
-
if (line.length == 0)
|
|
103
|
-
continue;
|
|
104
|
-
else {
|
|
105
|
-
nvElem = nvElem + line;
|
|
106
|
-
}
|
|
121
|
+
else {
|
|
122
|
+
nvElem = nvElem + line;
|
|
123
|
+
}
|
|
107
124
|
|
|
108
|
-
|
|
109
|
-
|
|
125
|
+
} else { // new NV Element starting here
|
|
126
|
+
if (nvElem.length == 0) {
|
|
110
127
|
|
|
111
|
-
|
|
112
|
-
|
|
128
|
+
line = this.checkNLPforComments(line);
|
|
129
|
+
nvElem = nvElem + line;
|
|
113
130
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
131
|
+
} else if (nvElem.length != 0) {
|
|
132
|
+
await this.addNLPListElement(nvElem, depth); // Add Parameter to Hashtable
|
|
133
|
+
nvElem = ""; // Clear first, before storing current line
|
|
117
134
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
135
|
+
line = this.checkNLPforComments(line);
|
|
136
|
+
nvElem = nvElem + line;
|
|
121
137
|
}
|
|
122
138
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
139
|
+
}
|
|
140
|
+
if (nvElem.length != 0) { // at eof, still one more parameter to read
|
|
141
|
+
await this.addNLPListElement(nvElem, depth);
|
|
142
|
+
nvElem = ""; // clear nvElem buffer after added
|
|
143
|
+
}
|
|
144
|
+
this.readInProgress = false;
|
|
145
|
+
let waiter;
|
|
146
|
+
while ((waiter = this.waiters.pop())) {
|
|
147
|
+
waiter();
|
|
148
|
+
}
|
|
131
149
|
}
|
|
132
|
-
|
|
133
150
|
/**
|
|
134
151
|
* Given a string, this method looks if the '#' character is present.
|
|
135
152
|
* If true, the line is truncated from that point onwards until the end
|
|
@@ -155,11 +172,28 @@ class NLParamParser {
|
|
|
155
172
|
}
|
|
156
173
|
return str1.join('');
|
|
157
174
|
}
|
|
175
|
+
// check if any of the IFiles has been changed
|
|
176
|
+
checkModfTime() {
|
|
177
|
+
if (this.modTime) {
|
|
178
|
+
for (const [key, value] of this.modTime) {
|
|
179
|
+
if (fs.existsSync(key)) {
|
|
180
|
+
const stat = fs.statSync(key);
|
|
181
|
+
if ((stat.mtime - value > 0)) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
} else
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
158
192
|
/**
|
|
159
193
|
* adds name value pairs from the input buffer into the hash table.
|
|
160
194
|
* @param {string} ibuf
|
|
161
195
|
*/
|
|
162
|
-
addNLPListElement(ibuf) {
|
|
196
|
+
async addNLPListElement(ibuf, depth) {
|
|
163
197
|
const res = ibuf.split(/\r?\n/).filter(element => element);
|
|
164
198
|
for (let i = 0; i < res.length; i++) {
|
|
165
199
|
if (res[i].charAt(0) != '(') {
|
|
@@ -169,14 +203,17 @@ class NLParamParser {
|
|
|
169
203
|
const name = nvp.name;
|
|
170
204
|
const uname = name.toUpperCase();
|
|
171
205
|
nvp.name = uname;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
this.
|
|
206
|
+
// support for ifile
|
|
207
|
+
if (uname == 'IFILE') {
|
|
208
|
+
await this.start(nvp.atom, depth + 1);
|
|
209
|
+
} else {
|
|
210
|
+
const unames = uname.split(","); //multiple aliases (alias1, alias2, alias3)
|
|
211
|
+
for (let i = 0; i < unames.length; i++) {
|
|
212
|
+
this.ht.set(unames[i], nvp);
|
|
213
|
+
}
|
|
175
214
|
}
|
|
176
215
|
}
|
|
177
216
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
217
|
toString() {
|
|
181
218
|
let out = "";
|
|
182
219
|
this.ht.forEach((value) => {
|
|
@@ -77,6 +77,9 @@ class SessionAtts {
|
|
|
77
77
|
if (typeof params.walletPassword === 'string') {
|
|
78
78
|
this.nt.walletPassword = params.walletPassword;
|
|
79
79
|
}
|
|
80
|
+
if (typeof params.walletContent === 'string') {
|
|
81
|
+
this.nt.wallet = params.walletContent;
|
|
82
|
+
}
|
|
80
83
|
if (params.expireTime > 0) {
|
|
81
84
|
this.nt.expireTime = params.expireTime * 1000 * 60;
|
|
82
85
|
}
|
|
@@ -135,6 +138,15 @@ class SessionAtts {
|
|
|
135
138
|
});
|
|
136
139
|
}
|
|
137
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Clear wallet
|
|
143
|
+
*/
|
|
144
|
+
clearWallet() {
|
|
145
|
+
if (this.nt.wallet && Buffer.isBuffer(this.nt.wallet))
|
|
146
|
+
this.nt.wallet.fill(0);
|
|
147
|
+
this.nt.wallet = null;
|
|
148
|
+
}
|
|
149
|
+
|
|
138
150
|
/**
|
|
139
151
|
* Prepare attributes for connection, Generate Connection ID and read Wallet file
|
|
140
152
|
*
|
|
@@ -151,7 +163,7 @@ class SessionAtts {
|
|
|
151
163
|
}
|
|
152
164
|
this.nt.connectionId = this.connectionId;
|
|
153
165
|
|
|
154
|
-
if (protocol && (protocol.toUpperCase() == "TCPS" && this.nt.walletFile)) {
|
|
166
|
+
if (protocol && (protocol.toUpperCase() == "TCPS" && !this.nt.wallet && this.nt.walletFile)) {
|
|
155
167
|
this.nt.wallet = await this.readWalletFile();
|
|
156
168
|
}
|
|
157
169
|
|
package/lib/thin/statement.js
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
const { Buffer } = require('buffer');
|
|
30
30
|
const constants = require('../constants');
|
|
31
31
|
const errors = require('../errors');
|
|
32
|
+
const protoConstants = require('./protocol/constants');
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* It is used to cache the metadata about bind information
|
|
@@ -519,6 +520,9 @@ class Statement {
|
|
|
519
520
|
// cursor also requires a full execute.
|
|
520
521
|
//---------------------------------------------------------------------------
|
|
521
522
|
_setVariable(bindInfo, variable) {
|
|
523
|
+
if (variable.type._oraTypeNum === protoConstants.TNS_DATA_TYPE_CURSOR) {
|
|
524
|
+
this.requiresFullExecute = true;
|
|
525
|
+
}
|
|
522
526
|
if (variable.maxSize !== bindInfo.maxSize
|
|
523
527
|
|| variable.dir !== bindInfo.dir
|
|
524
528
|
|| variable.isArray !== bindInfo.isArray
|
|
@@ -146,6 +146,11 @@ class StatementCache {
|
|
|
146
146
|
return stmt;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
clearCursor(statement) {
|
|
150
|
+
this._addCursorToClose(statement);
|
|
151
|
+
statement.cursorId = 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
149
154
|
//---------------------------------------------------------------------------
|
|
150
155
|
// returnStatement()
|
|
151
156
|
// Return the statement to the statement cache, if applicable. If the
|
package/lib/transformer.js
CHANGED
|
@@ -228,8 +228,7 @@ function transformValueIn(info, value, options) {
|
|
|
228
228
|
return value._impl;
|
|
229
229
|
|
|
230
230
|
// handle vectors
|
|
231
|
-
} else if (value
|
|
232
|
-
value instanceof Int8Array) {
|
|
231
|
+
} else if (nodbUtil.isVectorValue(value)) {
|
|
233
232
|
checkType(info, options, types.DB_TYPE_VECTOR);
|
|
234
233
|
return value;
|
|
235
234
|
} else if (info.type === types.DB_TYPE_VECTOR && Array.isArray(value)) {
|
package/lib/types.js
CHANGED
|
@@ -112,7 +112,7 @@ function getTypeByOraTypeNum(oraTypeNum, csfrm) {
|
|
|
112
112
|
|
|
113
113
|
const DB_TYPE_BFILE = new DbType(2020,
|
|
114
114
|
"DB_TYPE_BFILE", "BFILE",
|
|
115
|
-
{ oraTypeNum: 114, bufferSizeFactor:
|
|
115
|
+
{ oraTypeNum: 114, bufferSizeFactor: 4000 });
|
|
116
116
|
const DB_TYPE_BINARY_DOUBLE = new DbType(2008,
|
|
117
117
|
"DB_TYPE_BINARY_DOUBLE", "BINARY_DOUBLE",
|
|
118
118
|
{ oraTypeNum: 101, bufferSizeFactor: 8 });
|
package/lib/util.js
CHANGED
|
@@ -31,6 +31,7 @@ const errors = require('./errors.js');
|
|
|
31
31
|
const process = require('process');
|
|
32
32
|
const util = require('util');
|
|
33
33
|
const types = require('./types.js');
|
|
34
|
+
const constants = require('./constants.js');
|
|
34
35
|
|
|
35
36
|
// node-oracledb version number
|
|
36
37
|
let packageJSON;
|
|
@@ -224,6 +225,32 @@ function isObjectOrArray(value) {
|
|
|
224
225
|
return (value !== null && typeof value === 'object') || Array.isArray(value);
|
|
225
226
|
}
|
|
226
227
|
|
|
228
|
+
//---------------------------------------------------------------------------
|
|
229
|
+
// isPrivilege()
|
|
230
|
+
//
|
|
231
|
+
// Returns a boolean indicating if the supplied value is a valid privilege.
|
|
232
|
+
//---------------------------------------------------------------------------
|
|
233
|
+
function isPrivilege(value) {
|
|
234
|
+
// Privileges are mutually exclusive and cannot be specified together
|
|
235
|
+
// except SYSPRELIM, which cannot be specified alone, it is specified in a
|
|
236
|
+
// combo with SYSOPER or SYSDBA. SYSPRELIM is used only for
|
|
237
|
+
// startup/shutdown
|
|
238
|
+
|
|
239
|
+
// If SYSPRELIM specified, clear the bit
|
|
240
|
+
if (value & constants.SYSPRELIM) {
|
|
241
|
+
value = value ^ constants.SYSPRELIM;
|
|
242
|
+
}
|
|
243
|
+
return (
|
|
244
|
+
value === constants.SYSASM ||
|
|
245
|
+
value === constants.SYSBACKUP ||
|
|
246
|
+
value === constants.SYSDBA ||
|
|
247
|
+
value === constants.SYSDG ||
|
|
248
|
+
value === constants.SYSKM ||
|
|
249
|
+
value === constants.SYSOPER ||
|
|
250
|
+
value === constants.SYSRAC
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
227
254
|
function isShardingKey(value) {
|
|
228
255
|
if (!Array.isArray(value))
|
|
229
256
|
return false;
|
|
@@ -250,6 +277,36 @@ function isXid(value) {
|
|
|
250
277
|
typeof value.branchQualifier === 'string'));
|
|
251
278
|
}
|
|
252
279
|
|
|
280
|
+
function normalizeXid(value) {
|
|
281
|
+
let normalizedXid;
|
|
282
|
+
if (Buffer.isBuffer(value.globalTransactionId) &&
|
|
283
|
+
Buffer.isBuffer(value.branchQualifier)) {
|
|
284
|
+
normalizedXid = value;
|
|
285
|
+
} else {
|
|
286
|
+
normalizedXid = {
|
|
287
|
+
formatId: value.formatId,
|
|
288
|
+
globalTransactionId: value.globalTransactionId,
|
|
289
|
+
branchQualifier: value.branchQualifier
|
|
290
|
+
};
|
|
291
|
+
if (typeof value.globalTransactionId === 'string') {
|
|
292
|
+
normalizedXid.globalTransactionId = Buffer.from(value.globalTransactionId);
|
|
293
|
+
}
|
|
294
|
+
if (typeof value.branchQualifier === 'string') {
|
|
295
|
+
normalizedXid.branchQualifier = Buffer.from(value.branchQualifier);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (normalizedXid.globalTransactionId.length > 64) {
|
|
299
|
+
errors.throwErr(errors.ERR_INVALID_TRANSACTION_SIZE,
|
|
300
|
+
normalizedXid.globalTransactionId.length);
|
|
301
|
+
}
|
|
302
|
+
if (normalizedXid.branchQualifier.length > 64) {
|
|
303
|
+
errors.throwErr(errors.ERR_INVALID_BRANCH_SIZE,
|
|
304
|
+
normalizedXid.branchQualifier.length);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return normalizedXid;
|
|
308
|
+
}
|
|
309
|
+
|
|
253
310
|
function verifySodaDoc(content) {
|
|
254
311
|
if (isSodaDocument(content))
|
|
255
312
|
return content._impl;
|
|
@@ -335,8 +392,9 @@ function addTypeProperties(obj, attrName) {
|
|
|
335
392
|
// -----------------------------------------------------------------------------
|
|
336
393
|
function isVectorValue(value) {
|
|
337
394
|
return (value instanceof Float32Array ||
|
|
338
|
-
|
|
339
|
-
|
|
395
|
+
value instanceof Float64Array ||
|
|
396
|
+
value instanceof Int8Array || (Object.getPrototypeOf(value)
|
|
397
|
+
=== Uint8Array.prototype));
|
|
340
398
|
}
|
|
341
399
|
|
|
342
400
|
//-----------------------------------------------------------------------------
|
|
@@ -369,12 +427,14 @@ module.exports = {
|
|
|
369
427
|
isArrayOfStrings,
|
|
370
428
|
isObject,
|
|
371
429
|
isObjectOrArray,
|
|
430
|
+
isPrivilege,
|
|
372
431
|
isShardingKey,
|
|
373
432
|
isSodaDocument,
|
|
374
433
|
isTokenExpired,
|
|
375
434
|
isTokenValid,
|
|
376
435
|
isVectorValue,
|
|
377
436
|
isXid,
|
|
437
|
+
normalizeXid,
|
|
378
438
|
makeDate,
|
|
379
439
|
preventConcurrent,
|
|
380
440
|
serialize,
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
oracledb-6.5.0-darwin-x64.node v20.11.1 91a7f419eccbfcd18513c19cd25f55f18a4d8ccc 42edf925eedae73b0d5bd7777167a21987bafc89 Tue, 30 Apr 2024 20:12:57 GMT
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
91a7f419eccbfcd18513c19cd25f55f18a4d8ccc
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
oracledb-6.5.0-linux-arm64.node v18.19.1 91a7f419eccbfcd18513c19cd25f55f18a4d8ccc 42edf925eedae73b0d5bd7777167a21987bafc89 Wed, 01 May 2024 04:29:37 GMT
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
oracledb-6.5.0-linux-x64.node v16.20.2 91a7f419eccbfcd18513c19cd25f55f18a4d8ccc 42edf925eedae73b0d5bd7777167a21987bafc89 Wed, 01 May 2024 04:22:59 GMT
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
oracledb-6.5.0-win32-x64.node v18.16.0 91a7f419eccbfcd18513c19cd25f55f18a4d8ccc 42edf925eedae73b0d5bd7777167a21987bafc89 Wed, 01 May 2024 04:34:23 GMT
|