oracledb 7.0.0 → 26.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/README.md +16 -6
- package/build/Release/oracledb-26.0.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-26.0.0-darwin-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-26.0.0-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-26.0.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-26.0.0-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-26.0.0-linux-x64.node +0 -0
- package/build/Release/oracledb-26.0.0-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-26.0.0-win32-x64.node +0 -0
- package/build/Release/oracledb-26.0.0-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 +73 -18
- package/lib/constants.js +4 -0
- package/lib/endUserSecurityContext.js +30 -25
- package/lib/errors.js +92 -5
- package/lib/impl/connection.js +36 -0
- package/lib/impl/datahandlers/buffer.js +91 -18
- package/lib/impl/datahandlers/oson.js +3 -2
- package/lib/impl/parserHelpers.js +6 -2
- package/lib/impl/resultset.js +32 -11
- package/lib/oracledb.js +34 -0
- package/lib/pool.js +6 -2
- package/lib/queryStream.js +13 -5
- package/lib/resultset.js +10 -3
- package/lib/securityContextProvider.js +206 -0
- package/lib/thin/aq.js +2 -3
- package/lib/thin/connection.js +380 -24
- package/lib/thin/dbObject.js +20 -11
- package/lib/thin/pool.js +4 -3
- package/lib/thin/protocol/capabilities.js +37 -4
- package/lib/thin/protocol/constants.js +49 -1
- 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 +17 -6
- package/lib/thin/protocol/messages/base.js +75 -17
- package/lib/thin/protocol/messages/dataType.js +46 -0
- package/lib/thin/protocol/messages/directPathLoadStream.js +3 -2
- package/lib/thin/protocol/messages/execute.js +10 -4
- package/lib/thin/protocol/messages/index.js +4 -0
- package/lib/thin/protocol/messages/notification.js +281 -0
- package/lib/thin/protocol/messages/subscribe.js +181 -0
- package/lib/thin/protocol/messages/withData.js +23 -10
- package/lib/thin/protocol/packet.js +2 -4
- package/lib/thin/protocol/protocol.js +7 -4
- package/lib/thin/resultSet.js +40 -20
- package/lib/thin/sqlnet/ezConnectResolver.js +9 -2
- package/lib/thin/sqlnet/navNodes.js +7 -5
- package/lib/thin/sqlnet/networkSession.js +17 -11
- package/lib/thin/sqlnet/ntTcp.js +24 -8
- package/lib/thin/sqlnet/packet.js +24 -4
- package/lib/thin/sqlnet/sessionAtts.js +2 -2
- package/lib/thin/statement.js +6 -1
- package/lib/thin/statementCache.js +34 -6
- package/lib/thin/subscr.js +363 -0
- package/lib/transformer.js +60 -17
- package/lib/util.js +46 -8
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/plugins/configProviders/gcpstorage/index.js +121 -0
- package/plugins/configProviders/ociobject/index.js +1 -1
- package/plugins/token/cache.js +154 -0
- package/plugins/token/endUserSecurityProvider/index.js +377 -0
- package/plugins/token/extensionAzure/index.js +160 -28
- package/plugins/token/extensionOci/index.js +221 -16
- package/plugins/vectorsdk/OracleVecDB.js +346 -0
- package/plugins/vectorsdk/index.js +49 -0
- package/plugins/vectorsdk/utils.js +370 -0
- package/plugins/vectorsdk/vectorEmbedding.js +223 -0
- package/plugins/vectorsdk/vectorIndex.js +177 -0
- package/plugins/vectorsdk/vectorRecords.js +406 -0
- package/plugins/vectorsdk/vectorSchema.js +408 -0
- package/plugins/vectorsdk/vectorSearch.js +538 -0
- package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
- package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-7.0.0-js-buildinfo.txt +0 -1
- package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
- package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
- package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +0 -1
- package/eslint.config.js +0 -39
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
// Copyright (c) 2026, Oracle and/or its affiliates.
|
|
2
|
+
|
|
3
|
+
//-----------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// This software is dual-licensed to you under the Universal Permissive License
|
|
6
|
+
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
|
7
|
+
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
|
|
8
|
+
// either license.
|
|
9
|
+
//
|
|
10
|
+
// If you elect to accept the software under the Apache License, Version 2.0,
|
|
11
|
+
// the following applies:
|
|
12
|
+
//
|
|
13
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
// you may not use this file except in compliance with the License.
|
|
15
|
+
// You may obtain a copy of the License at
|
|
16
|
+
//
|
|
17
|
+
// https://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
//
|
|
19
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
// See the License for the specific language governing permissions and
|
|
23
|
+
// limitations under the License.
|
|
24
|
+
//
|
|
25
|
+
//-----------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
const errors = require("../errors.js");
|
|
30
|
+
const constants = require("../constants.js");
|
|
31
|
+
const protocolUtils = require("./protocol/utils.js");
|
|
32
|
+
const protocolConstants = require("./protocol/constants.js");
|
|
33
|
+
const messages = require("./protocol/messages");
|
|
34
|
+
|
|
35
|
+
class ThinSubscrImpl {
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates a thin subscription implementation instance.
|
|
39
|
+
*/
|
|
40
|
+
constructor(connImpl, options) {
|
|
41
|
+
this._connImpl = connImpl;
|
|
42
|
+
this.callback = options.callback;
|
|
43
|
+
this.namespace = options.namespace;
|
|
44
|
+
this.name = options.name;
|
|
45
|
+
this.timeout = options.timeout || 0;
|
|
46
|
+
this.operations = options.operations || 0;
|
|
47
|
+
this.qos = options.qos || 0;
|
|
48
|
+
this.groupingClass = options.groupingClass || 0;
|
|
49
|
+
this.groupingValue = options.groupingValue || 0;
|
|
50
|
+
this.groupingType = options.groupingType || 0;
|
|
51
|
+
this.regId = 0n;
|
|
52
|
+
this.clientId = null;
|
|
53
|
+
this._notifyConnImpl = null;
|
|
54
|
+
this._notifyLoopPromise = null;
|
|
55
|
+
this._unsubscribing = false;
|
|
56
|
+
this._notifyLoopActive = false;
|
|
57
|
+
this._listenerStopped = false;
|
|
58
|
+
this._registered = false;
|
|
59
|
+
this._onRetire = options._onRetire;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Invokes the user callback safely for a decoded notification.
|
|
64
|
+
*/
|
|
65
|
+
_invokeCallback(message) {
|
|
66
|
+
if (typeof this.callback !== 'function') {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
this.callback(message);
|
|
71
|
+
} catch {
|
|
72
|
+
// swallow callback exceptions so notification loop remains active
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Retires a subscription after server deregistration or successful explicit
|
|
78
|
+
* unsubscribe, removing the public entry that retains its callback.
|
|
79
|
+
*/
|
|
80
|
+
_retire() {
|
|
81
|
+
this._registered = false;
|
|
82
|
+
this._notifyLoopActive = false;
|
|
83
|
+
this._listenerStopped = false;
|
|
84
|
+
if (this._onRetire) {
|
|
85
|
+
this._onRetire(this);
|
|
86
|
+
this._onRetire = null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Closes and releases the dedicated EMON notification connection.
|
|
92
|
+
*/
|
|
93
|
+
_closeNotifyConnection() {
|
|
94
|
+
const notifyConnImpl = this._notifyConnImpl;
|
|
95
|
+
this._notifyConnImpl = null;
|
|
96
|
+
if (notifyConnImpl?.nscon) {
|
|
97
|
+
notifyConnImpl.nscon.forceDisconnect();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Internal method that registers the subscription with the database
|
|
103
|
+
* and stores returned IDs.
|
|
104
|
+
*/
|
|
105
|
+
async _register() {
|
|
106
|
+
const message = new messages.SubscrMessage(this._connImpl, this);
|
|
107
|
+
await this._connImpl._protocol._processMessage(message);
|
|
108
|
+
this.regId = message.registrationId;
|
|
109
|
+
this.clientId = message.clientId;
|
|
110
|
+
this._registered = true;
|
|
111
|
+
this._listenerStopped = false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Marks the EMON listener as unavailable while retaining the server
|
|
116
|
+
* registration for explicit unregister or same-name replacement. This is
|
|
117
|
+
* used for abnormal/recovery cases, not normal notification completion.
|
|
118
|
+
*/
|
|
119
|
+
_stopListener() {
|
|
120
|
+
this._notifyLoopActive = false;
|
|
121
|
+
this._listenerStopped = true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Internal method that registers a CQN query against an existing
|
|
126
|
+
* subscription registration.
|
|
127
|
+
*/
|
|
128
|
+
async _registerQuery(sql, binds) {
|
|
129
|
+
const options = {
|
|
130
|
+
_subscriptionRegId: this.regId,
|
|
131
|
+
keepInStmtCache: false,
|
|
132
|
+
autoCommit: false,
|
|
133
|
+
resultSet: true,
|
|
134
|
+
fetchArraySize: 1,
|
|
135
|
+
prefetchRows: 1
|
|
136
|
+
};
|
|
137
|
+
options.connection = {
|
|
138
|
+
_impl: this._connImpl,
|
|
139
|
+
_getDbObjectClass: (dbObjectClass) => dbObjectClass
|
|
140
|
+
};
|
|
141
|
+
const statement = this._connImpl._prepare(sql, options);
|
|
142
|
+
const internalTempLobs = [];
|
|
143
|
+
let statementReturned = false;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
if (!statement.isQuery) {
|
|
147
|
+
errors.throwErr(errors.ERR_NOT_A_QUERY);
|
|
148
|
+
}
|
|
149
|
+
const result = await this._connImpl._execute(statement, 1,
|
|
150
|
+
binds || [], options, false, internalTempLobs);
|
|
151
|
+
|
|
152
|
+
if (result?.resultSet) {
|
|
153
|
+
await result.resultSet.close();
|
|
154
|
+
statementReturned = true;
|
|
155
|
+
}
|
|
156
|
+
} finally {
|
|
157
|
+
if (!statementReturned) {
|
|
158
|
+
this._connImpl._returnStatement(statement);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
for (const lob of internalTempLobs) {
|
|
162
|
+
this._connImpl._tempLobsToClose.push(lob._locator);
|
|
163
|
+
this._connImpl._tempLobsTotalSize += lob._locator.length;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Starts the dedicated notification channel by establishing a separate EMON
|
|
170
|
+
* connection, sending a single notify request, and launching the async
|
|
171
|
+
* receive/decode loop until the subscription is unsubscribed, deregistered,
|
|
172
|
+
* or the listener fails.
|
|
173
|
+
*/
|
|
174
|
+
async _startNotifyLoop() {
|
|
175
|
+
|
|
176
|
+
// Use a separate EMON connection for notifications.
|
|
177
|
+
const notifyParams = await this._connImpl._getNotificationConnectParams();
|
|
178
|
+
const NotifyConnImpl = this._connImpl.constructor;
|
|
179
|
+
const notifyConnImpl = new NotifyConnImpl();
|
|
180
|
+
|
|
181
|
+
// Marking it as notification connection so that we do not cache
|
|
182
|
+
// connection parameters. Only the user-facing connection retains
|
|
183
|
+
// obfuscated parameters for future notification connections.
|
|
184
|
+
notifyConnImpl._isNotificationConnection = true;
|
|
185
|
+
|
|
186
|
+
// Retain user-facing connection metadata on the internal connection.
|
|
187
|
+
notifyConnImpl._connectString = this._connImpl._connectString;
|
|
188
|
+
notifyConnImpl._user = this._connImpl._user;
|
|
189
|
+
this._notifyConnImpl = notifyConnImpl;
|
|
190
|
+
try {
|
|
191
|
+
await notifyConnImpl.connect(notifyParams);
|
|
192
|
+
|
|
193
|
+
// clientId links this listener to the registered subscription.
|
|
194
|
+
const notifyMessage = new messages.NotifyMessage(notifyConnImpl, this);
|
|
195
|
+
notifyMessage.clientId = this.clientId;
|
|
196
|
+
await notifyConnImpl._protocol._encodeMessage(notifyMessage);
|
|
197
|
+
|
|
198
|
+
// Unsubscribe, deregistration, or listener failure stops this loop and
|
|
199
|
+
// closes the notification connection.
|
|
200
|
+
this._notifyLoopActive = true;
|
|
201
|
+
this._notifyLoopPromise = this._runNotifyLoop(notifyConnImpl,
|
|
202
|
+
notifyMessage);
|
|
203
|
+
this._notifyLoopPromise.catch(() => {
|
|
204
|
+
// prevent unhandled rejection if listener fails asynchronously
|
|
205
|
+
});
|
|
206
|
+
} catch (err) {
|
|
207
|
+
// Setup can fail before _runNotifyLoop() owns cleanup.
|
|
208
|
+
if (this._notifyConnImpl === notifyConnImpl) {
|
|
209
|
+
this._closeNotifyConnection();
|
|
210
|
+
}
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Runs the notification receive/decode loop for the dedicated channel.
|
|
217
|
+
*/
|
|
218
|
+
async _runNotifyLoop(notifyConnImpl, notifyMessage) {
|
|
219
|
+
const protocol = notifyConnImpl._protocol;
|
|
220
|
+
const readBuf = protocol.readBuf;
|
|
221
|
+
try {
|
|
222
|
+
notifyMessage.preProcess();
|
|
223
|
+
await readBuf.waitForPackets();
|
|
224
|
+
|
|
225
|
+
// Decode records as packets arrive until unsubscribe or deregistration.
|
|
226
|
+
while (this._notifyLoopActive) {
|
|
227
|
+
try {
|
|
228
|
+
notifyMessage.decode(readBuf);
|
|
229
|
+
} catch (err) {
|
|
230
|
+
if (err instanceof protocolUtils.OutOfPacketsError) {
|
|
231
|
+
await readBuf.waitForPackets();
|
|
232
|
+
readBuf.restorePoint();
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (!this._unsubscribing) {
|
|
236
|
+
|
|
237
|
+
// Listener failure while still subscribed is unexpected.
|
|
238
|
+
// Throw the error.
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Listener interruption during explicit unsubscribe is expected.
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
} catch (err) {
|
|
247
|
+
if (!this._unsubscribing) {
|
|
248
|
+
// The server registration can still exist after an EMON failure. Keep
|
|
249
|
+
// it in the public registry so unsubscribe() can deregister it.
|
|
250
|
+
this._stopListener();
|
|
251
|
+
}
|
|
252
|
+
throw err;
|
|
253
|
+
} finally {
|
|
254
|
+
// Close the EMON connection whenever the loop ends without going
|
|
255
|
+
// through unsubscribe(), so its socket cannot keep the Node.js event
|
|
256
|
+
// loop alive.
|
|
257
|
+
if (this._notifyConnImpl === notifyConnImpl) {
|
|
258
|
+
this._notifyLoopActive = false;
|
|
259
|
+
this._closeNotifyConnection();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Internal method for creating a subscription. Each new subscription uses a
|
|
266
|
+
* dedicated client-initiated EMON connection to receive notifications. CQN
|
|
267
|
+
* registrations with the same name reuse this listener to add queries.
|
|
268
|
+
*/
|
|
269
|
+
async subscribe(options) {
|
|
270
|
+
if (this.namespace === constants.SUBSCR_NAMESPACE_AQ && !this.qos) {
|
|
271
|
+
// Default AQ registrations request query notifications.
|
|
272
|
+
this.qos = constants.SUBSCR_QOS_QUERY;
|
|
273
|
+
}
|
|
274
|
+
await this._register();
|
|
275
|
+
try {
|
|
276
|
+
await this._startNotifyLoop();
|
|
277
|
+
if (this.namespace === constants.SUBSCR_NAMESPACE_DBCHANGE) {
|
|
278
|
+
await this._registerQuery(options.sql, options.binds);
|
|
279
|
+
}
|
|
280
|
+
} catch (err) {
|
|
281
|
+
try {
|
|
282
|
+
await this.unsubscribe(this._connImpl);
|
|
283
|
+
} catch {
|
|
284
|
+
// preserve the original subscribe failure
|
|
285
|
+
}
|
|
286
|
+
throw err;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Registers an additional query for an existing DBCHANGE subscription.
|
|
292
|
+
*/
|
|
293
|
+
async registerQuery(options) {
|
|
294
|
+
if (this.namespace === constants.SUBSCR_NAMESPACE_AQ) {
|
|
295
|
+
errors.throwErr(errors.ERR_NOT_IMPLEMENTED,
|
|
296
|
+
"registering a query on AQ subscriptions");
|
|
297
|
+
}
|
|
298
|
+
if (!this._registered || this._listenerStopped) {
|
|
299
|
+
errors.throwErr(errors.ERR_INVALID_SUBSCR);
|
|
300
|
+
}
|
|
301
|
+
await this._registerQuery(options.sql, options.binds);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Internal method for destroying the subscription.
|
|
306
|
+
*/
|
|
307
|
+
async unsubscribe(connImpl) {
|
|
308
|
+
if (!this.regId) {
|
|
309
|
+
errors.throwErr(errors.ERR_INVALID_SUBSCR);
|
|
310
|
+
}
|
|
311
|
+
this._unsubscribing = true;
|
|
312
|
+
let unregisterError;
|
|
313
|
+
let shouldRetire = false;
|
|
314
|
+
try {
|
|
315
|
+
if (this._registered) {
|
|
316
|
+
const message = new messages.SubscrMessage(connImpl, this);
|
|
317
|
+
message.opCode = protocolConstants.TNS_SUBSCR_OP_UNREGISTER;
|
|
318
|
+
message.registrationId = this.regId;
|
|
319
|
+
message.clientId = this.clientId;
|
|
320
|
+
await connImpl._protocol._processMessage(message);
|
|
321
|
+
}
|
|
322
|
+
shouldRetire = true;
|
|
323
|
+
} catch (err) {
|
|
324
|
+
unregisterError = err;
|
|
325
|
+
if (err.code === errors.getErr(errors.ERR_INVALID_SUBSCR).code) {
|
|
326
|
+
// The server already discarded this registration. Remove the public
|
|
327
|
+
// entry as well so its callback is not retained and the name can be
|
|
328
|
+
// subscribed again.
|
|
329
|
+
shouldRetire = true;
|
|
330
|
+
}
|
|
331
|
+
} finally {
|
|
332
|
+
this._notifyLoopActive = false;
|
|
333
|
+
if (shouldRetire) {
|
|
334
|
+
this._retire();
|
|
335
|
+
this.regId = 0n;
|
|
336
|
+
this.clientId = null;
|
|
337
|
+
} else {
|
|
338
|
+
// The EMON socket is closed below, so this registration cannot be
|
|
339
|
+
// reused without first creating a replacement listener. Retain the
|
|
340
|
+
// registration so the caller can retry UNREGISTER.
|
|
341
|
+
this._stopListener();
|
|
342
|
+
}
|
|
343
|
+
// Interrupt the background wait by force-closing the notification socket.
|
|
344
|
+
this._closeNotifyConnection();
|
|
345
|
+
if (this._notifyLoopPromise) {
|
|
346
|
+
try {
|
|
347
|
+
await this._notifyLoopPromise;
|
|
348
|
+
} catch {
|
|
349
|
+
// ignore listener errors during explicit unsubscribe
|
|
350
|
+
}
|
|
351
|
+
this._notifyLoopPromise = null;
|
|
352
|
+
}
|
|
353
|
+
if (!shouldRetire) {
|
|
354
|
+
this._unsubscribing = false;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (unregisterError) {
|
|
358
|
+
throw unregisterError;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
module.exports = ThinSubscrImpl;
|
package/lib/transformer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2023,
|
|
1
|
+
// Copyright (c) 2023, 2026, Oracle and/or its affiliates.
|
|
2
2
|
|
|
3
3
|
//-----------------------------------------------------------------------------
|
|
4
4
|
//
|
|
@@ -36,6 +36,12 @@ const util = require('util');
|
|
|
36
36
|
const types = require('./types.js');
|
|
37
37
|
const nodbUtil = require('./util.js');
|
|
38
38
|
|
|
39
|
+
const unsupportedJsonFieldNames = new Set([
|
|
40
|
+
'__proto__',
|
|
41
|
+
'constructor',
|
|
42
|
+
'prototype'
|
|
43
|
+
]);
|
|
44
|
+
|
|
39
45
|
//-----------------------------------------------------------------------------
|
|
40
46
|
// checkType()
|
|
41
47
|
//
|
|
@@ -78,12 +84,31 @@ function checkType(info, options) {
|
|
|
78
84
|
//-----------------------------------------------------------------------------
|
|
79
85
|
// transformJsonValue()
|
|
80
86
|
//
|
|
81
|
-
// Returns a normalized JSON value.
|
|
82
|
-
// Arrays are returned as a new array with transformed JSON values.
|
|
83
|
-
// returned as new objects with keys "fields" and "values", both of
|
|
84
|
-
// are arrays (with the value transformed to JSON values).
|
|
87
|
+
// Returns a normalized JSON value. Supported scalar values are returned
|
|
88
|
+
// unchanged. Arrays are returned as a new array with transformed JSON values.
|
|
89
|
+
// Objects are returned as new objects with keys "fields" and "values", both of
|
|
90
|
+
// which are arrays (with the value transformed to JSON values).
|
|
91
|
+
// Only enumerable own object properties are included.
|
|
92
|
+
// If rejectUnsupportedTypes is set, unsupported JSON values, circular
|
|
93
|
+
// references, and field names are rejected.
|
|
85
94
|
//-----------------------------------------------------------------------------
|
|
86
|
-
function transformJsonValue(value) {
|
|
95
|
+
function transformJsonValue(value, options = {}, activeValues) {
|
|
96
|
+
|
|
97
|
+
// bigInt cannot be represented in JSON and is always rejected
|
|
98
|
+
if (typeof value === 'bigint') {
|
|
99
|
+
errors.throwErr(errors.ERR_UNSUPPORTED_JSON_VALUE_TYPE, typeof value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// reject unsupported values when requested
|
|
103
|
+
if (options.rejectUnsupportedTypes &&
|
|
104
|
+
(typeof value === 'symbol' || typeof value === 'function')) {
|
|
105
|
+
errors.throwErr(errors.ERR_UNSUPPORTED_JSON_VALUE_TYPE, typeof value);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (options.rejectUnsupportedTypes &&
|
|
109
|
+
typeof value === 'number' && !Number.isFinite(value)) {
|
|
110
|
+
errors.throwErr(errors.ERR_NON_FINITE_JSON_NUMBER);
|
|
111
|
+
}
|
|
87
112
|
|
|
88
113
|
// handle simple scalars
|
|
89
114
|
if (value === undefined || value === null ||
|
|
@@ -93,15 +118,6 @@ function transformJsonValue(value) {
|
|
|
93
118
|
value instanceof types.IntervalYM || value instanceof types.IntervalDS)
|
|
94
119
|
return value;
|
|
95
120
|
|
|
96
|
-
// arrays are transformed to a new array with processed values
|
|
97
|
-
if (Array.isArray(value)) {
|
|
98
|
-
const outValue = new Array(value.length);
|
|
99
|
-
for (let i = 0; i < value.length; i++) {
|
|
100
|
-
outValue[i] = transformJsonValue(value[i]);
|
|
101
|
-
}
|
|
102
|
-
return outValue;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
121
|
// database objects are treated as empty objects
|
|
106
122
|
if (value instanceof BaseDbObject)
|
|
107
123
|
return {fields: [], values: []};
|
|
@@ -112,14 +128,41 @@ function transformJsonValue(value) {
|
|
|
112
128
|
return value;
|
|
113
129
|
}
|
|
114
130
|
|
|
131
|
+
if (options.rejectUnsupportedTypes) {
|
|
132
|
+
if (!activeValues)
|
|
133
|
+
activeValues = new Set();
|
|
134
|
+
if (activeValues.has(value))
|
|
135
|
+
errors.throwErr(errors.ERR_INVALID_PARAMETER_VALUE, 1);
|
|
136
|
+
activeValues.add(value);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// arrays are transformed to a new array with processed values
|
|
140
|
+
if (Array.isArray(value)) {
|
|
141
|
+
const outValue = new Array(value.length);
|
|
142
|
+
for (let i = 0; i < value.length; i++) {
|
|
143
|
+
outValue[i] = transformJsonValue(value[i], options, activeValues);
|
|
144
|
+
}
|
|
145
|
+
if (activeValues)
|
|
146
|
+
activeValues.delete(value);
|
|
147
|
+
return outValue;
|
|
148
|
+
}
|
|
149
|
+
|
|
115
150
|
// all other objects are transformed to an object with two arrays (fields
|
|
116
151
|
// and values)
|
|
117
152
|
const outValue = {};
|
|
118
|
-
outValue.fields = Object.
|
|
153
|
+
outValue.fields = Object.keys(value);
|
|
119
154
|
outValue.values = new Array(outValue.fields.length);
|
|
120
155
|
for (let i = 0; i < outValue.fields.length; i++) {
|
|
121
|
-
|
|
156
|
+
if (options.rejectUnsupportedTypes &&
|
|
157
|
+
unsupportedJsonFieldNames.has(outValue.fields[i])) {
|
|
158
|
+
errors.throwErr(errors.ERR_UNSUPPORTED_JSON_FIELD_NAME,
|
|
159
|
+
outValue.fields[i]);
|
|
160
|
+
}
|
|
161
|
+
outValue.values[i] = transformJsonValue(value[outValue.fields[i]], options,
|
|
162
|
+
activeValues);
|
|
122
163
|
}
|
|
164
|
+
if (activeValues)
|
|
165
|
+
activeValues.delete(value);
|
|
123
166
|
return outValue;
|
|
124
167
|
|
|
125
168
|
}
|
package/lib/util.js
CHANGED
|
@@ -33,6 +33,7 @@ const util = require('util');
|
|
|
33
33
|
const types = require('./types.js');
|
|
34
34
|
const constants = require('./constants.js');
|
|
35
35
|
const traceHandler = require('./traceHandler.js');
|
|
36
|
+
const securityContextProvider = require('./securityContextProvider.js');
|
|
36
37
|
const crypto = require("crypto");
|
|
37
38
|
|
|
38
39
|
const pemHeaders = [
|
|
@@ -271,6 +272,8 @@ function wrapFn(func, serialize, preventConcurrentErrorCode) {
|
|
|
271
272
|
let connImpl;
|
|
272
273
|
const traceEnabled = traceHandler.isEnabled();
|
|
273
274
|
const traceContext = {fn: func, args: arguments};
|
|
275
|
+
const hook = securityContextProvider.getInvocationHook();
|
|
276
|
+
let hookState;
|
|
274
277
|
|
|
275
278
|
// if concurrent operations are to be prevented, check for that now
|
|
276
279
|
if (preventConcurrentErrorCode) {
|
|
@@ -295,12 +298,29 @@ function wrapFn(func, serialize, preventConcurrentErrorCode) {
|
|
|
295
298
|
if (traceEnabled) {
|
|
296
299
|
traceEnterFn.call(this, traceContext);
|
|
297
300
|
}
|
|
301
|
+
if (hook?.before) {
|
|
302
|
+
// more attributes like args, sql can be passed to
|
|
303
|
+
// provider later.
|
|
304
|
+
const invocation = {
|
|
305
|
+
target: this,
|
|
306
|
+
connection: connImpl
|
|
307
|
+
};
|
|
308
|
+
hookState = await hook.before(invocation);
|
|
309
|
+
}
|
|
298
310
|
result = await traceContext.fn.apply(this, arguments);
|
|
299
|
-
return result;
|
|
300
311
|
} catch (err) {
|
|
301
312
|
tErr = errors.transformErr(err, wrapper);
|
|
302
|
-
throw tErr;
|
|
303
313
|
} finally {
|
|
314
|
+
if (hook?.after && hookState) {
|
|
315
|
+
try {
|
|
316
|
+
await hook.after(hookState);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
// Preserve the operation's error if both fail.
|
|
319
|
+
if (!tErr) {
|
|
320
|
+
tErr = errors.transformErr(err, wrapper);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
304
324
|
if (connImpl)
|
|
305
325
|
connImpl._releaseLock();
|
|
306
326
|
if (preventConcurrentErrorCode) {
|
|
@@ -311,6 +331,10 @@ function wrapFn(func, serialize, preventConcurrentErrorCode) {
|
|
|
311
331
|
}
|
|
312
332
|
}
|
|
313
333
|
|
|
334
|
+
if (tErr) {
|
|
335
|
+
throw tErr;
|
|
336
|
+
}
|
|
337
|
+
return result;
|
|
314
338
|
};
|
|
315
339
|
if (func.name) {
|
|
316
340
|
Object.defineProperty(wrapper, 'name', { value: func.name });
|
|
@@ -563,19 +587,33 @@ function isVectorValue(value) {
|
|
|
563
587
|
=== Uint8Array.prototype) || value instanceof types.SparseVector);
|
|
564
588
|
}
|
|
565
589
|
|
|
566
|
-
|
|
590
|
+
//---------------------------------------------------------------------------
|
|
567
591
|
// makeDate()
|
|
568
592
|
//
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
//
|
|
593
|
+
// Creates a Date from the supplied date/time components. When useLocal is
|
|
594
|
+
// true, the components are interpreted in the Node.js process local timezone.
|
|
595
|
+
// Otherwise, they are interpreted using the supplied UTC offset.
|
|
596
|
+
//---------------------------------------------------------------------------
|
|
572
597
|
function makeDate(useLocal, year, month, day, hour, minute,
|
|
573
598
|
second, fseconds, offset) {
|
|
574
599
|
if (useLocal) {
|
|
575
600
|
return new Date(year, month - 1, day, hour, minute, second, fseconds);
|
|
576
601
|
}
|
|
577
|
-
return new Date(
|
|
578
|
-
fseconds) - offset * 60000);
|
|
602
|
+
return new Date(makeUTCDate(year, month, day, hour, minute, second,
|
|
603
|
+
fseconds).getTime() - offset * 60000);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
//-----------------------------------------------------------------------------
|
|
607
|
+
// makeUTCDate()
|
|
608
|
+
//
|
|
609
|
+
// Creates a UTC date without Date.UTC()'s special handling of years 0 through
|
|
610
|
+
// 99, which is important when calculating historical timezone offsets.
|
|
611
|
+
// -----------------------------------------------------------------------------
|
|
612
|
+
function makeUTCDate(year, month, day, hour, minute, second, fseconds = 0) {
|
|
613
|
+
const date = new Date(0);
|
|
614
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
615
|
+
date.setUTCHours(hour, minute, second, fseconds);
|
|
616
|
+
return date;
|
|
579
617
|
}
|
|
580
618
|
|
|
581
619
|
//---------------------------------------------------------------------------
|
package/lib/version.js
CHANGED
package/package.json
CHANGED