oracledb 6.4.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/README.md +2 -2
- 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/lib/aqQueue.js +1 -1
- package/lib/connection.js +11 -14
- package/lib/errors.js +24 -2
- package/lib/impl/connection.js +2 -1
- package/lib/impl/datahandlers/constants.js +6 -5
- package/lib/impl/datahandlers/oson.js +301 -150
- package/lib/impl/datahandlers/vector.js +3 -22
- package/lib/oracledb.js +11 -1
- package/lib/pool.js +7 -1
- package/lib/settings.js +1 -0
- package/lib/thin/connection.js +23 -52
- package/lib/thin/pool.js +62 -6
- package/lib/thin/protocol/capabilities.js +17 -5
- package/lib/thin/protocol/constants.js +14 -2
- package/lib/thin/protocol/messages/auth.js +1 -1
- package/lib/thin/protocol/messages/base.js +8 -10
- package/lib/thin/protocol/messages/dataType.js +1 -0
- 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 +2 -5
- package/lib/thin/protocol/packet.js +35 -4
- 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 +14 -3
- package/lib/thin/sqlnet/packet.js +2 -5
- package/lib/thin/statement.js +36 -0
- package/lib/thin/statementCache.js +34 -11
- package/lib/thin/util.js +2 -2
- package/lib/transformer.js +9 -3
- package/lib/types.js +9 -1
- package/lib/util.js +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/build/Release/oracledb-6.4.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.4.0-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.4.0-js-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.4.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.4.0-linux-arm64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.4.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.4.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-6.4.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.4.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
|
//
|
|
@@ -36,7 +36,7 @@ const vector = require("./vector.js");
|
|
|
36
36
|
const nodbUtil = require("../../util.js");
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* Class used for
|
|
39
|
+
* Class used for decoding
|
|
40
40
|
*/
|
|
41
41
|
class OsonDecoder extends BaseBuffer {
|
|
42
42
|
|
|
@@ -52,6 +52,7 @@ class OsonDecoder extends BaseBuffer {
|
|
|
52
52
|
// significant bits of the node type; determine the offsets in the tree
|
|
53
53
|
// segment to the field ids array and the value offsets array
|
|
54
54
|
let container, offsetsPos, fieldIdsPos;
|
|
55
|
+
const containerOffset = this.pos - this.treeSegPos - 1;
|
|
55
56
|
let numChildren = this._getNumChildren(nodeType);
|
|
56
57
|
const isObject = ((nodeType & 0x40) === 0);
|
|
57
58
|
if (numChildren === undefined) {
|
|
@@ -86,7 +87,10 @@ class OsonDecoder extends BaseBuffer {
|
|
|
86
87
|
fieldIdsPos += this.fieldIdLength;
|
|
87
88
|
}
|
|
88
89
|
this.pos = offsetsPos;
|
|
89
|
-
|
|
90
|
+
let offset = this._getOffset(nodeType);
|
|
91
|
+
if (this.relativeOffsets) {
|
|
92
|
+
offset += containerOffset;
|
|
93
|
+
}
|
|
90
94
|
offsetsPos = this.pos;
|
|
91
95
|
this.pos = this.treeSegPos + offset;
|
|
92
96
|
if (isObject) {
|
|
@@ -143,6 +147,11 @@ class OsonDecoder extends BaseBuffer {
|
|
|
143
147
|
return this.readBytes(this.readUInt32BE()).toString();
|
|
144
148
|
} else if (nodeType === constants.TNS_JSON_TYPE_NUMBER_LENGTH_UINT8) {
|
|
145
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;
|
|
146
155
|
} else if (nodeType === constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT16) {
|
|
147
156
|
return Buffer.from(this.readBytes(this.readUInt16BE()));
|
|
148
157
|
} else if (nodeType === constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT32) {
|
|
@@ -216,6 +225,46 @@ class OsonDecoder extends BaseBuffer {
|
|
|
216
225
|
}
|
|
217
226
|
}
|
|
218
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
|
+
|
|
219
268
|
//---------------------------------------------------------------------------
|
|
220
269
|
// decode()
|
|
221
270
|
//
|
|
@@ -232,14 +281,16 @@ class OsonDecoder extends BaseBuffer {
|
|
|
232
281
|
errors.throwErr(errors.ERR_UNEXPECTED_DATA, magic.toString('hex'));
|
|
233
282
|
}
|
|
234
283
|
const version = this.readUInt8();
|
|
235
|
-
if (version !== constants.
|
|
284
|
+
if (version !== constants.TNS_JSON_VERSION_MAX_FNAME_255 &&
|
|
285
|
+
version !== constants.TNS_JSON_VERSION_MAX_FNAME_65535) {
|
|
236
286
|
errors.throwErr(errors.ERR_OSON_VERSION_NOT_SUPPORTED, version);
|
|
237
287
|
}
|
|
238
|
-
const
|
|
288
|
+
const primaryFlags = this.readUInt16BE();
|
|
289
|
+
this.relativeOffsets = primaryFlags & constants.TNS_JSON_FLAG_REL_OFFSET_MODE;
|
|
239
290
|
|
|
240
291
|
// scalar values are much simpler
|
|
241
|
-
if (
|
|
242
|
-
if (
|
|
292
|
+
if (primaryFlags & constants.TNS_JSON_FLAG_IS_SCALAR) {
|
|
293
|
+
if (primaryFlags & constants.TNS_JSON_FLAG_TREE_SEG_UINT32) {
|
|
243
294
|
this.skipBytes(4);
|
|
244
295
|
} else {
|
|
245
296
|
this.skipBytes(2);
|
|
@@ -247,31 +298,46 @@ class OsonDecoder extends BaseBuffer {
|
|
|
247
298
|
return this._decodeNode();
|
|
248
299
|
}
|
|
249
300
|
|
|
250
|
-
// determine the number of field names
|
|
251
|
-
let
|
|
252
|
-
if (
|
|
253
|
-
|
|
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();
|
|
254
305
|
this.fieldIdLength = 4;
|
|
255
|
-
} else if (
|
|
256
|
-
|
|
306
|
+
} else if (primaryFlags & constants.TNS_JSON_FLAG_NUM_FNAMES_UINT16) {
|
|
307
|
+
numShortFieldNames = this.readUInt16BE();
|
|
257
308
|
this.fieldIdLength = 2;
|
|
258
309
|
} else {
|
|
259
|
-
|
|
310
|
+
numShortFieldNames = this.readUInt8();
|
|
260
311
|
this.fieldIdLength = 1;
|
|
261
312
|
}
|
|
262
313
|
|
|
263
|
-
// determine the size of the field names segment
|
|
264
|
-
let
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
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();
|
|
268
319
|
} else {
|
|
269
|
-
|
|
270
|
-
|
|
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();
|
|
271
337
|
}
|
|
272
338
|
|
|
273
339
|
// skip the size of the tree segment
|
|
274
|
-
if (
|
|
340
|
+
if (primaryFlags & constants.TNS_JSON_FLAG_TREE_SEG_UINT32) {
|
|
275
341
|
this.skipBytes(4);
|
|
276
342
|
} else {
|
|
277
343
|
this.skipBytes(2);
|
|
@@ -280,37 +346,18 @@ class OsonDecoder extends BaseBuffer {
|
|
|
280
346
|
// skip the number of "tiny" nodes
|
|
281
347
|
this.skipBytes(2);
|
|
282
348
|
|
|
283
|
-
|
|
284
|
-
let hashIdSize;
|
|
285
|
-
if (flags & constants.TNS_JSON_FLAG_HASH_ID_UINT8) {
|
|
286
|
-
hashIdSize = 1;
|
|
287
|
-
} else if (flags & constants.TNS_JSON_FLAG_HASH_ID_UINT16) {
|
|
288
|
-
hashIdSize = 2;
|
|
289
|
-
} else {
|
|
290
|
-
hashIdSize = 4;
|
|
291
|
-
}
|
|
292
|
-
this.skipBytes(numFieldNames * hashIdSize);
|
|
349
|
+
this.fieldNames = new Array(numShortFieldNames + numLongFieldNames);
|
|
293
350
|
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
351
|
+
// if there are any short names, read them now
|
|
352
|
+
if (numShortFieldNames > 0) {
|
|
353
|
+
this._getFieldNames(0, numShortFieldNames,
|
|
354
|
+
shortFieldNameOffsetsSize, shortFieldNamesSegSize, 1);
|
|
355
|
+
}
|
|
299
356
|
|
|
300
|
-
//
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (flags & constants.TNS_JSON_FLAG_FNAMES_SEG_UINT32) {
|
|
305
|
-
offset += this.buf.readUInt32BE(fieldNameOffsetsPos);
|
|
306
|
-
fieldNameOffsetsPos += 4;
|
|
307
|
-
} else {
|
|
308
|
-
offset += this.buf.readUInt16BE(fieldNameOffsetsPos);
|
|
309
|
-
fieldNameOffsetsPos += 2;
|
|
310
|
-
}
|
|
311
|
-
const len = this.buf[offset];
|
|
312
|
-
const name = this.buf.subarray(offset + 1, offset + len + 1).toString();
|
|
313
|
-
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);
|
|
314
361
|
}
|
|
315
362
|
|
|
316
363
|
// determine tree segment position in the buffer
|
|
@@ -324,18 +371,22 @@ class OsonDecoder extends BaseBuffer {
|
|
|
324
371
|
|
|
325
372
|
class OsonFieldName {
|
|
326
373
|
|
|
327
|
-
constructor(name) {
|
|
374
|
+
constructor(name, maxFieldNameSize) {
|
|
328
375
|
this.name = name;
|
|
329
376
|
this.nameBytes = Buffer.from(name);
|
|
330
|
-
if (this.nameBytes.length >
|
|
331
|
-
errors.throwErr(errors.ERR_OSON_FIELD_NAME_LIMITATION);
|
|
377
|
+
if (this.nameBytes.length > maxFieldNameSize) {
|
|
378
|
+
errors.throwErr(errors.ERR_OSON_FIELD_NAME_LIMITATION, maxFieldNameSize);
|
|
332
379
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
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;
|
|
336
387
|
for (let i = 0; i < this.nameBytes.length; i++) {
|
|
337
388
|
const c = BigInt(this.nameBytes[i]);
|
|
338
|
-
this.hashId = ((this.hashId ^ c) *
|
|
389
|
+
this.hashId = ((this.hashId ^ c) * HASH_MULTIPLIER) & HASH_MASK;
|
|
339
390
|
}
|
|
340
391
|
this.hashId = Number(this.hashId) & 0xff;
|
|
341
392
|
}
|
|
@@ -344,41 +395,25 @@ class OsonFieldName {
|
|
|
344
395
|
|
|
345
396
|
class OsonFieldNamesSegment extends GrowableBuffer {
|
|
346
397
|
|
|
347
|
-
constructor(
|
|
398
|
+
constructor() {
|
|
348
399
|
super();
|
|
349
|
-
this.fieldNamesMap = new Map();
|
|
350
400
|
this.fieldNames = [];
|
|
351
|
-
this._examineNode(value);
|
|
352
|
-
this._processFieldNames();
|
|
353
401
|
}
|
|
354
402
|
|
|
355
403
|
//---------------------------------------------------------------------------
|
|
356
|
-
//
|
|
404
|
+
// addName()
|
|
357
405
|
//
|
|
358
|
-
//
|
|
359
|
-
// values are then examined to see if they also contain fields. Arrays are
|
|
360
|
-
// examined to determine they contain elements that contain fields.
|
|
406
|
+
// Adds a name to the field names segment.
|
|
361
407
|
//---------------------------------------------------------------------------
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
for (let i = 0; i < value.fields.length; i++) {
|
|
369
|
-
const name = value.fields[i];
|
|
370
|
-
const element = value.values[i];
|
|
371
|
-
if (!this.fieldNamesMap.has(name)) {
|
|
372
|
-
const fieldName = new OsonFieldName(name);
|
|
373
|
-
this.fieldNamesMap.set(name, fieldName);
|
|
374
|
-
this.fieldNames.push(fieldName);
|
|
375
|
-
fieldName.offset = this.pos;
|
|
376
|
-
this.writeUInt8(fieldName.nameBytes.length);
|
|
377
|
-
this.writeBytes(fieldName.nameBytes);
|
|
378
|
-
}
|
|
379
|
-
this._examineNode(element);
|
|
380
|
-
}
|
|
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);
|
|
381
414
|
}
|
|
415
|
+
this.writeBytes(fieldName.nameBytes);
|
|
416
|
+
this.fieldNames.push(fieldName);
|
|
382
417
|
}
|
|
383
418
|
|
|
384
419
|
//---------------------------------------------------------------------------
|
|
@@ -386,7 +421,7 @@ class OsonFieldNamesSegment extends GrowableBuffer {
|
|
|
386
421
|
//
|
|
387
422
|
// Processes the field names in preparation for encoding within OSON.
|
|
388
423
|
//---------------------------------------------------------------------------
|
|
389
|
-
_processFieldNames() {
|
|
424
|
+
_processFieldNames(fieldIdOffset) {
|
|
390
425
|
this.fieldNames.sort((a, b) => {
|
|
391
426
|
if (a.hashId < b.hashId)
|
|
392
427
|
return -1;
|
|
@@ -403,7 +438,7 @@ class OsonFieldNamesSegment extends GrowableBuffer {
|
|
|
403
438
|
return 0;
|
|
404
439
|
});
|
|
405
440
|
for (let i = 0; i < this.fieldNames.length; i++) {
|
|
406
|
-
this.fieldNames[i].fieldId = i + 1;
|
|
441
|
+
this.fieldNames[i].fieldId = fieldIdOffset + i + 1;
|
|
407
442
|
}
|
|
408
443
|
if (this.fieldNames.length < 256) {
|
|
409
444
|
this.fieldIdSize = 1;
|
|
@@ -423,7 +458,7 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
423
458
|
//
|
|
424
459
|
// Encodes an array in the OSON tree segment.
|
|
425
460
|
//---------------------------------------------------------------------------
|
|
426
|
-
_encodeArray(value,
|
|
461
|
+
_encodeArray(value, encoder) {
|
|
427
462
|
this._encodeContainer(constants.TNS_JSON_TYPE_ARRAY, value.length);
|
|
428
463
|
const len = value.length * 4;
|
|
429
464
|
const pos = this.reserveBytes(len);
|
|
@@ -431,7 +466,7 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
431
466
|
for (const element of value) {
|
|
432
467
|
this.buf.writeUInt32BE(this.pos, offsetsBufPos);
|
|
433
468
|
offsetsBufPos += 4;
|
|
434
|
-
this.encodeNode(element,
|
|
469
|
+
this.encodeNode(element, encoder);
|
|
435
470
|
}
|
|
436
471
|
}
|
|
437
472
|
|
|
@@ -463,27 +498,27 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
463
498
|
//
|
|
464
499
|
// Encodes an object in the OSON tree segment.
|
|
465
500
|
//---------------------------------------------------------------------------
|
|
466
|
-
_encodeObject(value,
|
|
501
|
+
_encodeObject(value, encoder) {
|
|
467
502
|
const numChildren = value.values.length;
|
|
468
503
|
this._encodeContainer(constants.TNS_JSON_TYPE_OBJECT, numChildren);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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);
|
|
473
508
|
|
|
474
509
|
for (let i = 0; i < value.fields.length; i++) {
|
|
475
|
-
const fieldName =
|
|
476
|
-
if (
|
|
510
|
+
const fieldName = encoder.fieldNamesMap.get(value.fields[i]);
|
|
511
|
+
if (encoder.fieldIdSize == 1) {
|
|
477
512
|
this.buf[fieldIdOffset] = fieldName.fieldId;
|
|
478
|
-
} else if (
|
|
513
|
+
} else if (encoder.fieldIdSize == 2) {
|
|
479
514
|
this.buf.writeUInt16BE(fieldName.fieldId, fieldIdOffset);
|
|
480
515
|
} else {
|
|
481
516
|
this.buf.writeUInt32BE(fieldName.fieldId, fieldIdOffset);
|
|
482
517
|
}
|
|
483
518
|
this.buf.writeUInt32BE(this.pos, valueOffset);
|
|
484
|
-
fieldIdOffset +=
|
|
519
|
+
fieldIdOffset += encoder.fieldIdSize;
|
|
485
520
|
valueOffset += 4;
|
|
486
|
-
this.encodeNode(value.values[i],
|
|
521
|
+
this.encodeNode(value.values[i], encoder);
|
|
487
522
|
}
|
|
488
523
|
}
|
|
489
524
|
|
|
@@ -492,7 +527,7 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
492
527
|
//
|
|
493
528
|
// Encodes a value (node) in the OSON tree segment.
|
|
494
529
|
//---------------------------------------------------------------------------
|
|
495
|
-
encodeNode(value,
|
|
530
|
+
encodeNode(value, encoder) {
|
|
496
531
|
|
|
497
532
|
// handle null
|
|
498
533
|
if (value === undefined || value === null) {
|
|
@@ -529,7 +564,7 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
529
564
|
}
|
|
530
565
|
|
|
531
566
|
// handle dates
|
|
532
|
-
} else if (util.isDate(value)) {
|
|
567
|
+
} else if (util.types.isDate(value)) {
|
|
533
568
|
if (value.getUTCMilliseconds() === 0) {
|
|
534
569
|
this.writeUInt8(constants.TNS_JSON_TYPE_TIMESTAMP7);
|
|
535
570
|
this.writeOracleDate(value, types.DB_TYPE_DATE, false);
|
|
@@ -551,7 +586,7 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
551
586
|
|
|
552
587
|
// handle arrays
|
|
553
588
|
} else if (Array.isArray(value)) {
|
|
554
|
-
this._encodeArray(value,
|
|
589
|
+
this._encodeArray(value, encoder);
|
|
555
590
|
|
|
556
591
|
// handle vectors
|
|
557
592
|
} else if (nodbUtil.isVectorValue(value)) {
|
|
@@ -562,46 +597,188 @@ class OsonTreeSegment extends GrowableBuffer {
|
|
|
562
597
|
this.writeUInt32BE(buf.length);
|
|
563
598
|
this.writeBytes(buf);
|
|
564
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));
|
|
604
|
+
|
|
565
605
|
// handle objects
|
|
566
606
|
} else {
|
|
567
|
-
this._encodeObject(value,
|
|
607
|
+
this._encodeObject(value, encoder);
|
|
568
608
|
}
|
|
569
609
|
|
|
570
610
|
}
|
|
571
611
|
|
|
572
612
|
}
|
|
573
613
|
|
|
614
|
+
/**
|
|
615
|
+
* Class used for encoding
|
|
616
|
+
*/
|
|
617
|
+
|
|
574
618
|
class OsonEncoder extends GrowableBuffer {
|
|
575
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
|
+
|
|
576
728
|
//---------------------------------------------------------------------------
|
|
577
729
|
// encode()
|
|
578
730
|
//
|
|
579
731
|
// Encodes the value as OSON and returns a buffer containing the OSON bytes.
|
|
580
732
|
//---------------------------------------------------------------------------
|
|
581
|
-
encode(value) {
|
|
733
|
+
encode(value, maxFieldNameSize) {
|
|
582
734
|
|
|
583
|
-
|
|
584
|
-
|
|
735
|
+
this.maxFieldNameSize = maxFieldNameSize;
|
|
736
|
+
|
|
737
|
+
// determine the flags to use
|
|
585
738
|
let flags = constants.TNS_JSON_FLAG_INLINE_LEAF;
|
|
586
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
|
|
587
760
|
flags |= constants.TNS_JSON_FLAG_HASH_ID_UINT8 |
|
|
588
761
|
constants.TNS_JSON_FLAG_TINY_NODES_STAT;
|
|
589
|
-
|
|
590
|
-
if (fnamesSeg.fieldNames.length > 65535) {
|
|
762
|
+
if (totalNumFieldNames > 65535) {
|
|
591
763
|
flags |= constants.TNS_JSON_FLAG_NUM_FNAMES_UINT32;
|
|
592
|
-
|
|
764
|
+
this.fieldIdSize = 4;
|
|
765
|
+
} else if (totalNumFieldNames > 255) {
|
|
593
766
|
flags |= constants.TNS_JSON_FLAG_NUM_FNAMES_UINT16;
|
|
767
|
+
this.fieldIdSize = 2;
|
|
768
|
+
} else {
|
|
769
|
+
this.fieldIdSize = 1;
|
|
594
770
|
}
|
|
595
|
-
if (
|
|
771
|
+
if (this.shortFieldNamesSeg.pos > 65535) {
|
|
596
772
|
flags |= constants.TNS_JSON_FLAG_FNAMES_SEG_UINT32;
|
|
597
773
|
}
|
|
598
774
|
} else {
|
|
775
|
+
// if the value is a simple scalar
|
|
599
776
|
flags |= constants.TNS_JSON_FLAG_IS_SCALAR;
|
|
600
777
|
}
|
|
601
778
|
|
|
602
|
-
// encode values into the tree segment
|
|
779
|
+
// encode values into the OSON tree segment
|
|
603
780
|
const treeSeg = new OsonTreeSegment();
|
|
604
|
-
treeSeg.encodeNode(value,
|
|
781
|
+
treeSeg.encodeNode(value, this);
|
|
605
782
|
if (treeSeg.pos > 65535) {
|
|
606
783
|
flags |= constants.TNS_JSON_FLAG_TREE_SEG_UINT32;
|
|
607
784
|
}
|
|
@@ -610,28 +787,16 @@ class OsonEncoder extends GrowableBuffer {
|
|
|
610
787
|
this.writeUInt8(constants.TNS_JSON_MAGIC_BYTE_1);
|
|
611
788
|
this.writeUInt8(constants.TNS_JSON_MAGIC_BYTE_2);
|
|
612
789
|
this.writeUInt8(constants.TNS_JSON_MAGIC_BYTE_3);
|
|
613
|
-
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
|
+
}
|
|
614
795
|
this.writeUInt16BE(flags);
|
|
615
796
|
|
|
616
797
|
// write extended header (when value is not scalar)
|
|
617
|
-
if (
|
|
618
|
-
|
|
619
|
-
// write number of field names
|
|
620
|
-
if (fnamesSeg.fieldNames.length < 256) {
|
|
621
|
-
this.writeUInt8(fnamesSeg.fieldNames.length);
|
|
622
|
-
} else if (fnamesSeg.fieldNames.length < 65536) {
|
|
623
|
-
this.writeUInt16BE(fnamesSeg.fieldNames.length);
|
|
624
|
-
} else {
|
|
625
|
-
this.writeUInt32BE(fnamesSeg.fieldNames.length);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// write size of field names segment
|
|
629
|
-
if (fnamesSeg.pos < 65536) {
|
|
630
|
-
this.writeUInt16BE(fnamesSeg.pos);
|
|
631
|
-
} else {
|
|
632
|
-
this.writeUInt32BE(fnamesSeg.pos);
|
|
633
|
-
}
|
|
634
|
-
|
|
798
|
+
if (this.shortFieldNamesSeg) {
|
|
799
|
+
this._writeExtendedHeader();
|
|
635
800
|
}
|
|
636
801
|
|
|
637
802
|
// write size of tree segment
|
|
@@ -642,30 +807,16 @@ class OsonEncoder extends GrowableBuffer {
|
|
|
642
807
|
}
|
|
643
808
|
|
|
644
809
|
// write remainder of header and any data (when value is not scalar)
|
|
645
|
-
if (
|
|
810
|
+
if (this.shortFieldNamesSeg) {
|
|
646
811
|
|
|
647
812
|
// write number of "tiny" nodes (always zero)
|
|
648
813
|
this.writeUInt16BE(0);
|
|
649
814
|
|
|
650
|
-
// write
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
// write array of field name offsets
|
|
656
|
-
for (const fieldName of fnamesSeg.fieldNames) {
|
|
657
|
-
if (fnamesSeg.pos < 65536) {
|
|
658
|
-
this.writeUInt16BE(fieldName.offset);
|
|
659
|
-
} else {
|
|
660
|
-
this.writeUInt32BE(fieldName.offset);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// write field names
|
|
665
|
-
if (fnamesSeg.pos > 0) {
|
|
666
|
-
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);
|
|
667
819
|
}
|
|
668
|
-
|
|
669
820
|
}
|
|
670
821
|
|
|
671
822
|
// write tree segment data
|