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.
Files changed (71) hide show
  1. package/LICENSE.txt +1 -1
  2. package/NOTICE.txt +1 -1
  3. package/README.md +3 -3
  4. package/build/Release/oracledb-6.5.0-darwin-x64.node +0 -0
  5. package/build/Release/oracledb-6.5.0-darwin-x64.node-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-6.5.0-js-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-6.5.0-linux-arm64.node +0 -0
  8. package/build/Release/oracledb-6.5.0-linux-arm64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-6.5.0-linux-x64.node +0 -0
  10. package/build/Release/oracledb-6.5.0-linux-x64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-6.5.0-win32-x64.node +0 -0
  12. package/build/Release/oracledb-6.5.0-win32-x64.node-buildinfo.txt +1 -0
  13. package/examples/README.md +3 -0
  14. package/lib/aqQueue.js +8 -8
  15. package/lib/connection.js +65 -22
  16. package/lib/constants.js +7 -1
  17. package/lib/dbObject.js +72 -1
  18. package/lib/errors.js +51 -10
  19. package/lib/impl/connection.js +2 -1
  20. package/lib/{thin/protocol → impl/datahandlers}/buffer.js +36 -32
  21. package/lib/impl/datahandlers/constants.js +106 -0
  22. package/lib/{thin/protocol → impl/datahandlers}/oson.js +320 -151
  23. package/lib/impl/datahandlers/vector.js +137 -0
  24. package/lib/impl/pool.js +9 -0
  25. package/lib/impl/resultset.js +34 -17
  26. package/lib/lob.js +22 -6
  27. package/lib/oracledb.js +35 -1
  28. package/lib/pool.js +17 -2
  29. package/lib/poolStatistics.js +3 -1
  30. package/lib/settings.js +31 -6
  31. package/lib/sodaDocCursor.js +14 -1
  32. package/lib/thin/connection.js +120 -193
  33. package/lib/thin/dbObject.js +4 -2
  34. package/lib/thin/lob.js +6 -6
  35. package/lib/thin/pool.js +84 -10
  36. package/lib/thin/protocol/capabilities.js +20 -6
  37. package/lib/thin/protocol/constants.js +34 -54
  38. package/lib/thin/protocol/messages/auth.js +2 -2
  39. package/lib/thin/protocol/messages/base.js +28 -27
  40. package/lib/thin/protocol/messages/dataType.js +4 -2
  41. package/lib/thin/protocol/messages/execute.js +5 -1
  42. package/lib/thin/protocol/messages/{connectionCookie.js → fastAuth.js} +18 -16
  43. package/lib/thin/protocol/messages/fetch.js +4 -0
  44. package/lib/thin/protocol/messages/index.js +2 -2
  45. package/lib/thin/protocol/messages/protocol.js +9 -1
  46. package/lib/thin/protocol/messages/withData.js +40 -11
  47. package/lib/thin/protocol/packet.js +93 -8
  48. package/lib/thin/protocol/protocol.js +10 -5
  49. package/lib/thin/protocol/utils.js +11 -18
  50. package/lib/thin/sqlnet/constants.js +5 -2
  51. package/lib/thin/sqlnet/ezConnectResolver.js +9 -5
  52. package/lib/thin/sqlnet/navNodes.js +4 -3
  53. package/lib/thin/sqlnet/networkSession.js +27 -4
  54. package/lib/thin/sqlnet/packet.js +2 -5
  55. package/lib/thin/statement.js +43 -7
  56. package/lib/thin/statementCache.js +190 -0
  57. package/lib/thin/util.js +2 -2
  58. package/lib/transformer.js +21 -6
  59. package/lib/types.js +21 -3
  60. package/lib/util.js +30 -1
  61. package/lib/version.js +2 -2
  62. package/package.json +1 -1
  63. package/build/Release/oracledb-6.3.0-darwin-x64.node +0 -0
  64. package/build/Release/oracledb-6.3.0-darwin-x64.node-buildinfo.txt +0 -1
  65. package/build/Release/oracledb-6.3.0-js-buildinfo.txt +0 -1
  66. package/build/Release/oracledb-6.3.0-linux-arm64.node +0 -0
  67. package/build/Release/oracledb-6.3.0-linux-arm64.node-buildinfo.txt +0 -1
  68. package/build/Release/oracledb-6.3.0-linux-x64.node +0 -0
  69. package/build/Release/oracledb-6.3.0-linux-x64.node-buildinfo.txt +0 -1
  70. package/build/Release/oracledb-6.3.0-win32-x64.node +0 -0
  71. 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 decodeing
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
- const offset = this._getOffset(nodeType);
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
- const nodeType = this.readUInt8();
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.TNS_JSON_VERSION) {
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 flags = this.readUInt16BE();
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 (flags & constants.TNS_JSON_FLAG_IS_SCALAR) {
233
- if (flags & constants.TNS_JSON_FLAG_TREE_SEG_UINT32) {
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 numFieldNames;
243
- if (flags & constants.TNS_JSON_FLAG_NUM_FNAMES_UINT32) {
244
- numFieldNames = this.readUInt32BE();
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 (flags & constants.TNS_JSON_FLAG_NUM_FNAMES_UINT16) {
247
- numFieldNames = this.readUInt16BE();
306
+ } else if (primaryFlags & constants.TNS_JSON_FLAG_NUM_FNAMES_UINT16) {
307
+ numShortFieldNames = this.readUInt16BE();
248
308
  this.fieldIdLength = 2;
249
309
  } else {
250
- numFieldNames = this.readUInt8();
310
+ numShortFieldNames = this.readUInt8();
251
311
  this.fieldIdLength = 1;
252
312
  }
253
313
 
254
- // determine the size of the field names segment
255
- let fieldNameOffsetsSize, fieldNamesSegSize;
256
- if (flags & constants.TNS_JSON_FLAG_FNAMES_SEG_UINT32) {
257
- fieldNameOffsetsSize = 4;
258
- fieldNamesSegSize = this.readUInt32BE();
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
- fieldNameOffsetsSize = 2;
261
- fieldNamesSegSize = this.readUInt16BE();
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 (flags & constants.TNS_JSON_FLAG_TREE_SEG_UINT32) {
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
- // skip the hash id array
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
- // skip over the field name offsets and field names
286
- let fieldNameOffsetsPos = this.pos;
287
- this.skipBytes(numFieldNames * fieldNameOffsetsSize);
288
- const fieldNamesPos = this.pos;
289
- this.skipBytes(fieldNamesSegSize);
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
- // determine the names of the fields
292
- this.fieldNames = new Array(numFieldNames);
293
- for (let i = 0; i < numFieldNames; i++) {
294
- let offset = fieldNamesPos;
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 > 255) {
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
- this.hashId = BigInt(0x811C9DC5);
325
- const multiplier = BigInt(16777619);
326
- const mask = BigInt(0xffffffff);
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) * multiplier) & mask;
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(value) {
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
- // _exmaineNode()
404
+ // addName()
348
405
  //
349
- // Examines the value. If it contains fields, unique names are retained. The
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
- _examineNode(value) {
354
- if (Array.isArray(value)) {
355
- for (const element of value) {
356
- this._examineNode(element);
357
- }
358
- } else if (value && Array.isArray(value.fields)) {
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, fnamesSeg) {
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, fnamesSeg);
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, fnamesSeg) {
501
+ _encodeObject(value, encoder) {
458
502
  const numChildren = value.values.length;
459
503
  this._encodeContainer(constants.TNS_JSON_TYPE_OBJECT, numChildren);
460
- const len = numChildren * (fnamesSeg.fieldIdSize + 4);
461
- const pos = this.reserveBytes(len);
462
- let fieldIdOffset = pos;
463
- let valueOffset = pos + (numChildren * fnamesSeg.fieldIdSize);
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 = fnamesSeg.fieldNamesMap.get(value.fields[i]);
467
- if (fnamesSeg.fieldIdSize == 1) {
510
+ const fieldName = encoder.fieldNamesMap.get(value.fields[i]);
511
+ if (encoder.fieldIdSize == 1) {
468
512
  this.buf[fieldIdOffset] = fieldName.fieldId;
469
- } else if (fnamesSeg.fieldIdSize == 2) {
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 += fnamesSeg.fieldIdSize;
519
+ fieldIdOffset += encoder.fieldIdSize;
476
520
  valueOffset += 4;
477
- this.encodeNode(value.values[i], fnamesSeg);
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, fnamesSeg) {
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, fnamesSeg);
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, fnamesSeg);
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
- // determine flags to use
566
- let fnamesSeg;
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
- fnamesSeg = new OsonFieldNamesSegment(value);
572
- if (fnamesSeg.fieldNames.length > 65535) {
762
+ if (totalNumFieldNames > 65535) {
573
763
  flags |= constants.TNS_JSON_FLAG_NUM_FNAMES_UINT32;
574
- } else if (fnamesSeg.fieldNames.length > 255) {
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 (fnamesSeg.pos > 65535) {
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, fnamesSeg);
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.writeUInt8(constants.TNS_JSON_VERSION);
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 (fnamesSeg) {
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 (fnamesSeg) {
810
+ if (this.shortFieldNamesSeg) {
628
811
 
629
812
  // write number of "tiny" nodes (always zero)
630
813
  this.writeUInt16BE(0);
631
814
 
632
- // write array of hash ids
633
- for (const fieldName of fnamesSeg.fieldNames) {
634
- this.writeUInt8(fieldName.hashId);
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