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