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