Skip to content

Commit e94c8c7

Browse files
committed
Code refactoring for OSON encode decode functionality
1 parent 9ac2093 commit e94c8c7

20 files changed

+386
-107
lines changed

doc/src/release_notes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ node-oracledb `v6.4.0 <https://github.com/oracle/node-oracledb/compare/v6.3.0...
1313
Common Changes
1414
++++++++++++++
1515

16+
#) Added methods :meth:`~Connection.decodeOSON` and
17+
:meth:`~Connection.encodeOSON` to support fetching and inserting into
18+
columns which have the check constraint ``IS JSON FORMAT OSON``
19+
enabled. Refer `Storing and Managing JSON Data <https://docs.oracle.com/en/database/oracle/oracle-database/19/adjsn/overview-of-storage-and-management-of-JSON-data.html#GUID-26AB85D2-3277-451B-BFAA-9DD45355FCC7>`__
20+
1621
#) Connections to standby database opened `MOUNTED` return
1722
`NAN <https://github.com/nodejs/nan>` for :meth:`~connection.maxOpenCursors`
1823
Fixed to return 0.

lib/connection.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const constants = require('./constants.js');
4343
const settings = require('./settings.js');
4444
const transformer = require('./transformer.js');
4545
const types = require('./types.js');
46+
const oson = require('impl/datahandlers/oson.js');
4647

4748
// global mapping of subscriptions; these cannot be tied to a particular
4849
// connection or pool since subscriptions can be created with one connection
@@ -839,6 +840,29 @@ class Connection extends EventEmitter {
839840
this._impl.setECID(value);
840841
}
841842

843+
//---------------------------------------------------------------------------
844+
// decode()
845+
//
846+
// Decodes OSON Buffer to JS data type.
847+
//---------------------------------------------------------------------------
848+
decodeOSON(buf) {
849+
errors.assertArgCount(arguments, 1, 1);
850+
errors.assertParamValue(Buffer.isBuffer(buf), 1);
851+
const decoder = new oson.OsonDecoder(buf);
852+
return decoder.decode();
853+
}
854+
855+
856+
//---------------------------------------------------------------------------
857+
// encode()
858+
//
859+
// Encodes the JS value into OSON bytes.
860+
//---------------------------------------------------------------------------
861+
encodeOSON(value) {
862+
const encoder = new oson.OsonEncoder();
863+
return encoder.encode(transformer.transformJsonValue(value));
864+
}
865+
842866
//---------------------------------------------------------------------------
843867
// execute()
844868
//

lib/thin/protocol/buffer.js renamed to lib/impl/datahandlers/buffer.js

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
const { Buffer } = require('buffer');
3030
const constants = require("./constants.js");
3131
const errors = require("../../errors.js");
32-
const settings = require("../../settings.js");
3332
const types = require("../../types.js");
33+
const nodbUtil = require("../../util.js");
3434

