oracledb 5.5.0 → 6.0.1

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 (107) hide show
  1. package/CHANGELOG.md +1 -1875
  2. package/LICENSE.txt +8 -222
  3. package/NOTICE.txt +1 -1
  4. package/README.md +38 -30
  5. package/SECURITY.md +38 -0
  6. package/build/Release/oracledb-6.0.1-darwin-x64.node +0 -0
  7. package/build/Release/oracledb-6.0.1-darwin-x64.node-buildinfo.txt +1 -0
  8. package/build/Release/oracledb-6.0.1-js-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-6.0.1-linux-arm64.node +0 -0
  10. package/build/Release/oracledb-6.0.1-linux-arm64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-6.0.1-linux-x64.node +0 -0
  12. package/build/Release/oracledb-6.0.1-linux-x64.node-buildinfo.txt +1 -0
  13. package/build/Release/oracledb-6.0.1-win32-x64.node +0 -0
  14. package/build/Release/oracledb-6.0.1-win32-x64.node-buildinfo.txt +1 -0
  15. package/examples/README.md +113 -0
  16. package/examples/dbconfig.js +100 -0
  17. package/examples/example.js +156 -0
  18. package/index.js +26 -0
  19. package/lib/aqDeqOptions.js +148 -8
  20. package/lib/aqEnqOptions.js +59 -7
  21. package/lib/aqMessage.js +118 -7
  22. package/lib/aqQueue.js +243 -53
  23. package/lib/connection.js +1326 -465
  24. package/lib/constants.js +203 -0
  25. package/lib/dbObject.js +346 -32
  26. package/lib/errors.js +785 -0
  27. package/lib/impl/aqDeqOptions.js +197 -0
  28. package/lib/impl/aqEnqOptions.js +89 -0
  29. package/lib/impl/aqMessage.js +134 -0
  30. package/lib/impl/aqQueue.js +81 -0
  31. package/lib/impl/connection.js +529 -0
  32. package/lib/impl/dbObject.js +159 -0
  33. package/lib/impl/index.js +59 -0
  34. package/lib/impl/lob.js +85 -0
  35. package/lib/impl/pool.js +195 -0
  36. package/lib/impl/resultset.js +223 -0
  37. package/lib/impl/sodaCollection.js +174 -0
  38. package/lib/impl/sodaDatabase.js +81 -0
  39. package/lib/impl/sodaDocCursor.js +63 -0
  40. package/lib/impl/sodaDocument.js +98 -0
  41. package/lib/impl/sodaOperation.js +109 -0
  42. package/lib/lob.js +157 -68
  43. package/lib/oracledb.js +1047 -345
  44. package/lib/pool.js +654 -465
  45. package/lib/poolStatistics.js +24 -9
  46. package/lib/queryStream.js +16 -8
  47. package/lib/resultset.js +291 -183
  48. package/lib/settings.js +191 -0
  49. package/lib/sodaCollection.js +214 -201
  50. package/lib/sodaDatabase.js +108 -71
  51. package/lib/sodaDocCursor.js +44 -32
  52. package/lib/sodaDocument.js +76 -14
  53. package/lib/sodaOperation.js +131 -118
  54. package/lib/thin/connection.js +1043 -0
  55. package/lib/thin/dbObject.js +630 -0
  56. package/lib/thin/index.js +40 -0
  57. package/lib/thin/lob.js +189 -0
  58. package/lib/thin/pool.js +481 -0
  59. package/lib/thin/protocol/buffer.js +1043 -0
  60. package/lib/thin/protocol/capabilities.js +121 -0
  61. package/lib/thin/protocol/constants.js +765 -0
  62. package/lib/thin/protocol/encryptDecrypt.js +185 -0
  63. package/lib/thin/protocol/messages/auth.js +349 -0
  64. package/lib/thin/protocol/messages/base.js +476 -0
  65. package/lib/thin/protocol/messages/commit.js +56 -0
  66. package/lib/thin/protocol/messages/dataType.js +391 -0
  67. package/lib/thin/protocol/messages/execute.js +312 -0
  68. package/lib/thin/protocol/messages/fetch.js +60 -0
  69. package/lib/thin/protocol/messages/index.js +53 -0
  70. package/lib/thin/protocol/messages/lobOp.js +201 -0
  71. package/lib/thin/protocol/messages/logOff.js +53 -0
  72. package/lib/thin/protocol/messages/ping.js +56 -0
  73. package/lib/thin/protocol/messages/protocol.js +78 -0
  74. package/lib/thin/protocol/messages/rollback.js +56 -0
  75. package/lib/thin/protocol/messages/sessionRelease.js +60 -0
  76. package/lib/thin/protocol/messages/withData.js +812 -0
  77. package/lib/thin/protocol/oson.js +661 -0
  78. package/lib/thin/protocol/packet.js +519 -0
  79. package/lib/thin/protocol/protocol.js +202 -0
  80. package/lib/thin/protocol/utils.js +89 -0
  81. package/lib/thin/resultSet.js +122 -0
  82. package/lib/thin/sqlnet/connStrategy.js +175 -0
  83. package/lib/thin/sqlnet/constants.js +199 -0
  84. package/lib/thin/sqlnet/ezConnectResolver.js +488 -0
  85. package/lib/thin/sqlnet/navNodes.js +870 -0
  86. package/lib/thin/sqlnet/networkSession.js +621 -0
  87. package/lib/thin/sqlnet/ntTcp.js +527 -0
  88. package/lib/thin/sqlnet/nvStrToNvPair.js +783 -0
  89. package/lib/thin/sqlnet/packet.js +405 -0
  90. package/lib/thin/sqlnet/paramParser.js +206 -0
  91. package/lib/thin/sqlnet/sessionAtts.js +160 -0
  92. package/lib/thin/statement.js +300 -0
  93. package/lib/thin/util.js +289 -0
  94. package/lib/transformer.js +252 -0
  95. package/lib/types.js +357 -0
  96. package/lib/util.js +196 -110
  97. package/lib/version.js +36 -0
  98. package/package/README.md +33 -30
  99. package/package/install.js +89 -64
  100. package/package/prunebinaries.js +60 -12
  101. package/package.json +3 -3
  102. package/build/Release/oracledb-5.5.0-darwin-x64.node +0 -0
  103. package/build/Release/oracledb-5.5.0-darwin-x64.node-buildinfo.txt +0 -1
  104. package/build/Release/oracledb-5.5.0-linux-x64.node +0 -0
  105. package/build/Release/oracledb-5.5.0-linux-x64.node-buildinfo.txt +0 -1
  106. package/build/Release/oracledb-5.5.0-win32-x64.node +0 -0
  107. package/build/Release/oracledb-5.5.0-win32-x64.node-buildinfo.txt +0 -1
