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.
- 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.1-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.0.1-darwin-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.1-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.1-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.0.1-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.1-linux-x64.node +0 -0
- package/build/Release/oracledb-6.0.1-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.1-win32-x64.node +0 -0
- package/build/Release/oracledb-6.0.1-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 +148 -8
- package/lib/aqEnqOptions.js +59 -7
- package/lib/aqMessage.js +118 -7
- package/lib/aqQueue.js +243 -53
- package/lib/connection.js +1326 -465
- package/lib/constants.js +203 -0
- package/lib/dbObject.js +346 -32
- package/lib/errors.js +785 -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 +157 -68
- package/lib/oracledb.js +1047 -345
- package/lib/pool.js +654 -465
- package/lib/poolStatistics.js +24 -9
- package/lib/queryStream.js +16 -8
- package/lib/resultset.js +291 -183
- package/lib/settings.js +191 -0
- package/lib/sodaCollection.js +214 -201
- package/lib/sodaDatabase.js +108 -71
- package/lib/sodaDocCursor.js +44 -32
- package/lib/sodaDocument.js +76 -14
- package/lib/sodaOperation.js +131 -118
- package/lib/thin/connection.js +1043 -0
- package/lib/thin/dbObject.js +630 -0
- package/lib/thin/index.js +40 -0
- package/lib/thin/lob.js +189 -0
- package/lib/thin/pool.js +481 -0
- package/lib/thin/protocol/buffer.js +1043 -0
- package/lib/thin/protocol/capabilities.js +121 -0
- package/lib/thin/protocol/constants.js +765 -0
- package/lib/thin/protocol/encryptDecrypt.js +185 -0
- package/lib/thin/protocol/messages/auth.js +349 -0
- package/lib/thin/protocol/messages/base.js +476 -0
- package/lib/thin/protocol/messages/commit.js +56 -0
- package/lib/thin/protocol/messages/dataType.js +391 -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 +201 -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 +812 -0
- package/lib/thin/protocol/oson.js +661 -0
- package/lib/thin/protocol/packet.js +519 -0
- package/lib/thin/protocol/protocol.js +202 -0
- package/lib/thin/protocol/utils.js +89 -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 +621 -0
- package/lib/thin/sqlnet/ntTcp.js +527 -0
- package/lib/thin/sqlnet/nvStrToNvPair.js +783 -0
- package/lib/thin/sqlnet/packet.js +405 -0
- package/lib/thin/sqlnet/paramParser.js +206 -0
- package/lib/thin/sqlnet/sessionAtts.js +160 -0
- package/lib/thin/statement.js +300 -0
- package/lib/thin/util.js +289 -0
- package/lib/transformer.js +252 -0
- package/lib/types.js +357 -0
- package/lib/util.js +196 -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,519 @@
|
|
|
1
|
+
// Copyright (c) 2022, 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 } = require('./buffer.js');
|
|
30
|
+
const { Buffer } = require('buffer');
|
|
31
|
+
const constants = require("./constants.js");
|
|
32
|
+
const oson = require('./oson.js');
|
|
33
|
+
const utils = require('./utils.js');
|
|
34
|
+
|
|
35
|
+
const TNS_BASE64_ALPHABET_ARRAY = Buffer.from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 'utf8');
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Class used for byte chunks used in the ChunkedBytesBuffer.
|
|
40
|
+
*/
|
|
41
|
+
class BytesChunk {
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Constructor.
|
|
45
|
+
* @param {Number} number of bytes to add to the chunk (rounded to the
|
|
46
|
+
* nearest chunk size to avoid unnecessary allocations and copies)
|
|
47
|
+
*/
|
|
48
|
+
constructor(numBytes) {
|
|
49
|
+
this.allocLen = numBytes;
|
|
50
|
+
const remainder = numBytes % constants.CHUNKED_BYTES_CHUNK_SIZE;
|
|
51
|
+
if (remainder > 0) {
|
|
52
|
+
this.allocLen += (constants.CHUNKED_BYTES_CHUNK_SIZE - remainder);
|
|
53
|
+
}
|
|
54
|
+
this.buf = Buffer.alloc(this.allocLen);
|
|
55
|
+
this.actualLen = 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Class used for handling chunked reads.
|
|
63
|
+
*/
|
|
64
|
+
class ChunkedBytesBuffer {
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Constructor.
|
|
68
|
+
*/
|
|
69
|
+
constructor() {
|
|
70
|
+
this.chunks = [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* End the chunked read and return a consolidated buffer.
|
|
75
|
+
*/
|
|
76
|
+
endChunkedRead() {
|
|
77
|
+
if (this.chunks.length > 1) {
|
|
78
|
+
let totalNumBytes = 0;
|
|
79
|
+
for (const chunk of this.chunks) {
|
|
80
|
+
totalNumBytes += chunk.actualLen;
|
|
81
|
+
}
|
|
82
|
+
let pos = 0;
|
|
83
|
+
const consolidatedChunk = new BytesChunk(totalNumBytes);
|
|
84
|
+
for (const chunk of this.chunks) {
|
|
85
|
+
chunk.buf.copy(consolidatedChunk.buf, pos, 0, chunk.actualLen);
|
|
86
|
+
pos += chunk.actualLen;
|
|
87
|
+
}
|
|
88
|
+
consolidatedChunk.actualLen = totalNumBytes;
|
|
89
|
+
this.chunks = [consolidatedChunk];
|
|
90
|
+
}
|
|
91
|
+
const chunk = this.chunks[0];
|
|
92
|
+
return chunk.buf.subarray(0, chunk.actualLen);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Constructor.
|
|
97
|
+
*/
|
|
98
|
+
getBuf(numBytes) {
|
|
99
|
+
let chunk;
|
|
100
|
+
if (this.chunks.length > 0) {
|
|
101
|
+
chunk = this.chunks[this.chunks.length - 1];
|
|
102
|
+
if (chunk.allocLen - chunk.actualLen < numBytes) {
|
|
103
|
+
chunk = undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!chunk) {
|
|
107
|
+
chunk = new BytesChunk(numBytes);
|
|
108
|
+
this.chunks.push(chunk);
|
|
109
|
+
}
|
|
110
|
+
const buf = chunk.buf.subarray(chunk.actualLen,
|
|
111
|
+
chunk.actualLen + numBytes);
|
|
112
|
+
chunk.actualLen += numBytes;
|
|
113
|
+
return buf;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Start a chunked read. This ensures that only one chunk is available and
|
|
118
|
+
* its actual length is set to zero.
|
|
119
|
+
*/
|
|
120
|
+
startChunkedRead() {
|
|
121
|
+
if (this.chunks.length > 0) {
|
|
122
|
+
this.chunks = this.chunks.splice(0, 1);
|
|
123
|
+
this.chunks[0].actualLen = 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Encapsulates the Network Read Buffer
|
|
132
|
+
*
|
|
133
|
+
* @class ReadPacket
|
|
134
|
+
*/
|
|
135
|
+
class ReadPacket extends BaseBuffer {
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Constructor.
|
|
139
|
+
* @param {Object} adapter used for sending/receiving data
|
|
140
|
+
* @param {Object} capabilities
|
|
141
|
+
*/
|
|
142
|
+
constructor(nsi, caps) {
|
|
143
|
+
super(nsi.sAtts.sdu);
|
|
144
|
+
this.nsi = nsi;
|
|
145
|
+
this.caps = caps;
|
|
146
|
+
this.chunkedBytesBuf = new ChunkedBytesBuffer();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Helper function that processes the length. If the length is defined as
|
|
151
|
+
* TNS_LONG_LENGTH_INDICATOR, a chunked read is performed.
|
|
152
|
+
*/
|
|
153
|
+
_readBytesWithLength(numBytes) {
|
|
154
|
+
if (numBytes !== constants.TNS_LONG_LENGTH_INDICATOR) {
|
|
155
|
+
return this.readBytes(numBytes);
|
|
156
|
+
}
|
|
157
|
+
this.chunkedBytesBuf.startChunkedRead();
|
|
158
|
+
while (true) { // eslint-disable-line
|
|
159
|
+
const numBytesInChunk = this.readUB4();
|
|
160
|
+
if (numBytesInChunk === 0) {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
this.readBytes(numBytesInChunk, true);
|
|
164
|
+
}
|
|
165
|
+
return this.chunkedBytesBuf.endChunkedRead();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
skipBytes(numBytes) {
|
|
169
|
+
|
|
170
|
+
// if no bytes are left in the buffer, a new packet needs to be fetched
|
|
171
|
+
// before anything else can take place
|
|
172
|
+
if (this.pos === this.size) {
|
|
173
|
+
this.receivePacket();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// if there is enough room in the buffer to satisfy the number of bytes
|
|
177
|
+
// requested, return the buffer directly
|
|
178
|
+
const numBytesLeft = this.numBytesLeft();
|
|
179
|
+
if (numBytes <= numBytesLeft) {
|
|
180
|
+
this.pos += numBytes;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
numBytes -= numBytesLeft;
|
|
184
|
+
|
|
185
|
+
// acquire packets until the requested number of bytes is satisfied
|
|
186
|
+
while (numBytes > 0) {
|
|
187
|
+
this.receivePacket();
|
|
188
|
+
const numSplitBytes = Math.min(numBytes, this.size - this.pos);
|
|
189
|
+
this.pos += numSplitBytes;
|
|
190
|
+
numBytes -= numSplitBytes;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Returns a buffer containing the specified number of bytes. If an
|
|
196
|
+
* insufficient number of bytes are available, a new packet is read.
|
|
197
|
+
* @param {Number} specifies the number of bytes to read from the buffer
|
|
198
|
+
*/
|
|
199
|
+
readBytes(numBytes, inChunkedRead = false) {
|
|
200
|
+
|
|
201
|
+
// if no bytes are left in the buffer, a new packet needs to be fetched
|
|
202
|
+
// before anything else can take place
|
|
203
|
+
if (this.pos === this.size) {
|
|
204
|
+
this.receivePacket();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// if there is enough room in the buffer to satisfy the number of bytes
|
|
208
|
+
// requested, return the buffer directly
|
|
209
|
+
const numBytesLeft = this.numBytesLeft();
|
|
210
|
+
if (numBytes <= numBytesLeft) {
|
|
211
|
+
let buf;
|
|
212
|
+
if (inChunkedRead) {
|
|
213
|
+
buf = this.chunkedBytesBuf.getBuf(numBytes);
|
|
214
|
+
this.buf.copy(buf, 0, this.pos, this.pos + numBytes);
|
|
215
|
+
} else {
|
|
216
|
+
buf = this.buf.subarray(this.pos, this.pos + numBytes);
|
|
217
|
+
}
|
|
218
|
+
this.pos += numBytes;
|
|
219
|
+
return buf;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// the requested bytes are split across multiple packets; if a chunked read
|
|
223
|
+
// is not in progress one is implicitly started; copy the bytes from the
|
|
224
|
+
// end of this packet
|
|
225
|
+
if (!inChunkedRead) {
|
|
226
|
+
this.chunkedBytesBuf.startChunkedRead();
|
|
227
|
+
}
|
|
228
|
+
const buf = this.chunkedBytesBuf.getBuf(numBytes);
|
|
229
|
+
let offset = 0;
|
|
230
|
+
this.buf.copy(buf, offset, this.pos, this.pos + numBytesLeft);
|
|
231
|
+
offset += numBytesLeft;
|
|
232
|
+
numBytes -= numBytesLeft;
|
|
233
|
+
|
|
234
|
+
// acquire packets until the requested number of bytes is satisfied
|
|
235
|
+
while (numBytes > 0) {
|
|
236
|
+
this.receivePacket();
|
|
237
|
+
const numSplitBytes = Math.min(numBytes, this.size - this.pos);
|
|
238
|
+
this.buf.copy(buf, offset, this.pos, this.pos + numSplitBytes);
|
|
239
|
+
this.pos += numSplitBytes;
|
|
240
|
+
offset += numSplitBytes;
|
|
241
|
+
numBytes -= numSplitBytes;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// if not in a chunked bytes read, return the buffer directly
|
|
245
|
+
if (!inChunkedRead) {
|
|
246
|
+
return this.chunkedBytesBuf.endChunkedRead();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Receives a packet from the adapter.
|
|
253
|
+
*/
|
|
254
|
+
receivePacket() {
|
|
255
|
+
if (this.savedBufferPos === this.savedBuffers.length) {
|
|
256
|
+
const packet = this.nsi.syncRecvPacket();
|
|
257
|
+
if (!packet)
|
|
258
|
+
throw new utils.OutOfPacketsError();
|
|
259
|
+
this.savedBuffers.push(packet.buf);
|
|
260
|
+
}
|
|
261
|
+
this.startPacket(this.savedBuffers[this.savedBufferPos++]);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
restorePoint() {
|
|
265
|
+
this.savedBufferPos = 0;
|
|
266
|
+
this.startPacket(this.savedBuffers[this.savedBufferPos++]);
|
|
267
|
+
this.pos = this.savedPos;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
savePoint() {
|
|
271
|
+
if (this.savedBuffers) {
|
|
272
|
+
this.savedBuffers = this.savedBuffers.splice(this.savedBufferPos - 1);
|
|
273
|
+
} else {
|
|
274
|
+
this.savedBuffers = [this.buf];
|
|
275
|
+
}
|
|
276
|
+
this.savedBufferPos = 1;
|
|
277
|
+
this.savedPos = this.pos;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
startPacket(buf) {
|
|
281
|
+
this.buf = buf;
|
|
282
|
+
this.pos = 10; // skip packet heaader and data flags
|
|
283
|
+
this.size = buf.length;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async waitForPackets() {
|
|
287
|
+
const packet = await this.nsi.recvPacket();
|
|
288
|
+
if (!this.savedBuffers) {
|
|
289
|
+
this.savedBuffers = [packet.buf];
|
|
290
|
+
this.savedBufferPos = 0;
|
|
291
|
+
} else {
|
|
292
|
+
this.savedBuffers.push(packet.buf);
|
|
293
|
+
}
|
|
294
|
+
this.startPacket(this.savedBuffers[this.savedBufferPos++]);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Reads OSON (QLocator followed by data) and decodes it into a JavaScript
|
|
299
|
+
* object.
|
|
300
|
+
*/
|
|
301
|
+
readOson() {
|
|
302
|
+
const numBytes = this.readUB4();
|
|
303
|
+
if (numBytes === 0) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
this.skipUB8(); // size (unused)
|
|
307
|
+
this.skipUB4(); // chunk size (unused)
|
|
308
|
+
const decoder = new oson.OsonDecoder(this.readBytesWithLength());
|
|
309
|
+
this.skipBytesChunked(); // locator (unused)
|
|
310
|
+
return decoder.decode();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
readURowID() {
|
|
314
|
+
let outputOffset = 0, inputOffset = 1;
|
|
315
|
+
let buf = this.readBytesWithLength();
|
|
316
|
+
if (buf === null)
|
|
317
|
+
return null;
|
|
318
|
+
buf = this.readBytesWithLength();
|
|
319
|
+
let inputLen = buf.length;
|
|
320
|
+
|
|
321
|
+
// Handle physical rowid
|
|
322
|
+
if (buf && buf[0] === 1) {
|
|
323
|
+
let rba = buf.readUInt32BE(1);
|
|
324
|
+
let partitionID = buf.readUInt16BE(5);
|
|
325
|
+
let blockNum = buf.readUInt32BE(7);
|
|
326
|
+
let slotNum = buf.readUInt16BE(11);
|
|
327
|
+
return utils.encodeRowID({rba, partitionID, blockNum, slotNum});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// handle logical rowid
|
|
331
|
+
let outputLen = Math.floor(inputLen / 3) * 4;
|
|
332
|
+
let remainder = inputLen % 3;
|
|
333
|
+
if (remainder === 1) {
|
|
334
|
+
outputLen += 1;
|
|
335
|
+
} else if (remainder === 2) {
|
|
336
|
+
outputLen += 3;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
let outputValue = Buffer.alloc(outputLen);
|
|
340
|
+
inputLen -= 1;
|
|
341
|
+
outputValue[0] = 42;
|
|
342
|
+
outputOffset += 1;
|
|
343
|
+
while (inputLen > 0) {
|
|
344
|
+
// produce first byte of quadruple
|
|
345
|
+
let pos = buf[inputOffset] >> 2;
|
|
346
|
+
outputValue[outputOffset] = TNS_BASE64_ALPHABET_ARRAY[pos];
|
|
347
|
+
outputOffset += 1;
|
|
348
|
+
|
|
349
|
+
// produce second byte of quadruple, but if only one byte is left,
|
|
350
|
+
// produce that one byte and exit
|
|
351
|
+
pos = (buf[inputOffset] & 0x3) << 4;
|
|
352
|
+
if (inputLen == 1) {
|
|
353
|
+
outputValue[outputOffset] = TNS_BASE64_ALPHABET_ARRAY[pos];
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
inputOffset += 1;
|
|
357
|
+
pos |= ((buf[inputOffset] & 0xf0) >> 4);
|
|
358
|
+
outputValue[outputOffset] = TNS_BASE64_ALPHABET_ARRAY[pos];
|
|
359
|
+
outputOffset += 1;
|
|
360
|
+
|
|
361
|
+
// produce third byte of quadruple, but if only two bytes are left,
|
|
362
|
+
// produce that one byte and exit
|
|
363
|
+
pos = (buf[inputOffset] & 0xf) << 2;
|
|
364
|
+
if (inputLen == 2) {
|
|
365
|
+
outputValue[outputOffset] = TNS_BASE64_ALPHABET_ARRAY[pos];
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
inputOffset += 1;
|
|
369
|
+
pos |= ((buf[inputOffset] & 0xc0) >> 6);
|
|
370
|
+
outputValue[outputOffset] = TNS_BASE64_ALPHABET_ARRAY[pos];
|
|
371
|
+
outputOffset += 1;
|
|
372
|
+
|
|
373
|
+
// produce final byte of quadruple
|
|
374
|
+
pos = buf[inputOffset] & 0x3f;
|
|
375
|
+
outputValue[outputOffset] = TNS_BASE64_ALPHABET_ARRAY[pos];
|
|
376
|
+
outputOffset += 1;
|
|
377
|
+
inputOffset += 1;
|
|
378
|
+
inputLen -= 3;
|
|
379
|
+
}
|
|
380
|
+
return outputValue.toString('utf-8');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
readRowID() {
|
|
384
|
+
let rba = this.readUB4();
|
|
385
|
+
let partitionID = this.readUB2();
|
|
386
|
+
this.skipUB1();
|
|
387
|
+
let blockNum = this.readUB4();
|
|
388
|
+
let slotNum = this.readUB2();
|
|
389
|
+
return {rba, partitionID, blockNum, slotNum};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
skipBytesChunked() {
|
|
393
|
+
const numBytes = this.readUInt8();
|
|
394
|
+
if (numBytes !== constants.TNS_LONG_LENGTH_INDICATOR) {
|
|
395
|
+
this.skipBytes(numBytes);
|
|
396
|
+
} else {
|
|
397
|
+
while (true) { // eslint-disable-line
|
|
398
|
+
const tempNumBytes = this.readUB4();
|
|
399
|
+
if (tempNumBytes === 0)
|
|
400
|
+
break;
|
|
401
|
+
this.skipBytes(tempNumBytes);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Encapsulates the Network Write Buffer
|
|
411
|
+
*
|
|
412
|
+
* @class WritePacket
|
|
413
|
+
*/
|
|
414
|
+
class WritePacket extends BaseBuffer {
|
|
415
|
+
|
|
416
|
+
constructor(nsi, caps, protocol) {
|
|
417
|
+
super(nsi.sAtts.sdu);
|
|
418
|
+
this.size = this.maxSize;
|
|
419
|
+
this.isLargeSDU = nsi.sAtts.version >= constants.TNS_VERSION_MIN_LARGE_SDU;
|
|
420
|
+
this.protocol = protocol;
|
|
421
|
+
this.packetType = constants.TNS_PACKET_TYPE_DATA;
|
|
422
|
+
this.caps = caps;
|
|
423
|
+
this.nsi = nsi;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Grows the buffer by sending the existing buffer on the transport. A copy
|
|
428
|
+
* is made so that the existing buffer can be used for the next batch of data
|
|
429
|
+
* that needs to be sent
|
|
430
|
+
*/
|
|
431
|
+
_grow() {
|
|
432
|
+
this._sendPacket();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Sends the data in the buffer on the transport. First, the packet header is
|
|
437
|
+
* set up by writing the size and packet type.
|
|
438
|
+
*/
|
|
439
|
+
_sendPacket(finalPacket = false) {
|
|
440
|
+
const size = this.pos;
|
|
441
|
+
this.pos = 0;
|
|
442
|
+
if (this.isLargeSDU) {
|
|
443
|
+
this.writeUInt32BE(size);
|
|
444
|
+
} else {
|
|
445
|
+
this.writeUInt16BE(size);
|
|
446
|
+
this.writeUInt16BE(0);
|
|
447
|
+
}
|
|
448
|
+
this.writeUInt8(this.packetType);
|
|
449
|
+
this.writeUInt8(0);
|
|
450
|
+
this.writeUInt16BE(0);
|
|
451
|
+
let buf = this.buf.subarray(0, size);
|
|
452
|
+
if (!finalPacket) {
|
|
453
|
+
buf = Buffer.from(buf);
|
|
454
|
+
this.startPacket();
|
|
455
|
+
}
|
|
456
|
+
this.nsi.ntAdapter.send(buf);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Starts a packet.
|
|
461
|
+
*/
|
|
462
|
+
startPacket(dataFlags = 0) {
|
|
463
|
+
this.pos = constants.PACKET_HEADER_SIZE;
|
|
464
|
+
if (this.packetType === constants.TNS_PACKET_TYPE_DATA) {
|
|
465
|
+
this.writeUInt16BE(dataFlags);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Starts a database request.
|
|
471
|
+
*/
|
|
472
|
+
startRequest(packetType, dataFlags = 0) {
|
|
473
|
+
this.packetType = packetType;
|
|
474
|
+
this.startPacket(dataFlags);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Ends a database request.
|
|
479
|
+
*/
|
|
480
|
+
endRequest() {
|
|
481
|
+
if (this.pos > constants.PACKET_HEADER_SIZE) {
|
|
482
|
+
this._sendPacket(true);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
writeKeyValue(key, value, flags = 0) {
|
|
487
|
+
const keyBytesLen = Buffer.byteLength(key);
|
|
488
|
+
const valBytesLen = Buffer.byteLength(value);
|
|
489
|
+
this.writeUB4(keyBytesLen);
|
|
490
|
+
this.writeBytesWithLength(Buffer.from(key));
|
|
491
|
+
this.writeUB4(valBytesLen);
|
|
492
|
+
if (valBytesLen > 0) {
|
|
493
|
+
this.writeBytesWithLength(Buffer.from(value));
|
|
494
|
+
}
|
|
495
|
+
this.writeUB4(flags);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Encodes a JavaScript object into OSON and then writes it (QLocator
|
|
500
|
+
* followed by data) to the buffer.
|
|
501
|
+
*/
|
|
502
|
+
writeOson(value) {
|
|
503
|
+
const encoder = new oson.OsonEncoder();
|
|
504
|
+
const buf = encoder.encode(value);
|
|
505
|
+
this.writeQLocator(buf.length);
|
|
506
|
+
this.writeBytesWithLength(buf);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
writeSeqNum() {
|
|
510
|
+
this.writeUInt8(this.protocol.sequenceId);
|
|
511
|
+
this.protocol.sequenceId = (this.protocol.sequenceId + 1) % 256;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
module.exports = {
|
|
517
|
+
ReadPacket,
|
|
518
|
+
WritePacket
|
|
519
|
+
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Copyright (c) 2022, 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 constants = require("./constants.js");
|
|
30
|
+
const Capabilities = require("./capabilities.js");
|
|
31
|
+
const {WritePacket, ReadPacket} = require("./packet.js");
|
|
32
|
+
const errors = require("../../errors");
|
|
33
|
+
const utils = require("./utils.js");
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Handles protocol details.
|
|
37
|
+
*
|
|
38
|
+
* @class Protocol
|
|
39
|
+
*/
|
|
40
|
+
class Protocol {
|
|
41
|
+
|
|
42
|
+
constructor(conn) {
|
|
43
|
+
this._breakInProgress = false;
|
|
44
|
+
this.txnInProgress = false;
|
|
45
|
+
this.connInProgress = true;
|
|
46
|
+
this.nsi = conn.nscon;
|
|
47
|
+
this.sequenceId = 1;
|
|
48
|
+
/**
|
|
49
|
+
* Compile and Runtime capabilities negotiated with Server
|
|
50
|
+
* @type {object}
|
|
51
|
+
*/
|
|
52
|
+
this.caps = new Capabilities(conn.nscon.sAtts.version);
|
|
53
|
+
this.writeBuf = new WritePacket(conn.nscon, this.caps, this);
|
|
54
|
+
this.readBuf = new ReadPacket(conn.nscon, this.caps);
|
|
55
|
+
this.callTimeout = 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Decodes the message returned by the database. A message may consist of
|
|
60
|
+
* multiple packets. Not all packets may be available so if insufficient
|
|
61
|
+
* packets are available, the message decode function is expected to return
|
|
62
|
+
* the value true if more data is expected to follow.
|
|
63
|
+
*
|
|
64
|
+
* If that occurs, waiting occurs until more packets arrive.
|
|
65
|
+
*
|
|
66
|
+
* @param {object} message: the RPC dynamic structure specific to the RPC
|
|
67
|
+
*/
|
|
68
|
+
async _decodeMessage(message) {
|
|
69
|
+
message.preProcess();
|
|
70
|
+
await this.readBuf.waitForPackets();
|
|
71
|
+
while (true) { // eslint-disable-line
|
|
72
|
+
if (this.nsi.isBreak) {
|
|
73
|
+
await this.resetMessage();
|
|
74
|
+
await this.readBuf.waitForPackets();
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
message.decode(this.readBuf);
|
|
78
|
+
break;
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (err instanceof utils.OutOfPacketsError) {
|
|
81
|
+
await this.readBuf.waitForPackets();
|
|
82
|
+
this.readBuf.restorePoint();
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
throw (err);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
await message.postProcess();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Encodes the message to be sent to the database. A message may be encoded
|
|
93
|
+
* in multiple packets. In order to facilitate encoding of very large
|
|
94
|
+
* messages consisting of a large number of packets, the message encode
|
|
95
|
+
* function is expected to return the value true if more data is to follow.
|
|
96
|
+
*
|
|
97
|
+
* If that occurs, waiting occurs until the stream has drained and is ready
|
|
98
|
+
* to accept more data.
|
|
99
|
+
*
|
|
100
|
+
* @param {object} message: the RPC dynamic structure specific to the RPC
|
|
101
|
+
*/
|
|
102
|
+
async _encodeMessage(message) {
|
|
103
|
+
const adapter = this.nsi.ntAdapter;
|
|
104
|
+
this.writeBuf.startRequest(constants.TNS_PACKET_TYPE_DATA);
|
|
105
|
+
while (message.encode(this.writeBuf)) {
|
|
106
|
+
await adapter.pauseWrite();
|
|
107
|
+
}
|
|
108
|
+
this.writeBuf.endRequest();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async _recoverFromError(message) {
|
|
112
|
+
/*
|
|
113
|
+
* We have NJS error(protocol related) detected during packet write/read
|
|
114
|
+
* operation. Issue a break and reset to clear channel . We receive the
|
|
115
|
+
* response as ORA-1013 from the server.
|
|
116
|
+
*/
|
|
117
|
+
try {
|
|
118
|
+
this.breakMessage();
|
|
119
|
+
this._breakInProgress = false;
|
|
120
|
+
await this.resetMessage();
|
|
121
|
+
await this.readBuf.waitForPackets();
|
|
122
|
+
message.decode(this.readBuf);
|
|
123
|
+
} catch (err) { // Recovery failed
|
|
124
|
+
// TODO: throw different error indicating that reset failed, probably
|
|
125
|
+
// put inside resetMessage()
|
|
126
|
+
this.nsi.disconnect();
|
|
127
|
+
errors.throwErr(errors.ERR_CONNECTION_CLOSED);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
*
|
|
133
|
+
* @param {object} message The RPC dynamic structure specific to the RPC
|
|
134
|
+
* @return {Promise}
|
|
135
|
+
*/
|
|
136
|
+
async _processMessage(message) {
|
|
137
|
+
let callTimer;
|
|
138
|
+
let callTimeoutExpired = false;
|
|
139
|
+
try {
|
|
140
|
+
if (this.callTimeout > 0) {
|
|
141
|
+
callTimer = setTimeout(() => {
|
|
142
|
+
callTimeoutExpired = true;
|
|
143
|
+
this.breakMessage();
|
|
144
|
+
}, this.callTimeout);
|
|
145
|
+
}
|
|
146
|
+
await this._encodeMessage(message);
|
|
147
|
+
if (message.messageType !== constants.TNS_MSG_TYPE_ONEWAY_FN) {
|
|
148
|
+
await this._decodeMessage(message);
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (!this.connInProgress &&
|
|
152
|
+
err.code !== errors.ERR_CONNECTION_CLOSED_CODE) {
|
|
153
|
+
await this._recoverFromError(message);
|
|
154
|
+
}
|
|
155
|
+
throw err;
|
|
156
|
+
} finally {
|
|
157
|
+
clearTimeout(callTimer);
|
|
158
|
+
}
|
|
159
|
+
if (message.flushOutBinds) {
|
|
160
|
+
await this.flushOutBindMessage(message);
|
|
161
|
+
}
|
|
162
|
+
this.txnInProgress = message.callStatus & constants.TNS_TXN_IN_PROGRESS;
|
|
163
|
+
if (message.errorOccurred) {
|
|
164
|
+
if (callTimeoutExpired) {
|
|
165
|
+
errors.throwErr(errors.ERR_CALL_TIMEOUT_EXCEEDED, this.callTimeout);
|
|
166
|
+
}
|
|
167
|
+
let err = new Error(message.errorInfo.message);
|
|
168
|
+
err.offset = message.errorInfo.pos;
|
|
169
|
+
err.errorNum = message.errorInfo.num;
|
|
170
|
+
err = errors.transformErr(err);
|
|
171
|
+
if (err.code === errors.ERR_CONNECTION_CLOSED_CODE) {
|
|
172
|
+
this.nsi.disconnect();
|
|
173
|
+
}
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async flushOutBindMessage(message) {
|
|
179
|
+
this.writeBuf.startRequest(constants.TNS_PACKET_TYPE_DATA);
|
|
180
|
+
this.writeBuf.writeUInt8(constants.TNS_MSG_TYPE_FLUSH_OUT_BINDS);
|
|
181
|
+
this.writeBuf.endRequest();
|
|
182
|
+
await this._decodeMessage(message);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Send break packet
|
|
187
|
+
*/
|
|
188
|
+
breakMessage() {
|
|
189
|
+
this._breakInProgress = true;
|
|
190
|
+
this.nsi.sendBreak();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Reset the connection
|
|
195
|
+
*/
|
|
196
|
+
async resetMessage() {
|
|
197
|
+
await this.nsi.reset();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = Protocol;
|