3535
/**
3636
* Base buffer class used for managing buffered data without unnecessary
@@ -184,7 +184,7 @@ class BaseBuffer {
184184
fseconds = Math.floor(buf.readUInt32BE(7) / (1000 * 1000));
185185
}
186186
const year = (buf[0] - 100) * 100 + buf[1] - 100;
187-
return settings._makeDate(useLocalTime, year, buf[2], buf[3], buf[4] - 1,
187+
return nodbUtil.makeDate(useLocalTime, year, buf[2], buf[3], buf[4] - 1,
188188
buf[5] - 1, buf[6] - 1, fseconds, 0);
189189
}
190190

@@ -887,29 +887,6 @@ class BaseBuffer {
887887

888888
}
889889

890-
//---------------------------------------------------------------------------
891-
// writeQLocator()
892-
//
893-
// Writes a QLocator. QLocators are always 40 bytes in length.
894-
//---------------------------------------------------------------------------
895-
writeQLocator(numBytes) {
896-
this.writeUB4(40); // QLocator length
897-
this.writeUInt8(40); // repeated length
898-
this.writeUInt16BE(38); // internal length
899-
this.writeUInt16BE(constants.TNS_LOB_QLOCATOR_VERSION);
900-
this.writeUInt8(constants.TNS_LOB_LOC_FLAGS_VALUE_BASED |
901-
constants.TNS_LOB_LOC_FLAGS_BLOB | constants.TNS_LOB_LOC_FLAGS_ABSTRACT);
902-
this.writeUInt8(constants.TNS_LOB_LOC_FLAGS_INIT);
903-
this.writeUInt16BE(0); // additional flags
904-
this.writeUInt16BE(1); // byt1
905-
this.writeUInt64BE(numBytes);
906-
this.writeUInt16BE(0); // unused
907-
this.writeUInt16BE(0); // csid
908-
this.writeUInt16BE(0); // unused
909-
this.writeUInt64BE(0); // unused
910-
this.writeUInt64BE(0); // unused
911-
}
912-
913890
//---------------------------------------------------------------------------
914891
// writeStr()
915892
//

lib/impl/datahandlers/constants.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (c) 2024, 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 constants = require('../../constants.js');
30+
31+
module.exports = {
32+
33+
// vector constants
34+
TNS_VECTOR_MAGIC_BYTE: 0xDB,
35+
TNS_VECTOR_VERSION: 0,
36+
37+
// vector flags
38+
TNS_VECTOR_FLAG_DIM_UINT8: 0x0001,
39+
TNS_VECTOR_FLAG_DIM_UINT32: 0x0002,
40+
TNS_VECTOR_FLAG_NORMSRC: 0x0040,
41+
TNS_VECTOR_FLAG_NORM: 0x0008,
42+
43+
// base JSON constants
44+
TNS_JSON_MAGIC_BYTE_1: 0xff,
45+
TNS_JSON_MAGIC_BYTE_2: 0x4a, // 'J'
46+
TNS_JSON_MAGIC_BYTE_3: 0x5a, // 'Z'
47+
TNS_JSON_VERSION: 1,
48+
TNS_JSON_FLAG_HASH_ID_UINT8: 0x0100,
49+
TNS_JSON_FLAG_HASH_ID_UINT16: 0x0200,
50+
TNS_JSON_FLAG_NUM_FNAMES_UINT16: 0x0400,
51+
TNS_JSON_FLAG_FNAMES_SEG_UINT32: 0x0800,
52+
TNS_JSON_FLAG_TINY_NODES_STAT: 0x2000,
53+
TNS_JSON_FLAG_TREE_SEG_UINT32: 0x1000,
54+
TNS_JSON_FLAG_REL_OFFSET_MODE: 0x01,
55+
TNS_JSON_FLAG_INLINE_LEAF: 0x02,
56+
TNS_JSON_FLAG_LEN_IN_PCODE: 0x04,
57+
TNS_JSON_FLAG_NUM_FNAMES_UINT32: 0x08,
58+
TNS_JSON_FLAG_IS_SCALAR: 0x10,
59+
60+
// JSON data types
61+
TNS_JSON_TYPE_NULL: 0x30,
62+
TNS_JSON_TYPE_TRUE: 0x31,
63+
TNS_JSON_TYPE_FALSE: 0x32,
64+
TNS_JSON_TYPE_STRING_LENGTH_UINT8: 0x33,
65+
TNS_JSON_TYPE_NUMBER_LENGTH_UINT8: 0x34,
66+
TNS_JSON_TYPE_BINARY_DOUBLE: 0x36,
67+
TNS_JSON_TYPE_STRING_LENGTH_UINT16: 0x37,
68+
TNS_JSON_TYPE_STRING_LENGTH_UINT32: 0x38,
69+
TNS_JSON_TYPE_TIMESTAMP: 0x39,
70+
TNS_JSON_TYPE_BINARY_LENGTH_UINT16: 0x3a,
71+
TNS_JSON_TYPE_BINARY_LENGTH_UINT32: 0x3b,
72+
TNS_JSON_TYPE_DATE: 0x3c,
73+
TNS_JSON_TYPE_INTERVAL_YM: 0x3d,
74+
TNS_JSON_TYPE_INTERVAL_DS: 0x3e,
75+
TNS_JSON_TYPE_TIMESTAMP_TZ: 0x7c,
76+
TNS_JSON_TYPE_TIMESTAMP7: 0x7d,
77+
TNS_JSON_TYPE_BINARY_FLOAT: 0x7f,
78+
TNS_JSON_TYPE_OBJECT: 0x84,
79+
TNS_JSON_TYPE_ARRAY: 0xc0,
80+
TNS_JSON_TYPE_EXTENDED: 0x7b,
81+
TNS_JSON_TYPE_VECTOR: 0x01,
82+
83+
// timezone offsets
84+
TZ_HOUR_OFFSET: 20,
85+
TZ_MINUTE_OFFSET: 60,
86+
87+
// general constants
88+
TNS_MAX_SHORT_LENGTH: 252,
89+
TNS_DURATION_MID: 0x80000000,
90+
TNS_DURATION_OFFSET: 60,
91+
TNS_CHUNK_SIZE: 32767,
92+
TNS_HAS_REGION_ID: 0x80,
93+
NUMBER_MAX_DIGITS: 40,
94+
BUFFER_CHUNK_SIZE: 65536,
95+
CSFRM_IMPLICIT: constants.CSFRM_IMPLICIT,
96+
97+
// vector generic constants
98+
VECTOR_FORMAT_FLOAT32: constants.VECTOR_FORMAT_FLOAT32,
99+
VECTOR_FORMAT_FLOAT64: constants.VECTOR_FORMAT_FLOAT64,
100+
VECTOR_FORMAT_INT8: constants.VECTOR_FORMAT_INT8,
101+
102+
TNS_NULL_LENGTH_INDICATOR: 255,
103+
TNS_LONG_LENGTH_INDICATOR: 254,
104+
105+
};

lib/thin/protocol/oson.js renamed to lib/impl/datahandlers/oson.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const constants = require("./constants.js");
3232
const errors = require("../../errors.js");
3333
const types = require("../../types.js");
3434
const util = require("util");
35+
const vector = require("./vector.js");
36+
const nodbUtil = require("../../util.js");
3537

3638
/**
3739
* Class used for decodeing
@@ -106,7 +108,7 @@ class OsonDecoder extends BaseBuffer {
106108
_decodeNode() {
107109

108110
// if the most significant bit is set the node refers to a container
109-
const nodeType = this.readUInt8();
111+
let nodeType = this.readUInt8();
110112
if (nodeType & 0x80) {
111113
return this._decodeContainerNode(nodeType);
112114
}
@@ -145,6 +147,13 @@ class OsonDecoder extends BaseBuffer {
145147
return Buffer.from(this.readBytes(this.readUInt16BE()));
146148
} else if (nodeType === constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT32) {
147149
return Buffer.from(this.readBytes(this.readUInt32BE()));
150+
} else if (nodeType === constants.TNS_JSON_TYPE_EXTENDED) {
151+
nodeType = this.readUInt8();
152+
if (nodeType === constants.TNS_JSON_TYPE_VECTOR) {
153+
const vecImage = this.readBytes(this.readUInt32BE());
154+
const decoder = new vector.VectorDecoder(vecImage);
155+
return decoder.decode();
156+
}
148157
}
149158

150159
// handle number/decimal with length stored inside the node itself
@@ -544,6 +553,15 @@ class OsonTreeSegment extends GrowableBuffer {
544553
} else if (Array.isArray(value)) {
545554
this._encodeArray(value, fnamesSeg);
546555

556+
// handle vectors
557+
} else if (nodbUtil.isVectorValue(value)) {
558+
this.writeUInt8(constants.TNS_JSON_TYPE_EXTENDED);
559+
this.writeUInt8(constants.TNS_JSON_TYPE_VECTOR);
560+
const encoder = new vector.VectorEncoder();
561+
const buf = encoder.encode(value);
562+
this.writeUInt32BE(buf.length);
563+
this.writeBytes(buf);
564+
547565
// handle objects
548566
} else {
549567
this._encodeObject(value, fnamesSeg);
File renamed without changes.

lib/settings.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
const constants = require('./constants.js');
3030
const errors = require('./errors.js');
3131
const types = require('./types.js');
32+
const nodbUtil = require("./util.js");
33+
const vector = require('impl/datahandlers/vector.js');
3234

3335
class Settings {
3436

@@ -106,11 +108,29 @@ class Settings {
106108
// information).
107109
//---------------------------------------------------------------------------
108110
_makeDate(useLocal, year, month, day, hour, minute, second, fseconds, offset) {
109-
if (useLocal) {
110-
return new Date(year, month - 1, day, hour, minute, second, fseconds);
111-
}
112-
return new Date(Date.UTC(year, month - 1, day, hour, minute, second,
113-
fseconds) - offset * 60000);
111+
return nodbUtil.makeDate(useLocal, year, month, day, hour, minute, second, fseconds, offset);
112+
}
113+
114+
//---------------------------------------------------------------------------
115+
// _decodeVector()
116+
//
117+
// Returns a typed array by decoding buffer.
118+
//
119+
//---------------------------------------------------------------------------
120+
_decodeVector(buffer) {
121+
const decoder = new vector.VectorDecoder(buffer);
122+
return decoder.decode();
123+
}
124+
125+
//---------------------------------------------------------------------------
126+
// _encodeVector()
127+
//
128+
// Create a Vector image from typedarray
129+
//
130+
//---------------------------------------------------------------------------
131+
_encodeVector(value) {
132+
const encoder = new vector.VectorEncoder();
133+
return encoder.encode(value);
114134
}
115135

116136
//---------------------------------------------------------------------------

lib/thin/connection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const ConnectionImpl = require('../impl/connection.js');
3131
const ThinResultSetImpl = require('./resultSet.js');
3232
const ThinLobImpl = require("./lob.js");
3333
const Protocol = require("./protocol/protocol.js");
34-
const { BaseBuffer } = require('./protocol/buffer.js');
34+
const { BaseBuffer } = require('../impl/datahandlers/buffer.js');
3535
const {NetworkSession: nsi} = require("./sqlnet/networkSession.js");
3636
const { Statement } = require("./statement");
3737
const thinUtil = require('./util');

lib/thin/dbObject.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const constants = require('./protocol/constants.js');
3131
const errors = require('../errors.js');
3232
const types = require('../types.js');
3333
const DbObjectImpl = require('../impl/dbObject.js');
34-
const { GrowableBuffer } = require('./protocol/buffer.js');
34+
const { GrowableBuffer } = require('../impl/datahandlers/buffer.js');
3535
const ThinLobImpl = require('./lob.js');
3636

3737
class DbObjectPickleBuffer extends GrowableBuffer {

0 commit comments

Comments
 (0)