oracledb 6.3.0 → 6.5.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 +3 -3
- package/build/Release/oracledb-6.5.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.5.0-darwin-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.5.0-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.5.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.5.0-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.5.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.5.0-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.5.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.5.0-win32-x64.node-buildinfo.txt +1 -0
- package/examples/README.md +3 -0
- package/lib/aqQueue.js +8 -8
- package/lib/connection.js +65 -22
- package/lib/constants.js +7 -1
- package/lib/dbObject.js +72 -1
- package/lib/errors.js +51 -10
- package/lib/impl/connection.js +2 -1
- package/lib/{thin/protocol → impl/datahandlers}/buffer.js +36 -32
- package/lib/impl/datahandlers/constants.js +106 -0
- package/lib/{thin/protocol → impl/datahandlers}/oson.js +320 -151
- package/lib/impl/datahandlers/vector.js +137 -0
- package/lib/impl/pool.js +9 -0
- package/lib/impl/resultset.js +34 -17
- package/lib/lob.js +22 -6
- package/lib/oracledb.js +35 -1
- package/lib/pool.js +17 -2
- package/lib/poolStatistics.js +3 -1
- package/lib/settings.js +31 -6
- package/lib/sodaDocCursor.js +14 -1
- package/lib/thin/connection.js +120 -193
- package/lib/thin/dbObject.js +4 -2
- package/lib/thin/lob.js +6 -6
- package/lib/thin/pool.js +84 -10
- package/lib/thin/protocol/capabilities.js +20 -6
- package/lib/thin/protocol/constants.js +34 -54
- package/lib/thin/protocol/messages/auth.js +2 -2
- package/lib/thin/protocol/messages/base.js +28 -27
- package/lib/thin/protocol/messages/dataType.js +4 -2
- package/lib/thin/protocol/messages/execute.js +5 -1
- package/lib/thin/protocol/messages/{connectionCookie.js → fastAuth.js} +18 -16
- package/lib/thin/protocol/messages/fetch.js +4 -0
- package/lib/thin/protocol/messages/index.js +2 -2
- package/lib/thin/protocol/messages/protocol.js +9 -1
- package/lib/thin/protocol/messages/withData.js +40 -11
- package/lib/thin/protocol/packet.js +93 -8
- package/lib/thin/protocol/protocol.js +10 -5
- package/lib/thin/protocol/utils.js +11 -18
- package/lib/thin/sqlnet/constants.js +5 -2
- package/lib/thin/sqlnet/ezConnectResolver.js +9 -5
- package/lib/thin/sqlnet/navNodes.js +4 -3
- package/lib/thin/sqlnet/networkSession.js +27 -4
- package/lib/thin/sqlnet/packet.js +2 -5
- package/lib/thin/statement.js +43 -7
- package/lib/thin/statementCache.js +190 -0
- package/lib/thin/util.js +2 -2
- package/lib/transformer.js +21 -6
- package/lib/types.js +21 -3
- package/lib/util.js +30 -1
- package/lib/version.js +2 -2
- package/package.json +1 -1
- package/build/Release/oracledb-6.3.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.3.0-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.3.0-js-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.3.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.3.0-linux-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.3.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.3.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.3.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.3.0-win32-x64.node-buildinfo.txt +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2023, Oracle and/or its affiliates.
|
|
1
|
+
// Copyright (c) 2023, 2024, Oracle and/or its affiliates.
|
|
2
2
|
|
|
3
3
|
//-----------------------------------------------------------------------------
|
|
4
4
|
//
|
|
@@ -32,9 +32,11 @@ const constants = require("./constants.js");
|
|
|
32
32
|
const errors = require("../../errors.js");
|
|
33
33
|
const types = require("../../types.js");
|
|
34
34
|
const util = require("util");
|
|
35
|
+
const vector = require("./vector.js");
|
|
36
|
+
const nodbUtil = require("../../util.js");
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
|
-
* Class used for
|
|
39
|
+
* Class used for decoding
|
|
38
40
|
*/
|
|
39
41
|
class OsonDecoder extends BaseBuffer {
|
|
40
42
|
|
|
@@ -50,6 +52,7 @@ class OsonDecoder extends BaseBuffer {
|
|
|
50
52
|
// significant bits of the node type; determine the offsets in the tree
|
|
51
53
|
// segment to the field ids array and the value offsets array
|
|
52
54
|
let container, offsetsPos, fieldIdsPos;
|
|
55
|
+
const containerOffset = this.pos - this.treeSegPos - 1;
|
|
53
56
|
let numChildren = this._getNumChildren(nodeType);
|
|
54
57
|
const isObject = ((nodeType & 0x40) === 0);
|
|
55
58
|
if (numChildren === undefined) {
|
|
@@ -84,7 +87,10 @@ class OsonDecoder extends BaseBuffer {
|
|
|
84
87
|
fieldIdsPos += this.fieldIdLength;
|
|
85
88
|
}
|
|
86
89
|
this.pos = offsetsPos;
|
|
87
|
-
|
|
90
|
+
let offset = this._getOffset(nodeType);
|
|
91
|
+
if (this.relativeOffsets) {
|
|
92
|
+
offset += containerOffset;
|
|
93
|
+
}
|
|
88
94
|
offsetsPos = this.pos;
|
|
89
95
|
this.pos = this.treeSegPos + offset;
|
|
90
96
|
if (isObject) {
|
|
@@ -106,7 +112,7 @@ class OsonDecoder extends BaseBuffer {
|
|
|
106
112
|
_decodeNode() {
|
|
107
113
|
|
|
108
114
|
// if the most significant bit is set the node refers to a container
|
|
109
|
-
|
|
115
|
+
let nodeType = this.readUInt8();
|
|
110
116
|
if (nodeType & 0x80) {
|
|
111
117
|
return this._decodeContainerNode(nodeType);
|
|
112
118
|
}
|
|
@@ -141,10 +147,22 @@ class OsonDecoder extends BaseBuffer {
|
|
|
141
147
|
return this.readBytes(this.readUInt32BE()).toString();
|
|
142
148
|
} else if (nodeType === constants.TNS_JSON_TYPE_NUMBER_LENGTH_UINT8) {
|
|
143
149
|
return parseFloat(this.readOracleNumber());
|
|
150
|
+
} else if (nodeType === constants.TNS_JSON_TYPE_ID) {
|
|
151
|
+
const buf = this.readBytes(this.readUInt8());
|
|
152
|
+
const jsonId = new types.JsonId(buf.length);
|
|
153
|
+
buf.copy(jsonId);
|
|
154
|
+
return jsonId;
|
|
144
155
|
} else if (nodeType === constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT16) {
|
|
145
156
|
return Buffer.from(this.readBytes(this.readUInt16BE()));
|
|
146
157
|
} else if (nodeType === constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT32) {
|
|
147
158
|
return Buffer.from(this.readBytes(this.readUInt32BE()));
|
|
159
|
+
} else if (nodeType === constants.TNS_JSON_TYPE_EXTENDED) {
|
|
160
|
+
nodeType = this.readUInt8();
|
|
161
|
+
if (nodeType === constants.TNS_JSON_TYPE_VECTOR) {
|
|
162
|
+
const vecImage = this.readBytes(this.readUInt32BE());
|
|
163
|
+
const decoder = new vector.VectorDecoder(vecImage);
|
|
164
|
+
return decoder.decode();
|
|
165
|
+
}
|
|
148
166
|
}
|
|
149
167
|
|
|
150
168
|
// handle number/decimal with length stored inside the node itself
|
|
@@ -207,6 +225,46 @@ class OsonDecoder extends BaseBuffer {
|
|
|
207
225
|
}
|
|
208
226
|
}
|
|
209
227
|
|
|
228
|
+
//---------------------------------------------------------------------------
|
|
229
|
+
// _getFieldNames
|
|
230
|
+
//
|
|
231
|
+
// Reads the field names from the buffer.
|
|
232
|
+
//---------------------------------------------------------------------------
|
|
233
|
+
_getFieldNames(arrStartPos, numFields, offsetsSize, fieldNamesSegSize, fieldNamesSize) {
|
|
234
|
+
|
|
235
|
+
// skip the hash id array (1 byte * fieldNamesSize for each field)
|
|
236
|
+
this.skipBytes(numFields * fieldNamesSize);
|
|
237
|
+
|
|
238
|
+
// skip the field name offsets array for now
|
|
239
|
+
const offsetsPos = this.pos;
|
|
240
|
+
this.skipBytes(numFields * offsetsSize);
|
|
241
|
+
const ptr = this.readBytes(fieldNamesSegSize);
|
|
242
|
+
const finalPos = this.pos;
|
|
243
|
+
|
|
244
|
+
// determine the names of the fields
|
|
245
|
+
this.pos = offsetsPos;
|
|
246
|
+
let offset;
|
|
247
|
+
for (let i = arrStartPos; i < arrStartPos + numFields; i++) {
|
|
248
|
+
if (offsetsSize === 2) {
|
|
249
|
+
offset = this.readUInt16BE();
|
|
250
|
+
} else {
|
|
251
|
+
offset = this.readUInt32BE();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// get the field name object
|
|
255
|
+
let temp;
|
|
256
|
+
if (fieldNamesSize === 1) {
|
|
257
|
+
// Short Field Name
|
|
258
|
+
temp = ptr.readUInt8(offset);
|
|
259
|
+
} else {
|
|
260
|
+
// Long Field Name
|
|
261
|
+
temp = ptr.readUInt16BE(offset);
|
|
262
|
+
}
|
|
263
|
+
this.fieldNames[i] = ptr.subarray(offset + fieldNamesSize, offset + temp + fieldNamesSize).toString();
|
|
264
|
+
}
|
|
265
|
+
this.pos = finalPos;
|
|
266
|
+
}
|
|
267
|
+
|
|
210
268
|
//---------------------------------------------------------------------------
|
|
211
269
|
// decode()
|
|
212
270
|
//
|
|
@@ -223,14 +281,16 @@ class OsonDecoder extends BaseBuffer {
|
|
|
223
281
|
errors.throwErr(errors.ERR_UNEXPECTED_DATA, magic.toString('hex'));
|
|
224
282
|
}
|
|
225
283
|
const version = this.readUInt8();
|
|
226
|
-
if (version !== constants.
|
|
284
|
+
if (version !== constants.TNS_JSON_VERSION_MAX_FNAME_255 &&
|
|
285
|
+
version !== constants.TNS_JSON_VERSION_MAX_FNAME_65535) {
|
|
227
286
|
errors.throwErr(errors.ERR_OSON_VERSION_NOT_SUPPORTED, version);
|
|
228
287
|
}
|
|
229
|
-
const
|
|
288
|
+
const primaryFlags = this.readUInt16BE();
|
|
289
|
+
this.relativeOffsets = primaryFlags & constants.TNS_JSON_FLAG_REL_OFFSET_MODE;
|
|
230
290
|
|
|
231
291
|
// scalar values are much simpler
|
|
232
|
-
if (
|
|
233
|
-
if (
|
|
292
|
+
if (primaryFlags & constants.TNS_JSON_FLAG_IS_SCALAR) {
|
|
293
|
+
if (primaryFlags & constants.TNS_JSON_FLAG_TREE_SEG_UINT32) {
|
|
234
294
|
this.skipBytes(4);
|
|
235
295
|
} else {
|
|
236
296
|
this.skipBytes(2);
|
|
@@ -238,31 +298,46 @@ class OsonDecoder extends BaseBuffer {
|
|
|
238
298
|
return this._decodeNode();
|
|
239
299
|
}
|
|
240
300
|
|
|
241
|
-
// determine the number of field names
|
|
242
|
-
let
|
|
243
|
-
if (
|
|
244
|
-
|
|
301
|
+
// determine the number of short field names
|
|
302
|
+
let numShortFieldNames;
|
|
303
|
+
if (primaryFlags & constants.TNS_JSON_FLAG_NUM_FNAMES_UINT32) {
|
|
304
|
+
numShortFieldNames = this.readUInt32BE();
|
|
245
305
|
this.fieldIdLength = 4;
|
|
246
|
-
} else if (
|
|
247
|
-
|
|
306
|
+
} else if (primaryFlags & constants.TNS_JSON_FLAG_NUM_FNAMES_UINT16) {
|
|
307
|
+
numShortFieldNames = this.readUInt16BE();
|
|
248
308
|
this.fieldIdLength = 2;
|
|
249
309
|
} else {
|
|
250
|
-
|
|
310
|
+
numShortFieldNames = this.readUInt8();
|
|
251
311
|
this.fieldIdLength = 1;
|
|
252
312
|
}
|
|
253
313
|
|
|
254
|
-
// determine the size of the field names segment
|
|
255
|
-
let
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
314
|
+
// determine the size of the short field names segment
|
|
315
|
+
let shortFieldNameOffsetsSize, shortFieldNamesSegSize;
|
|
316
|
+
if (primaryFlags & constants.TNS_JSON_FLAG_FNAMES_SEG_UINT32) {
|
|
317
|
+
shortFieldNameOffsetsSize = 4;
|
|
318
|
+
shortFieldNamesSegSize = this.readUInt32BE();
|
|
259
319
|
} else {
|
|
260
|
-
|
|
261
|
-
|
|
320
|
+
shortFieldNameOffsetsSize = 2;
|
|
321
|
+
shortFieldNamesSegSize = this.readUInt16BE();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// if the version indicates that field names > 255 bytes exist, parse
|
|
325
|
+
// the information about that segment
|
|
326
|
+
let longFieldNameOffsetsSize, longFieldNamesSegSize;
|
|
327
|
+
let numLongFieldNames = 0;
|
|
328
|
+
if (version === constants.TNS_JSON_VERSION_MAX_FNAME_65535) {
|
|
329
|
+
const secondaryFlags = this.readUInt16BE();
|
|
330
|
+
if (secondaryFlags & constants.TNS_JSON_FLAG_SEC_FNAMES_SEG_UINT16) {
|
|
331
|
+
longFieldNameOffsetsSize = 2;
|
|
332
|
+
} else {
|
|
333
|
+
longFieldNameOffsetsSize = 4;
|
|
334
|
+
}
|
|
335
|
+
numLongFieldNames = this.readUInt32BE();
|
|
336
|
+
longFieldNamesSegSize = this.readUInt32BE();
|
|
262
337
|
}
|
|
263
338
|
|
|
264
339
|
// skip the size of the tree segment
|
|
265
|
-
if (
|
|
340
|
+
if (primaryFlags & constants.TNS_JSON_FLAG_TREE_SEG_UINT32) {
|
|
266
341
|
this.skipBytes(4);
|
|
267
342
|
} else {
|
|
268
343
|
this.skipBytes(2);
|
|
@@ -271,37 +346,18 @@ class OsonDecoder extends BaseBuffer {
|
|
|
271
346
|
// skip the number of "tiny" nodes
|
|
272
347
|
this.skipBytes(2);
|
|
273
348
|
|
|
274
|
-
|
|
275
|
-
let hashIdSize;
|
|
276
|
-
if (flags & constants.TNS_JSON_FLAG_HASH_ID_UINT8) {
|
|
277
|
-
hashIdSize = 1;
|
|
278
|
-
} else if (flags & constants.TNS_JSON_FLAG_HASH_ID_UINT16) {
|
|
279
|
-
hashIdSize = 2;
|
|
280
|
-
} else {
|
|
281
|
-
hashIdSize = 4;
|
|
282
|
-
}
|
|
283
|
-
this.skipBytes(numFieldNames * hashIdSize);
|
|
349
|
+
this.fieldNames = new Array(numShortFieldNames + numLongFieldNames);
|
|
284
350
|
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
351
|
+
// if there are any short names, read them now
|
|
352
|
+
if (numShortFieldNames > 0) {
|
|
353
|
+
this._getFieldNames(0, numShortFieldNames,
|
|
354
|
+
shortFieldNameOffsetsSize, shortFieldNamesSegSize, 1);
|
|
355
|
+
}
|
|
290
356
|
|
|
291
|
-
//
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (flags & constants.TNS_JSON_FLAG_FNAMES_SEG_UINT32) {
|
|
296
|
-
offset += this.buf.readUInt32BE(fieldNameOffsetsPos);
|
|
297
|
-
fieldNameOffsetsPos += 4;
|
|
298
|
-
} else {
|
|
299
|
-
offset += this.buf.readUInt16BE(fieldNameOffsetsPos);
|
|
300
|
-
fieldNameOffsetsPos += 2;
|
|
301
|
-
}
|
|
302
|
-
const len = this.buf[offset];
|
|
303
|
-
const name = this.buf.subarray(offset + 1, offset + len + 1).toString();
|
|
304
|
-
this.fieldNames[i] = name;
|
|
357
|
+
// if there are any long names, read them now
|
|
358
|
+
if (numLongFieldNames > 0) {
|
|
359
|
+
this._getFieldNames(numShortFieldNames, numLongFieldNames,
|
|
360
|
+
longFieldNameOffsetsSize, longFieldNamesSegSize, 2);
|
|
305
361
|
}
|
|
306
362
|
|
|
307
363
|
// determine tree segment position in the buffer
|
|
@@ -315,18 +371,22 @@ class OsonDecoder extends BaseBuffer {
|
|
|
315
371
|
|
|
316
372
|
class OsonFieldName {
|
|
317
373
|
|
|
318
|
-
constructor(name) {
|
|
374
|
+
constructor(name, maxFieldNameSize) {
|
|
319
375
|
this.name = name;
|
|
320
376
|
this.nameBytes = Buffer.from(name);
|
|
321
|
-
if (this.nameBytes.length >
|
|
322
|
-
errors.throwErr(errors.ERR_OSON_FIELD_NAME_LIMITATION);
|
|
377
|
+
if (this.nameBytes.length > maxFieldNameSize) {
|
|
378
|
+
errors.throwErr(errors.ERR_OSON_FIELD_NAME_LIMITATION, maxFieldNameSize);
|
|
323
379
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const
|
|
380
|
+
|
|
381
|
+
// BigInt constants for calculating Hash ID for the OSON Field Name
|
|
382
|
+
const INITIAL_HASHID = 0x811C9DC5n;
|
|
383
|
+
const HASH_MULTIPLIER = 16777619n;
|
|
384
|
+
const HASH_MASK = 0xffffffffn;
|
|
385
|
+
|
|
386
|
+
this.hashId = INITIAL_HASHID;
|
|
327
387
|
for (let i = 0; i < this.nameBytes.length; i++) {
|
|
328
388
|
const c = BigInt(this.nameBytes[i]);
|
|
329
|
-
this.hashId = ((this.hashId ^ c) *
|
|
389
|
+
this.hashId = ((this.hashId ^ c) * HASH_MULTIPLIER) & HASH_MASK;
|
|
330
390
|
}
|
|
331
391
|
this.hashId = Number(this.hashId) & 0xff;
|
|
332
392
|
}
|
|
@@ -335,41 +395,25 @@ class OsonFieldName {
|
|
|
335
395
|
|
|
336
396
|
class OsonFieldNamesSegment extends GrowableBuffer {
|
|
337
397
|
|
|
338
|
-
constructor(
|
|
398
|
+
constructor() {
|
|
339
399
|
super();
|
|
340
|
-
this.fieldNamesMap = new Map();
|
|
341
400
|
this.fieldNames = [];
|
|
342
|
-
this._examineNode(value);
|
|
343
|
-
this._processFieldNames();
|
|
344
401
|
}
|
|
345
402
|
|
|
346
403
|
//---------------------------------------------------------------------------
|
|
347
|
-
//
|
|
404
|
+
// addName()
|
|
348
405
|
//
|
|
349
|
-
//
|
|
350
|
-
// values are then examined to see if they also contain fields. Arrays are
|
|
351
|
-
// examined to determine they contain elements that contain fields.
|
|
406
|
+
// Adds a name to the field names segment.
|
|
352
407
|
//---------------------------------------------------------------------------
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
for (let i = 0; i < value.fields.length; i++) {
|
|
360
|
-
const name = value.fields[i];
|
|
361
|
-
const element = value.values[i];
|
|
362
|
-
if (!this.fieldNamesMap.has(name)) {
|
|
363
|
-
const fieldName = new OsonFieldName(name);
|
|
364
|
-
this.fieldNamesMap.set(name, fieldName);
|
|
365
|
-
this.fieldNames.push(fieldName);
|
|
366
|
-
fieldName.offset = this.pos;
|
|
367
|
-
this.writeUInt8(fieldName.nameBytes.length);
|
|
368
|
-
this.writeBytes(fieldName.nameBytes);
|
|
369
|
-
}
|
|
370
|
-
this._examineNode(element);
|
|
371
|
-
}
|
|
408
|
+
addName(fieldName) {
|
|
409
|
+
fieldName.offset = this.pos;
|
|
410
|
+
if (fieldName.nameBytes.length <= 255) {
|
|
411
|
+
this.writeUInt8(fieldName.nameBytes.length);
|
|
412
|
+
} else {
|
|
413
|
+
this.writeUInt16BE(fieldName.nameBytes.length);
|
|
372
414
|
}
|
|
415
|
+
this.writeBytes(fieldName.nameBytes);
|
|
416
|
+
this.fieldNames.push(fieldName);
|
|
373
417
|
}
|
|
374
418
|
|
|
375
419
|
//---------------------------------------------------------------------------
|
|
@@ -377,7 +421,7 @@ class OsonFieldNamesSegment extends GrowableBuffer {
|
|
|
377
421
|
//
|
|
378
422
|
// Processes the field names in preparation for encoding within OSON.
|
|
379
423
|
//---------------------------------------------------------------------------
|
|
380
|
-
_processFieldNames() {
|
|
424
|
+
_processFieldNames(fieldIdOffset) {
|
|
381
425
|
this.fieldNames.sort((a, b) => {
|
|
382
426
|
if (a.hashId < b.hashId)
|
|
383
427
|
return -1;
|
|
@@ -394,7 +438,7 @@ class OsonFieldNamesSegment extends GrowableBuffer {
|
|
|
394
438
|
return 0;
|
|
395
439
|
});
|
|
396
440
|
for (let i = 0; i < this.fieldNames.length; i++) {
|
|
397
|
-
this.fieldNames[i].fieldId = i + 1;
|
|
441
|
+
this.fieldNames[i].fieldId = fieldIdOffset + i + 1;
|
|
398
442
|
}
|
|
399
443
|
if (this.fieldNames.length < 256) {
|
|
400
444
|
this.fieldIdSize = 1;
|
|
@@ -414,7 +458,7 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
414
458
|
//
|
|
415
459
|
// Encodes an array in the OSON tree segment.
|
|
416
460
|
//---------------------------------------------------------------------------
|
|
417
|
-
_encodeArray(value,
|
|
461
|
+
_encodeArray(value, encoder) {
|
|
418
462
|
this._encodeContainer(constants.TNS_JSON_TYPE_ARRAY, value.length);
|
|
419
463
|
const len = value.length * 4;
|
|
420
464
|
const pos = this.reserveBytes(len);
|
|
@@ -422,7 +466,7 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
422
466
|
for (const element of value) {
|
|
423
467
|
this.buf.writeUInt32BE(this.pos, offsetsBufPos);
|
|
424
468
|
offsetsBufPos += 4;
|
|
425
|
-
this.encodeNode(element,
|
|
469
|
+
this.encodeNode(element, encoder);
|
|
426
470
|
}
|
|
427
471
|
}
|
|
428
472
|
|
|
@@ -454,27 +498,27 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
454
498
|
//
|
|
455
499
|
// Encodes an object in the OSON tree segment.
|
|
456
500
|
//---------------------------------------------------------------------------
|
|
457
|
-
_encodeObject(value,
|
|
501
|
+
_encodeObject(value, encoder) {
|
|
458
502
|
const numChildren = value.values.length;
|
|
459
503
|
this._encodeContainer(constants.TNS_JSON_TYPE_OBJECT, numChildren);
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
504
|
+
let fieldIdOffset = this.pos;
|
|
505
|
+
let valueOffset = this.pos + (numChildren * encoder.fieldIdSize);
|
|
506
|
+
const finalOffset = valueOffset + numChildren * 4;
|
|
507
|
+
this.reserveBytes(finalOffset - this.pos);
|
|
464
508
|
|
|
465
509
|
for (let i = 0; i < value.fields.length; i++) {
|
|
466
|
-
const fieldName =
|
|
467
|
-
if (
|
|
510
|
+
const fieldName = encoder.fieldNamesMap.get(value.fields[i]);
|
|
511
|
+
if (encoder.fieldIdSize == 1) {
|
|
468
512
|
this.buf[fieldIdOffset] = fieldName.fieldId;
|
|
469
|
-
} else if (
|
|
513
|
+
} else if (encoder.fieldIdSize == 2) {
|
|
470
514
|
this.buf.writeUInt16BE(fieldName.fieldId, fieldIdOffset);
|
|
471
515
|
} else {
|
|
472
516
|
this.buf.writeUInt32BE(fieldName.fieldId, fieldIdOffset);
|
|
473
517
|
}
|
|
474
518
|
this.buf.writeUInt32BE(this.pos, valueOffset);
|
|
475
|
-
fieldIdOffset +=
|
|
519
|
+
fieldIdOffset += encoder.fieldIdSize;
|
|
476
520
|
valueOffset += 4;
|
|
477
|
-
this.encodeNode(value.values[i],
|
|
521
|
+
this.encodeNode(value.values[i], encoder);
|
|
478
522
|
}
|
|
479
523
|
}
|
|
480
524
|
|
|
@@ -483,7 +527,7 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
483
527
|
//
|
|
484
528
|
// Encodes a value (node) in the OSON tree segment.
|
|
485
529
|
//---------------------------------------------------------------------------
|
|
486
|
-
encodeNode(value,
|
|
530
|
+
encodeNode(value, encoder) {
|
|
487
531
|
|
|
488
532
|
// handle null
|
|
489
533
|
if (value === undefined || value === null) {
|
|
@@ -520,7 +564,7 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
520
564
|
}
|
|
521
565
|
|
|
522
566
|
// handle dates
|
|
523
|
-
} else if (util.isDate(value)) {
|
|
567
|
+
} else if (util.types.isDate(value)) {
|
|
524
568
|
if (value.getUTCMilliseconds() === 0) {
|
|
525
569
|
this.writeUInt8(constants.TNS_JSON_TYPE_TIMESTAMP7);
|
|
526
570
|
this.writeOracleDate(value, types.DB_TYPE_DATE, false);
|
|
@@ -542,48 +586,199 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
542
586
|
|
|
543
587
|
// handle arrays
|
|
544
588
|
} else if (Array.isArray(value)) {
|
|
545
|
-
this._encodeArray(value,
|
|
589
|
+
this._encodeArray(value, encoder);
|
|
590
|
+
|
|
591
|
+
// handle vectors
|
|
592
|
+
} else if (nodbUtil.isVectorValue(value)) {
|
|
593
|
+
this.writeUInt8(constants.TNS_JSON_TYPE_EXTENDED);
|
|
594
|
+
this.writeUInt8(constants.TNS_JSON_TYPE_VECTOR);
|
|
595
|
+
const encoder = new vector.VectorEncoder();
|
|
596
|
+
const buf = encoder.encode(value);
|
|
597
|
+
this.writeUInt32BE(buf.length);
|
|
598
|
+
this.writeBytes(buf);
|
|
599
|
+
|
|
600
|
+
} else if (value instanceof types.JsonId) {
|
|
601
|
+
this.writeUInt8(constants.TNS_JSON_TYPE_ID);
|
|
602
|
+
this.writeUInt8(value.length);
|
|
603
|
+
this.writeBytes(Buffer.from(value.buffer));
|
|
546
604
|
|
|
547
605
|
// handle objects
|
|
548
606
|
} else {
|
|
549
|
-
this._encodeObject(value,
|
|
607
|
+
this._encodeObject(value, encoder);
|
|
550
608
|
}
|
|
551
609
|
|
|
552
610
|
}
|
|
553
611
|
|
|
554
612
|
}
|
|
555
613
|
|
|
614
|
+
/**
|
|
615
|
+
* Class used for encoding
|
|
616
|
+
*/
|
|
617
|
+
|
|
556
618
|
class OsonEncoder extends GrowableBuffer {
|
|
557
619
|
|
|
620
|
+
//---------------------------------------------------------------------------
|
|
621
|
+
// _addFieldName()
|
|
622
|
+
//
|
|
623
|
+
// Add a field with the given name.
|
|
624
|
+
//---------------------------------------------------------------------------
|
|
625
|
+
_addFieldName(name) {
|
|
626
|
+
const fieldName = new OsonFieldName(name, this.maxFieldNameSize);
|
|
627
|
+
this.fieldNamesMap.set(name, fieldName);
|
|
628
|
+
if (fieldName.nameBytes.length <= 255) {
|
|
629
|
+
this.shortFieldNamesSeg.addName(fieldName);
|
|
630
|
+
} else {
|
|
631
|
+
if (!this.longFieldNamesSeg) {
|
|
632
|
+
this.longFieldNamesSeg = new OsonFieldNamesSegment();
|
|
633
|
+
}
|
|
634
|
+
this.longFieldNamesSeg.addName(fieldName);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
//---------------------------------------------------------------------------
|
|
639
|
+
// _examineNode()
|
|
640
|
+
//
|
|
641
|
+
// Examines the value. If it contains fields, unique names are retained. The
|
|
642
|
+
// values are then examined to see if they also contain fields. Arrays are
|
|
643
|
+
// examined to determine they contain elements that contain fields.
|
|
644
|
+
//---------------------------------------------------------------------------
|
|
645
|
+
_examineNode(value) {
|
|
646
|
+
if (Array.isArray(value)) {
|
|
647
|
+
for (const element of value) {
|
|
648
|
+
this._examineNode(element);
|
|
649
|
+
}
|
|
650
|
+
} else if (value && Array.isArray(value.fields)) {
|
|
651
|
+
for (let i = 0; i < value.fields.length; i++) {
|
|
652
|
+
const name = value.fields[i];
|
|
653
|
+
const element = value.values[i];
|
|
654
|
+
if (!this.fieldNamesMap.has(name)) {
|
|
655
|
+
this._addFieldName(name);
|
|
656
|
+
}
|
|
657
|
+
this._examineNode(element);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
//---------------------------------------------------------------------------
|
|
663
|
+
// _writeExtendedHeader()
|
|
664
|
+
//
|
|
665
|
+
// Write the extended header containing information about the short and long
|
|
666
|
+
// field name segments.
|
|
667
|
+
//---------------------------------------------------------------------------
|
|
668
|
+
_writeExtendedHeader() {
|
|
669
|
+
// write number of short field names
|
|
670
|
+
if (this.fieldIdSize === 1) {
|
|
671
|
+
this.writeUInt8(this.shortFieldNamesSeg.fieldNames.length);
|
|
672
|
+
} else if (this.fieldIdSize === 2) {
|
|
673
|
+
this.writeUInt16BE(this.shortFieldNamesSeg.fieldNames.length);
|
|
674
|
+
} else {
|
|
675
|
+
this.writeUInt32BE(this.shortFieldNamesSeg.fieldNames.length);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// write size of short field names segment
|
|
679
|
+
if (this.shortFieldNamesSeg.pos < 65536) {
|
|
680
|
+
this.writeUInt16BE(this.shortFieldNamesSeg.pos);
|
|
681
|
+
} else {
|
|
682
|
+
this.writeUInt32BE(this.shortFieldNamesSeg.pos);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// write fields for long field names segment, if applicable
|
|
686
|
+
if (this.longFieldNamesSeg) {
|
|
687
|
+
let secondaryFlags = 0;
|
|
688
|
+
if (this.longFieldNamesSeg.pos < 65536) {
|
|
689
|
+
secondaryFlags = constants.TNS_JSON_FLAG_SEC_FNAMES_SEG_UINT16;
|
|
690
|
+
}
|
|
691
|
+
this.writeUInt16BE(secondaryFlags);
|
|
692
|
+
this.writeUInt32BE(this.longFieldNamesSeg.fieldNames.length);
|
|
693
|
+
this.writeUInt32BE(this.longFieldNamesSeg.pos);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
//---------------------------------------------------------------------------
|
|
698
|
+
// _writeFieldNamesSeg()
|
|
699
|
+
//
|
|
700
|
+
// Write the contents of the field names segment to the buffer.
|
|
701
|
+
//---------------------------------------------------------------------------
|
|
702
|
+
_writeFieldNamesSeg(fieldNamesSeg) {
|
|
703
|
+
// write array of hash ids
|
|
704
|
+
for (const fieldName of fieldNamesSeg.fieldNames) {
|
|
705
|
+
if (fieldName.nameBytes.length <= 255) {
|
|
706
|
+
this.writeUInt8(fieldName.hashId);
|
|
707
|
+
} else {
|
|
708
|
+
this.writeUInt16BE(fieldName.hashId);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// write array of field name offsets for the short field names
|
|
713
|
+
for (const fieldName of fieldNamesSeg.fieldNames) {
|
|
714
|
+
if (fieldNamesSeg.pos < 65536) {
|
|
715
|
+
this.writeUInt16BE(fieldName.offset);
|
|
716
|
+
} else {
|
|
717
|
+
this.writeUInt32BE(fieldName.offset);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// write field names
|
|
722
|
+
if (fieldNamesSeg.pos > 0) {
|
|
723
|
+
this.writeBytes(fieldNamesSeg.buf.subarray(0, fieldNamesSeg.pos));
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
}
|
|
727
|
+
|
|
558
728
|
//---------------------------------------------------------------------------
|
|
559
729
|
// encode()
|
|
560
730
|
//
|
|
561
731
|
// Encodes the value as OSON and returns a buffer containing the OSON bytes.
|
|
562
732
|
//---------------------------------------------------------------------------
|
|
563
|
-
encode(value) {
|
|
733
|
+
encode(value, maxFieldNameSize) {
|
|
564
734
|
|
|
565
|
-
|
|
566
|
-
|
|
735
|
+
this.maxFieldNameSize = maxFieldNameSize;
|
|
736
|
+
|
|
737
|
+
// determine the flags to use
|
|
567
738
|
let flags = constants.TNS_JSON_FLAG_INLINE_LEAF;
|
|
568
739
|
if (Array.isArray(value) || (value && Array.isArray(value.fields))) {
|
|
740
|
+
// examine all values recursively to determine the unique set of field
|
|
741
|
+
// names and whether they need to be added to the long field names
|
|
742
|
+
// segment (> 255 bytes) or short field names segment (<= 255 bytes)
|
|
743
|
+
this.fieldNamesMap = new Map();
|
|
744
|
+
this.shortFieldNamesSeg = new OsonFieldNamesSegment();
|
|
745
|
+
this._examineNode(value);
|
|
746
|
+
|
|
747
|
+
// perform processing of field names segments and determine the total
|
|
748
|
+
// number of unique field names in the value
|
|
749
|
+
let totalNumFieldNames = 0;
|
|
750
|
+
if (this.shortFieldNamesSeg) {
|
|
751
|
+
this.shortFieldNamesSeg._processFieldNames(0);
|
|
752
|
+
totalNumFieldNames += this.shortFieldNamesSeg.fieldNames.length;
|
|
753
|
+
}
|
|
754
|
+
if (this.longFieldNamesSeg) {
|
|
755
|
+
this.longFieldNamesSeg._processFieldNames(totalNumFieldNames);
|
|
756
|
+
totalNumFieldNames += this.longFieldNamesSeg.fieldNames.length;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// determine remaining flags and field id size
|
|
569
760
|
flags |= constants.TNS_JSON_FLAG_HASH_ID_UINT8 |
|
|
570
761
|
constants.TNS_JSON_FLAG_TINY_NODES_STAT;
|
|
571
|
-
|
|
572
|
-
if (fnamesSeg.fieldNames.length > 65535) {
|
|
762
|
+
if (totalNumFieldNames > 65535) {
|
|
573
763
|
flags |= constants.TNS_JSON_FLAG_NUM_FNAMES_UINT32;
|
|
574
|
-
|
|
764
|
+
this.fieldIdSize = 4;
|
|
765
|
+
} else if (totalNumFieldNames > 255) {
|
|
575
766
|
flags |= constants.TNS_JSON_FLAG_NUM_FNAMES_UINT16;
|
|
767
|
+
this.fieldIdSize = 2;
|
|
768
|
+
} else {
|
|
769
|
+
this.fieldIdSize = 1;
|
|
576
770
|
}
|
|
577
|
-
if (
|
|
771
|
+
if (this.shortFieldNamesSeg.pos > 65535) {
|
|
578
772
|
flags |= constants.TNS_JSON_FLAG_FNAMES_SEG_UINT32;
|
|
579
773
|
}
|
|
580
774
|
} else {
|
|
775
|
+
// if the value is a simple scalar
|
|
581
776
|
flags |= constants.TNS_JSON_FLAG_IS_SCALAR;
|
|
582
777
|
}
|
|
583
778
|
|
|
584
|
-
// encode values into the tree segment
|
|
779
|
+
// encode values into the OSON tree segment
|
|
585
780
|
const treeSeg = new OsonTreeSegment();
|
|
586
|
-
treeSeg.encodeNode(value,
|
|
781
|
+
treeSeg.encodeNode(value, this);
|
|
587
782
|
if (treeSeg.pos > 65535) {
|
|
588
783
|
flags |= constants.TNS_JSON_FLAG_TREE_SEG_UINT32;
|
|
589
784
|
}
|
|
@@ -592,28 +787,16 @@ class OsonEncoder extends GrowableBuffer {
|
|
|
592
787
|
this.writeUInt8(constants.TNS_JSON_MAGIC_BYTE_1);
|
|
593
788
|
this.writeUInt8(constants.TNS_JSON_MAGIC_BYTE_2);
|
|
594
789
|
this.writeUInt8(constants.TNS_JSON_MAGIC_BYTE_3);
|
|
595
|
-
this.
|
|
790
|
+
if (this.longFieldNamesSeg) {
|
|
791
|
+
this.writeUInt8(constants.TNS_JSON_VERSION_MAX_FNAME_65535);
|
|
792
|
+
} else {
|
|
793
|
+
this.writeUInt8(constants.TNS_JSON_VERSION_MAX_FNAME_255);
|
|
794
|
+
}
|
|
596
795
|
this.writeUInt16BE(flags);
|
|
597
796
|
|
|
598
797
|
// write extended header (when value is not scalar)
|
|
599
|
-
if (
|
|
600
|
-
|
|
601
|
-
// write number of field names
|
|
602
|
-
if (fnamesSeg.fieldNames.length < 256) {
|
|
603
|
-
this.writeUInt8(fnamesSeg.fieldNames.length);
|
|
604
|
-
} else if (fnamesSeg.fieldNames.length < 65536) {
|
|
605
|
-
this.writeUInt16BE(fnamesSeg.fieldNames.length);
|
|
606
|
-
} else {
|
|
607
|
-
this.writeUInt32BE(fnamesSeg.fieldNames.length);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// write size of field names segment
|
|
611
|
-
if (fnamesSeg.pos < 65536) {
|
|
612
|
-
this.writeUInt16BE(fnamesSeg.pos);
|
|
613
|
-
} else {
|
|
614
|
-
this.writeUInt32BE(fnamesSeg.pos);
|
|
615
|
-
}
|
|
616
|
-
|
|
798
|
+
if (this.shortFieldNamesSeg) {
|
|
799
|
+
this._writeExtendedHeader();
|
|
617
800
|
}
|
|
618
801
|
|
|
619
802
|
// write size of tree segment
|
|
@@ -624,30 +807,16 @@ class OsonEncoder extends GrowableBuffer {
|
|
|
624
807
|
}
|
|
625
808
|
|
|
626
809
|
// write remainder of header and any data (when value is not scalar)
|
|
627
|
-
if (
|
|
810
|
+
if (this.shortFieldNamesSeg) {
|
|
628
811
|
|
|
629
812
|
// write number of "tiny" nodes (always zero)
|
|
630
813
|
this.writeUInt16BE(0);
|
|
631
814
|
|
|
632
|
-
// write
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
// write array of field name offsets
|
|
638
|
-
for (const fieldName of fnamesSeg.fieldNames) {
|
|
639
|
-
if (fnamesSeg.pos < 65536) {
|
|
640
|
-
this.writeUInt16BE(fieldName.offset);
|
|
641
|
-
} else {
|
|
642
|
-
this.writeUInt32BE(fieldName.offset);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// write field names
|
|
647
|
-
if (fnamesSeg.pos > 0) {
|
|
648
|
-
this.writeBytes(fnamesSeg.buf.subarray(0, fnamesSeg.pos));
|
|
815
|
+
// write the field names segments
|
|
816
|
+
this._writeFieldNamesSeg(this.shortFieldNamesSeg);
|
|
817
|
+
if (this.longFieldNamesSeg) {
|
|
818
|
+
this._writeFieldNamesSeg(this.longFieldNamesSeg);
|
|
649
819
|
}
|
|
650
|
-
|
|
651
820
|
}
|
|
652
821
|
|
|
653
822
|
// write tree segment data
|