@@ -0,0 +1,661 @@
1
+ // Copyright (c) 2023, Oracle and/or its affiliates.
2
+
3
+ //-----------------------------------------------------------------------------
4
+ //
5
+ // This software is dual-licensed to you under the Universal Permissive License
6
+ // (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
7
+ // 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
8
+ // either license.
9
+ //
10
+ // If you elect to accept the software under the Apache License, Version 2.0,
11
+ // the following applies:
12
+ //
13
+ // Licensed under the Apache License, Version 2.0 (the "License");
14
+ // you may not use this file except in compliance with the License.
15
+ // You may obtain a copy of the License at
16
+ //
17
+ // https://www.apache.org/licenses/LICENSE-2.0
18
+ //
19
+ // Unless required by applicable law or agreed to in writing, software
20
+ // distributed under the License is distributed on an "AS IS" BASIS,
21
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
+ // See the License for the specific language governing permissions and
23
+ // limitations under the License.
24
+ //
25
+ //-----------------------------------------------------------------------------
26
+
27
+ 'use strict';
28
+
29
+ const { BaseBuffer, GrowableBuffer } = require('./buffer.js');
30
+ const { Buffer } = require('buffer');
31
+ const constants = require("./constants.js");
32
+ const errors = require("../../errors.js");
33
+ const types = require("../../types.js");
34
+ const util = require("util");
35
+
36
+ /**
37
+ * Class used for decodeing
38
+ */
39
+ class OsonDecoder extends BaseBuffer {
40
+
41
+ //---------------------------------------------------------------------------
42
+ // _decodeContainerNode()
43
+ //
44
+ // Decodes a container node (object or array) from the tree segment and
45
+ // returns the JavaScript equivalent.
46
+ //---------------------------------------------------------------------------
47
+ _decodeContainerNode(nodeType) {
48
+
49
+ // determine the number of children by examining the 4th and 5th most
50
+ // significant bits of the node type; determine the offsets in the tree
51
+ // segment to the field ids array and the value offsets array
52
+ let container, offsetsPos, fieldIdsPos;
53
+ let numChildren = this._getNumChildren(nodeType);
54
+ const isObject = ((nodeType & 0x40) === 0);
55
+ if (numChildren === undefined) {
56
+ const offset = this._getOffset(nodeType);
57
+ offsetsPos = this.pos;
58
+ this.pos = this.treeSegPos + offset;
59
+ const sharedNodeType = this.readUInt8();
60
+ numChildren = this._getNumChildren(sharedNodeType);
61
+ container = (isObject) ? {} : new Array(numChildren);
62
+ fieldIdsPos = this.pos;
63
+ } else if (isObject) {
64
+ container = {};
65
+ fieldIdsPos = this.pos;
66
+ offsetsPos = this.pos + this.fieldIdLength * numChildren;
67
+ } else {
68
+ container = new Array(numChildren);
69
+ offsetsPos = this.pos;
70
+ }
71
+
72
+ for (let i = 0; i < numChildren; i++) {
73
+ let name;
74
+ if (isObject) {
75
+ let fieldId;
76
+ if (this.fieldIdLength === 1) {
77
+ fieldId = this.buf[fieldIdsPos];
78
+ } else if (this.fieldIdLength == 2) {
79
+ fieldId = this.buf.readUInt16BE(fieldIdsPos);
80
+ } else {
81
+ fieldId = this.buf.readUInt32BE(fieldIdsPos);
82
+ }
83
+ name = this.fieldNames[fieldId - 1];
84
+ fieldIdsPos += this.fieldIdLength;
85
+ }
86
+ this.pos = offsetsPos;
87
+ const offset = this._getOffset(nodeType);
88
+ offsetsPos = this.pos;
89
+ this.pos = this.treeSegPos + offset;
90
+ if (isObject) {
91
+ container[name] = this._decodeNode();
92
+ } else {
93
+ container[i] = this._decodeNode();
94
+ }
95
+ }
96
+
97
+ return container;
98
+ }
99
+
100
+ //---------------------------------------------------------------------------
101
+ // _decodeNode()
102
+ //
103
+ // Decodes a node from the tree segment and returns the JavaScript
104
+ // equivalent.
105
+ //---------------------------------------------------------------------------
106
+ _decodeNode() {
107
+
108
+ // if the most significant bit is set the node refers to a container
109
+ const nodeType = this.readUInt8();
110
+ if (nodeType & 0x80) {
111
+ return this._decodeContainerNode(nodeType);
112
+ }
113
+
114
+ // handle simple scalars
115
+ if (nodeType === constants.TNS_JSON_TYPE_NULL) {
116
+ return null;
117
+ } else if (nodeType === constants.TNS_JSON_TYPE_TRUE) {
118
+ return true;
119
+ } else if (nodeType === constants.TNS_JSON_TYPE_FALSE) {
120
+ return false;
121
+
122
+ // handle fixed length scalars
123
+ } else if (nodeType === constants.TNS_JSON_TYPE_DATE ||
124
+ nodeType === constants.TNS_JSON_TYPE_TIMESTAMP7) {
125
+ return this.parseOracleDate(this.readBytes(7));
126
+ } else if (nodeType === constants.TNS_JSON_TYPE_TIMESTAMP) {
127
+ return this.parseOracleDate(this.readBytes(11));
128
+ } else if (nodeType === constants.TNS_JSON_TYPE_TIMESTAMP_TZ) {
129
+ return this.parseOracleDate(this.readBytes(13));
130
+ } else if (nodeType === constants.TNS_JSON_TYPE_BINARY_FLOAT) {
131
+ return this.parseBinaryFloat(this.readBytes(4));
132
+ } else if (nodeType === constants.TNS_JSON_TYPE_BINARY_DOUBLE) {
133
+ return this.parseBinaryDouble(this.readBytes(8));
134
+
135
+ // handle scalars with lengths stored outside the node itself
136
+ } else if (nodeType === constants.TNS_JSON_TYPE_STRING_LENGTH_UINT8) {
137
+ return this.readBytes(this.readUInt8()).toString();
138
+ } else if (nodeType === constants.TNS_JSON_TYPE_STRING_LENGTH_UINT16) {
139
+ return this.readBytes(this.readUInt16BE()).toString();
140
+ } else if (nodeType === constants.TNS_JSON_TYPE_STRING_LENGTH_UINT32) {
141
+ return this.readBytes(this.readUInt32BE()).toString();
142
+ } else if (nodeType === constants.TNS_JSON_TYPE_NUMBER_LENGTH_UINT8) {
143
+ return parseFloat(this.readOracleNumber());
144
+ } else if (nodeType === constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT16) {
145
+ return Buffer.from(this.readBytes(this.readUInt16BE()));
146
+ } else if (nodeType === constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT32) {
147
+ return Buffer.from(this.readBytes(this.readUInt32BE()));
148
+ }
149
+
150
+ // handle number/decimal with length stored inside the node itself
151
+ const typeBits = nodeType & 0xf0;
152
+ if (typeBits === 0x20 || typeBits === 0x60) {
153
+ const len = nodeType & 0x0f;
154
+ return parseFloat(this.parseOracleNumber(this.readBytes(len + 1)));
155
+
156
+ // handle integer with length stored inside the node itself
157
+ } else if (typeBits === 0x40 || typeBits === 0x50) {
158
+ const len = nodeType & 0x0f;
159
+ return parseFloat(this.parseOracleNumber(this.readBytes(len)));
160
+
161
+ // handle string with length stored inside the node itself
162
+ } else if ((nodeType & 0xe0) == 0) {
163
+ if (nodeType === 0)
164
+ return '';
165
+ return this.readBytes(nodeType).toString();
166
+ }
167
+
168
+ errors.throwErr(errors.ERR_UNSUPPORTED_DATA_TYPE_IN_JSON, nodeType);
169
+ }
170
+
171
+ //---------------------------------------------------------------------------
172
+ // _getNumChildren()
173
+ //
174
+ // Returns the number of children a container has. This is determined by
175
+ // looking at the 4th and 5th most significant bits of the node type.
176
+ //
177
+ // 00 - number of children is uint8_t
178
+ // 01 - number of children is uint16_t
179
+ // 10 - number of children is uint32_t
180
+ // 11 - field ids are shared with another object whose offset follows
181
+ //
182
+ // In the latter case the value undefined is returned and the number of
183
+ // children must be read from the shared object at the specified offset.
184
+ //---------------------------------------------------------------------------
185
+ _getNumChildren(nodeType) {
186
+ const childrenBits = (nodeType & 0x18);
187
+ if (childrenBits === 0) {
188
+ return this.readUInt8();
189
+ } else if (childrenBits === 0x08) {
190
+ return this.readUInt16BE();
191
+ } else if (childrenBits === 0x10) {
192
+ return this.readUInt32BE();
193
+ }
194
+ }
195
+
196
+ //---------------------------------------------------------------------------
197
+ // _getOffset()
198
+ //
199
+ // Returns an offset. The offset will be either a 16-bit or 32-bit value
200
+ // depending on the value of the 3rd significant bit of the node type.
201
+ //---------------------------------------------------------------------------
202
+ _getOffset(nodeType) {
203
+ if (nodeType & 0x20) {
204
+ return this.readUInt32BE();
205
+ } else {
206
+ return this.readUInt16BE();
207
+ }
208
+ }
209
+
210
+ //---------------------------------------------------------------------------
211
+ // decode()
212
+ //
213
+ // Decodes the OSON and returns a JavaScript object corresponding to its
214
+ // contents.
215
+ //---------------------------------------------------------------------------
216
+ decode() {
217
+
218
+ // parse root header
219
+ const magic = this.readBytes(3);
220
+ if (magic[0] !== constants.TNS_JSON_MAGIC_BYTE_1 ||
221
+ magic[1] !== constants.TNS_JSON_MAGIC_BYTE_2 ||
222
+ magic[2] !== constants.TNS_JSON_MAGIC_BYTE_3) {
223
+ errors.throwErr(errors.ERR_UNEXPECTED_DATA, magic.toString('hex'));
224
+ }
225
+ const version = this.readUInt8();
226
+ if (version !== constants.TNS_JSON_VERSION) {
227
+ errors.throwErr(errors.ERR_OSON_VERSION_NOT_SUPPORTED, version);
228
+ }
229
+ const flags = this.readUInt16BE();
230
+
231
+ // scalar values are much simpler
232
+ if (flags & constants.TNS_JSON_FLAG_IS_SCALAR) {
233
+ if (flags & constants.TNS_JSON_FLAG_TREE_SEG_UINT32) {
234
+ this.skipBytes(4);
235
+ } else {
236
+ this.skipBytes(2);
237
+ }
238
+ return this._decodeNode();
239
+ }
240
+
241
+ // determine the number of field names
242
+ let numFieldNames;
243
+ if (flags & constants.TNS_JSON_FLAG_NUM_FNAMES_UINT32) {
244
+ numFieldNames = this.readUInt32BE();
245
+ this.fieldIdLength = 4;
246
+ } else if (flags & constants.TNS_JSON_FLAG_NUM_FNAMES_UINT16) {
247
+ numFieldNames = this.readUInt16BE();
248
+ this.fieldIdLength = 2;
249
+ } else {
250
+ numFieldNames = this.readUInt8();
251
+ this.fieldIdLength = 1;
252
+ }
253
+
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();
259
+ } else {
260
+ fieldNameOffsetsSize = 2;
261
+ fieldNamesSegSize = this.readUInt16BE();
262
+ }
263
+
264
+ // skip the size of the tree segment
265
+ if (flags & constants.TNS_JSON_FLAG_TREE_SEG_UINT32) {
266
+ this.skipBytes(4);
267
+ } else {
268
+ this.skipBytes(2);
269
+ }
270
+
271
+ // skip the number of "tiny" nodes
272
+ this.skipBytes(2);
273
+
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);
284
+
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);
290
+
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;
305
+ }
306
+
307
+ // determine tree segment position in the buffer
308
+ this.treeSegPos = this.pos;
309
+
310
+ // decode the root node
311
+ return this._decodeNode();
312
+ }
313
+
314
+ }
315
+
316
+ class OsonFieldName {
317
+
318
+ constructor(name) {
319
+ this.name = name;
320
+ this.nameBytes = Buffer.from(name);
321
+ if (this.nameBytes.length > 255) {
322
+ errors.throwErr(errors.ERR_OSON_FIELD_NAME_LIMITATION);
323
+ }
324
+ this.hashId = BigInt(0x811C9DC5);
325
+ const multiplier = BigInt(16777619);
326
+ const mask = BigInt(0xffffffff);
327
+ for (let i = 0; i < this.nameBytes.length; i++) {
328
+ const c = BigInt(this.nameBytes[i]);
329
+ this.hashId = ((this.hashId ^ c) * multiplier) & mask;
330
+ }
331
+ this.hashId = Number(this.hashId) & 0xff;
332
+ }
333
+
334
+ }
335
+
336
+ class OsonFieldNamesSegment extends GrowableBuffer {
337
+
338
+ constructor(value) {
339
+ super();
340
+ this.fieldNamesMap = new Map();
341
+ this.fieldNames = [];
342
+ this._examineNode(value);
343
+ this._processFieldNames();
344
+ }
345
+
346
+ //---------------------------------------------------------------------------
347
+ // _exmaineNode()
348
+ //
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.
352
+ //---------------------------------------------------------------------------
353
+ _examineNode(value) {
354
+ if (Array.isArray(value)) {
355
+ for (let 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
+ }
372
+ }
373
+ }
374
+
375
+ //---------------------------------------------------------------------------
376
+ // _processFieldNames()
377
+ //
378
+ // Processes the field names in preparation for encoding within OSON.
379
+ //---------------------------------------------------------------------------
380
+ _processFieldNames() {
381
+ this.fieldNames.sort((a, b) => {
382
+ if (a.hashId < b.hashId)
383
+ return -1;
384
+ if (a.hashId > b.hashId)
385
+ return 1;
386
+ if (a.nameBytes.length < b.nameBytes.length)
387
+ return -1;
388
+ if (a.nameBytes.length > b.nameBytes.length)
389
+ return 1;
390
+ if (a.name < b.name)
391
+ return -1;
392
+ if (a.name > b.name)
393
+ return 1;
394
+ return 0;
395
+ });
396
+ for (let i = 0; i < this.fieldNames.length; i++) {
397
+ this.fieldNames[i].fieldId = i + 1;
398
+ }
399
+ if (this.fieldNames.length < 256) {
400
+ this.fieldIdSize = 1;
401
+ } else if (this.fieldNames.length < 65536) {
402
+ this.fieldIdSize = 2;
403
+ } else {
404
+ this.fieldIdSize = 4;
405
+ }
406
+ }
407
+
408
+ }
409
+
410
+ class OsonTreeSegment extends GrowableBuffer {
411
+
412
+ //---------------------------------------------------------------------------
413
+ // _encodeArray()
414
+ //
415
+ // Encodes an array in the OSON tree segment.
416
+ //---------------------------------------------------------------------------
417
+ _encodeArray(value, fnamesSeg) {
418
+ this._encodeContainer(constants.TNS_JSON_TYPE_ARRAY, value.length);
419
+ let offsetsBufPos = 0;
420
+ const offsetsBuf = this.reserveBytes(value.length * 4);
421
+ for (let element of value) {
422
+ offsetsBuf.writeUInt32BE(this.pos, offsetsBufPos);
423
+ offsetsBufPos += 4;
424
+ this.encodeNode(element, fnamesSeg);
425
+ }
426
+ }
427
+
428
+ //---------------------------------------------------------------------------
429
+ // _encodeContainer()
430
+ //
431
+ // Encodes the first part of a container (array or object) in the OSON tree
432
+ // segment.
433
+ //---------------------------------------------------------------------------
434
+ _encodeContainer(nodeType, numChildren) {
435
+ nodeType |= 0x20; // use uint32_t for offsets
436
+ if (numChildren > 65535) {
437
+ nodeType |= 0x10; // num children is uint32_t
438
+ } else if (numChildren > 255) {
439
+ nodeType |= 0x08; // num children is uint16_t
440
+ }
441
+ this.writeUInt8(nodeType);
442
+ if (numChildren < 256) {
443
+ this.writeUInt8(numChildren);
444
+ } else if (numChildren < 65536) {
445
+ this.writeUInt16BE(numChildren);
446
+ } else {
447
+ this.writeUInt32BE(numChildren);
448
+ }
449
+ }
450
+
451
+ //---------------------------------------------------------------------------
452
+ // _encodeObject()
453
+ //
454
+ // Encodes an object in the OSON tree segment.
455
+ //---------------------------------------------------------------------------
456
+ _encodeObject(value, fnamesSeg) {
457
+ const numChildren = value.values.length;
458
+ this._encodeContainer(constants.TNS_JSON_TYPE_OBJECT, numChildren);
459
+ let fieldIdOffset = 0;
460
+ let valueOffset = numChildren * fnamesSeg.fieldIdSize;
461
+ const buf = this.reserveBytes(numChildren * (fnamesSeg.fieldIdSize + 4));
462
+ for (let i = 0; i < value.fields.length; i++) {
463
+ const fieldName = fnamesSeg.fieldNamesMap.get(value.fields[i]);
464
+ if (fnamesSeg.fieldIdSize == 1) {
465
+ buf[fieldIdOffset] = fieldName.fieldId;
466
+ } else if (fnamesSeg.fieldIdSize == 2) {
467
+ buf.writeUInt16BE(fieldName.fieldId, fieldIdOffset);
468
+ } else {
469
+ buf.writeUInt32BE(fieldName.fieldId, fieldIdOffset);
470
+ }
471
+ buf.writeUInt32BE(this.pos, valueOffset);
472
+ fieldIdOffset += fnamesSeg.fieldIdSize;
473
+ valueOffset += 4;
474
+ this.encodeNode(value.values[i], fnamesSeg);
475
+ }
476
+ }
477
+
478
+ //---------------------------------------------------------------------------
479
+ // encodeNode()
480
+ //
481
+ // Encodes a value (node) in the OSON tree segment.
482
+ //---------------------------------------------------------------------------
483
+ encodeNode(value, fnamesSeg) {
484
+
485
+ // handle null
486
+ if (value === undefined || value === null) {
487
+ this.writeUInt8(constants.TNS_JSON_TYPE_NULL);
488
+
489
+ // handle booleans
490
+ } else if (typeof value === 'boolean') {
491
+ if (value) {
492
+ this.writeUInt8(constants.TNS_JSON_TYPE_TRUE);
493
+ } else {
494
+ this.writeUInt8(constants.TNS_JSON_TYPE_FALSE);
495
+ }
496
+
497
+ // handle numbers
498
+ } else if (typeof value === 'number') {
499
+ this.writeUInt8(constants.TNS_JSON_TYPE_NUMBER_LENGTH_UINT8);
500
+ this.writeOracleNumber(value.toString());
501
+
502
+ // handle strings
503
+ } else if (typeof value === 'string') {
504
+ const buf = Buffer.from(value);
505
+ if (buf.length < 256) {
506
+ this.writeUInt8(constants.TNS_JSON_TYPE_STRING_LENGTH_UINT8);
507
+ this.writeUInt8(buf.length);
508
+ } else if (buf.length < 65536) {
509
+ this.writeUInt8(constants.TNS_JSON_TYPE_STRING_LENGTH_UINT16);
510
+ this.writeUInt16BE(buf.length);
511
+ } else {
512
+ this.writeUInt8(constants.TNS_JSON_TYPE_STRING_LENGTH_UINT32);
513
+ this.writeUInt32BE(buf.length);
514
+ }
515
+ if (buf.length > 0) {
516
+ this.writeBytes(buf);
517
+ }
518
+
519
+ // handle dates
520
+ } else if (util.isDate(value)) {
521
+ if (value.getUTCMilliseconds() === 0) {
522
+ this.writeUInt8(constants.TNS_JSON_TYPE_TIMESTAMP7);
523
+ this.writeOracleDate(value, types.DB_TYPE_DATE, false);
524
+ } else {
525
+ this.writeUInt8(constants.TNS_JSON_TYPE_TIMESTAMP);
526
+ this.writeOracleDate(value, types.DB_TYPE_TIMESTAMP, false);
527
+ }
528
+
529
+ // handle buffers
530
+ } else if (Buffer.isBuffer(value)) {
531
+ if (value.length < 65536) {
532
+ this.writeUInt8(constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT16);
533
+ this.writeUInt16BE(value.length);
534
+ } else {
535
+ this.writeUInt8(constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT32);
536
+ this.writeUInt32BE(value.length);
537
+ }
538
+ this.writeBytes(value);
539
+
540
+ // handle arrays
541
+ } else if (Array.isArray(value)) {
542
+ this._encodeArray(value, fnamesSeg);
543
+
544
+ // handle objects
545
+ } else {
546
+ this._encodeObject(value, fnamesSeg);
547
+ }
548
+
549
+ }
550
+
551
+ }
552
+
553
+ class OsonEncoder extends GrowableBuffer {
554
+
555
+ //---------------------------------------------------------------------------
556
+ // encode()
557
+ //
558
+ // Encodes the value as OSON and returns a buffer containing the OSON bytes.
559
+ //---------------------------------------------------------------------------
560
+ encode(value) {
561
+
562
+ // determine flags to use
563
+ let fnamesSeg;
564
+ let flags = constants.TNS_JSON_FLAG_INLINE_LEAF;
565
+ if (Array.isArray(value) || (value && Array.isArray(value.fields))) {
566
+ flags |= constants.TNS_JSON_FLAG_HASH_ID_UINT8 |
567
+ constants.TNS_JSON_FLAG_TINY_NODES_STAT;
568
+ fnamesSeg = new OsonFieldNamesSegment(value);
569
+ if (fnamesSeg.fieldNames.length > 65535) {
570
+ flags |= constants.TNS_JSON_FLAG_NUM_FNAMES_UINT32;
571
+ } else if (fnamesSeg.fieldNames.length > 255) {
572
+ flags |= constants.TNS_JSON_FLAG_NUM_FNAMES_UINT16;
573
+ }
574
+ if (fnamesSeg.pos > 65535) {
575
+ flags |= constants.TNS_JSON_FLAG_FNAMES_SEG_UINT32;
576
+ }
577
+ } else {
578
+ flags |= constants.TNS_JSON_FLAG_IS_SCALAR;
579
+ }
580
+
581
+ // encode values into the tree segment
582
+ const treeSeg = new OsonTreeSegment();
583
+ treeSeg.encodeNode(value, fnamesSeg);
584
+ if (treeSeg.pos > 65535) {
585
+ flags |= constants.TNS_JSON_FLAG_TREE_SEG_UINT32;
586
+ }
587
+
588
+ // write initial header
589
+ this.writeUInt8(constants.TNS_JSON_MAGIC_BYTE_1);
590
+ this.writeUInt8(constants.TNS_JSON_MAGIC_BYTE_2);
591
+ this.writeUInt8(constants.TNS_JSON_MAGIC_BYTE_3);
592
+ this.writeUInt8(constants.TNS_JSON_VERSION);
593
+ this.writeUInt16BE(flags);
594
+
595
+ // write extended header (when value is not scalar)
596
+ if (fnamesSeg) {
597
+
598
+ // write number of field names
599
+ if (fnamesSeg.fieldNames.length < 256) {
600
+ this.writeUInt8(fnamesSeg.fieldNames.length);
601
+ } else if (fnamesSeg.fieldNames.length < 65536) {
602
+ this.writeUInt16BE(fnamesSeg.fieldNames.length);
603
+ } else {
604
+ this.writeUInt32BE(fnamesSeg.fieldNames.length);
605
+ }
606
+
607
+ // write size of field names segment
608
+ if (fnamesSeg.pos < 65536) {
609
+ this.writeUInt16BE(fnamesSeg.pos);
610
+ } else {
611
+ this.writeUInt32BE(fnamesSeg.pos);
612
+ }
613
+
614
+ }
615
+
616
+ // write size of tree segment
617
+ if (treeSeg.pos < 65536) {
618
+ this.writeUInt16BE(treeSeg.pos);
619
+ } else {
620
+ this.writeUInt32BE(treeSeg.pos);
621
+ }
622
+
623
+ // write remainder of header and any data (when value is not scalar)
624
+ if (fnamesSeg) {
625
+
626
+ // write number of "tiny" nodes (always zero)
627
+ this.writeUInt16BE(0);
628
+
629
+ // write array of hash ids
630
+ for (let fieldName of fnamesSeg.fieldNames) {
631
+ this.writeUInt8(fieldName.hashId);
632
+ }
633
+
634
+ // write array of field name offsets
635
+ for (let fieldName of fnamesSeg.fieldNames) {
636
+ if (fnamesSeg.pos < 65536) {
637
+ this.writeUInt16BE(fieldName.offset);
638
+ } else {
639
+ this.writeUInt32BE(fieldName.offset);
640
+ }
641
+ }
642
+
643
+ // write field names
644
+ if (fnamesSeg.pos > 0) {
645
+ this.writeBytes(fnamesSeg.buf.subarray(0, fnamesSeg.pos));
646
+ }
647
+
648
+ }
649
+
650
+ // write tree segment data
651
+ this.writeBytes(treeSeg.buf.subarray(0, treeSeg.pos));
652
+
653
+ return this.buf.subarray(0, this.pos);
654
+ }
655
+
656
+ }
657
+
658
+ module.exports = {
659
+ OsonDecoder,
660
+ OsonEncoder
661
+ };