oracledb 6.3.0 → 6.4.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 (58) hide show
  1. package/LICENSE.txt +1 -1
  2. package/NOTICE.txt +1 -1
  3. package/README.md +3 -3
  4. package/build/Release/oracledb-6.4.0-darwin-x64.node +0 -0
  5. package/build/Release/oracledb-6.4.0-darwin-x64.node-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-6.4.0-js-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-6.4.0-linux-arm64.node +0 -0
  8. package/build/Release/oracledb-6.4.0-linux-arm64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-6.4.0-linux-x64.node +0 -0
  10. package/build/Release/oracledb-6.4.0-linux-x64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-6.4.0-win32-x64.node +0 -0
  12. package/build/Release/oracledb-6.4.0-win32-x64.node-buildinfo.txt +1 -0
  13. package/examples/README.md +3 -0
  14. package/lib/aqQueue.js +8 -8
  15. package/lib/connection.js +55 -9
  16. package/lib/constants.js +7 -1
  17. package/lib/dbObject.js +72 -1
  18. package/lib/errors.js +27 -8
  19. package/lib/{thin/protocol → impl/datahandlers}/buffer.js +36 -32
  20. package/lib/impl/datahandlers/constants.js +105 -0
  21. package/lib/{thin/protocol → impl/datahandlers}/oson.js +19 -1
  22. package/lib/impl/datahandlers/vector.js +156 -0
  23. package/lib/impl/pool.js +9 -0
  24. package/lib/impl/resultset.js +34 -17
  25. package/lib/lob.js +22 -6
  26. package/lib/oracledb.js +24 -0
  27. package/lib/pool.js +10 -1
  28. package/lib/poolStatistics.js +3 -1
  29. package/lib/settings.js +30 -6
  30. package/lib/sodaDocCursor.js +14 -1
  31. package/lib/thin/connection.js +100 -144
  32. package/lib/thin/dbObject.js +4 -2
  33. package/lib/thin/lob.js +6 -6
  34. package/lib/thin/pool.js +22 -4
  35. package/lib/thin/protocol/capabilities.js +3 -1
  36. package/lib/thin/protocol/constants.js +20 -52
  37. package/lib/thin/protocol/messages/auth.js +1 -1
  38. package/lib/thin/protocol/messages/base.js +21 -18
  39. package/lib/thin/protocol/messages/dataType.js +3 -2
  40. package/lib/thin/protocol/messages/withData.js +38 -6
  41. package/lib/thin/protocol/packet.js +58 -4
  42. package/lib/thin/sqlnet/networkSession.js +13 -1
  43. package/lib/thin/statement.js +7 -7
  44. package/lib/thin/statementCache.js +167 -0
  45. package/lib/transformer.js +13 -4
  46. package/lib/types.js +12 -2
  47. package/lib/util.js +29 -0
  48. package/lib/version.js +2 -2
  49. package/package.json +1 -1
  50. package/build/Release/oracledb-6.3.0-darwin-x64.node +0 -0
  51. package/build/Release/oracledb-6.3.0-darwin-x64.node-buildinfo.txt +0 -1
  52. package/build/Release/oracledb-6.3.0-js-buildinfo.txt +0 -1
  53. package/build/Release/oracledb-6.3.0-linux-arm64.node +0 -0
  54. package/build/Release/oracledb-6.3.0-linux-arm64.node-buildinfo.txt +0 -1
  55. package/build/Release/oracledb-6.3.0-linux-x64.node +0 -0
  56. package/build/Release/oracledb-6.3.0-linux-x64.node-buildinfo.txt +0 -1
  57. package/build/Release/oracledb-6.3.0-win32-x64.node +0 -0
  58. package/build/Release/oracledb-6.3.0-win32-x64.node-buildinfo.txt +0 -1
@@ -322,7 +322,7 @@ class AuthMessage extends Message {
322
322
 
323
323
  this.conn.dbDomain = this.sessionData['AUTH_SC_DB_DOMAIN'];
324
324
  this.conn.dbName = this.sessionData['AUTH_SC_DBUNIQUE_NAME'];
325
- this.conn.maxOpenCursors = Number(this.sessionData['AUTH_MAX_OPEN_CURSORS']);
325
+ this.conn.maxOpenCursors = Number(this.sessionData['AUTH_MAX_OPEN_CURSORS'] || 0);
326
326
  this.conn.serviceName = this.sessionData['AUTH_SC_SERVICE_NAME'];
327
327
  this.conn.instanceName = this.sessionData['AUTH_INSTANCENAME'];
328
328
  const fullVersionNum = Number(this.sessionData['AUTH_VERSION_NO']);
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -90,11 +90,11 @@ class Message {
90
90
  buf.skipBytesChunked();
91
91
  }
92
92
  // batch error codes
93
- let numEntries = buf.readUB2(); // batch error codes array
94
- if (numEntries > 0) {
93
+ const numErrors = buf.readUB2(); // batch error codes array
94
+ if (numErrors > 0) {
95
95
  this.errorInfo.batchErrors = [];
96
96
  const firstByte = buf.readUInt8();
97
- for (let i = 0; i < numEntries; i++) {
97
+ for (let i = 0; i < numErrors; i++) {
98
98
  if (firstByte === constants.TNS_LONG_LENGTH_INDICATOR) {
99
99
  buf.skipUB4(); // chunk length ignored
100
100
  }
@@ -107,14 +107,21 @@ class Message {
107
107
  }
108
108
 
109
109
  // batch error offset
110
- numEntries = buf.readUB2(); // batch error row offset array
111
- if (numEntries > 0) {
110
+ const numOffsets = buf.readUB4(); // batch error row offset array
111
+ if (numOffsets > 0) {
112
+ if (numOffsets > 65535) {
113
+ errors.throwErr(errors.ERR_TOO_MANY_BATCH_ERRORS);
114
+ }
112
115
  const firstByte = buf.readUInt8();
113
- for (let i = 0; i < numEntries; i++) {
116
+ let offset;
117
+ for (let i = 0; i < numOffsets; i++) {
114
118
  if (firstByte === constants.TNS_LONG_LENGTH_INDICATOR) {
115
119
  buf.skipUB4(); // chunk length ignored
116
120
  }
117
- this.errorInfo.batchErrors[i].offset = buf.readUB4();
121
+ offset = buf.readUB4();
122
+ if (i < numErrors) {
123
+ this.errorInfo.batchErrors[i].offset = offset;
124
+ }
118
125
  }
119
126
  if (firstByte === constants.TNS_LONG_LENGTH_INDICATOR) {
120
127
  buf.skipBytes(1); // ignore end marker
@@ -122,10 +129,10 @@ class Message {
122
129
  }
123
130
 
124
131
  // batch error messages
125
- numEntries = buf.readUB2(); // batch error messages array
126
- if (numEntries > 0) {
132
+ const errMsgArr = buf.readUB2(); // batch error messages array
133
+ if (errMsgArr > 0) {
127
134
  buf.skipBytes(1); // ignore packed size
128
- for (let i = 0; i < numEntries; i++) {
135
+ for (let i = 0; i < errMsgArr; i++) {
129
136
  buf.skipUB2(); // skip chunk length
130
137
 
131
138
  this.errorInfo.batchErrors[i].message = buf.readStr(constants.CSFRM_IMPLICIT);
@@ -275,7 +282,7 @@ class Message {
275
282
  const flags = buf.readUB4(); // session flags
276
283
  if (flags & constants.TNS_SESSGET_SESSION_CHANGED) {
277
284
  if (this.connection._drcpEstablishSession) {
278
- this.connection.resetStatementCache();
285
+ this.connection.statementCache.clearOpenCursors();
279
286
  }
280
287
  }
281
288
  this.connection._drcpEstablishSession = false;
@@ -290,7 +297,7 @@ class Message {
290
297
  if (this.connection._currentSchemaModified) {
291
298
  this._writeCurrentSchemaPiggyback(buf);
292
299
  }
293
- if (this.connection._cursorsToClose.size > 0 && !this.connection._drcpEstablishSession) {
300
+ if (this.connection.statementCache._cursorsToClose.size > 0 && !this.connection._drcpEstablishSession) {
294
301
  this.writeCloseCursorsPiggyBack(buf);
295
302
  }
296
303
  if (
@@ -319,11 +326,7 @@ class Message {
319
326
  writeCloseCursorsPiggyBack(buf) {
320
327
  this.writePiggybackHeader(buf, constants.TNS_FUNC_CLOSE_CURSORS);
321
328
  buf.writeUInt8(1);
322
- buf.writeUB4(this.connection._cursorsToClose.size);
323
- for (const cursorNum of this.connection._cursorsToClose.keys()) {
324
- buf.writeUB4(cursorNum);
325
- }
326
- this.connection._cursorsToClose.clear();
329
+ this.connection.statementCache.writeCursorsToClose(buf);
327
330
  }
328
331
 
329
332
  writeCloseTempLobsPiggyback(buf) {
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -392,7 +392,8 @@ const dataTypes = [
392
392
  [constants.TNS_DATA_TYPE_UDS, constants.TNS_DATA_TYPE_UDS, constants.TNS_TYPE_REP_UNIVERSAL],
393
393
  [constants.TNS_DATA_TYPE_TNP, constants.TNS_DATA_TYPE_TNP, constants.TNS_TYPE_REP_UNIVERSAL],
394
394
  [constants.TNS_DATA_TYPE_OER, constants.TNS_DATA_TYPE_OER, constants.TNS_TYPE_REP_UNIVERSAL],
395
- [constants.TNS_DATA_TYPE_OAC, constants.TNS_DATA_TYPE_OAC, constants.TNS_TYPE_REP_UNIVERSAL]
395
+ [constants.TNS_DATA_TYPE_OAC, constants.TNS_DATA_TYPE_OAC, constants.TNS_TYPE_REP_UNIVERSAL],
396
+ [constants.TNS_DATA_TYPE_VECTOR, constants.TNS_DATA_TYPE_VECTOR, constants.TNS_TYPE_REP_UNIVERSAL]
396
397
  ];
397
398
 
398
399
  module.exports = DataTypeMessage;
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -117,7 +117,7 @@ class MessageWithData extends Message {
117
117
  this.errorOccurred = false;
118
118
  this.statement.moreRowsToFetch = false;
119
119
  } else if (this.errorInfo.num !== 0 && this.errorInfo.cursorId !== 0) {
120
- this.connection.statementCache.delete(this.statement.sql);
120
+ this.connection.statementCache._cachedStatements.delete(this.statement.sql);
121
121
  this.statement.returnToCache = false;
122
122
  }
123
123
  if (this.errorInfo.batchErrors) {
@@ -148,15 +148,16 @@ class MessageWithData extends Message {
148
148
  buf.skipUB1();
149
149
  }
150
150
  resultSet.metadata = [];
151
+ const metadata = [];
152
+ const queryVars = [];
151
153
  for (let i = 0; i < statement.numQueryVars; i++) {
152
154
  const variable = this.processColumnInfo(buf, i + 1);
153
155
  if (prevQueryVars && i < prevQueryVars.length) {
154
156
  this._adjustFetchType(prevQueryVars[i], variable);
155
157
  }
156
- statement.queryVars.push(variable);
157
- resultSet.metadata.push(variable.fetchInfo);
158
+ queryVars.push(variable);
159
+ metadata.push(variable.fetchInfo);
158
160
  }
159
-
160
161
  let numBytes = buf.readUB4();
161
162
  if (numBytes > 0) {
162
163
  buf.skipBytesChunked(); // current date
@@ -170,6 +171,13 @@ class MessageWithData extends Message {
170
171
  buf.skipBytesChunked();
171
172
  }
172
173
 
174
+ /*
175
+ * The message state(resultSet) and statement state(queryVars) is modified
176
+ * at end of the DescribeInfo function so that an OutOfPacketsError
177
+ * won't cause partial information state to be stored.
178
+ */
179
+ resultSet.metadata = metadata;
180
+ statement.queryVars = queryVars;
173
181
  this.resultSetsToSetup.push(resultSet);
174
182
  }
175
183
 
@@ -223,6 +231,7 @@ class MessageWithData extends Message {
223
231
  nullable: nullable
224
232
  };
225
233
  fetchInfo.isJson = Boolean(udsFlags & constants.TNS_UDS_FLAGS_IS_JSON);
234
+ fetchInfo.isOson = Boolean(udsFlags & constants.TNS_UDS_FLAGS_IS_OSON);
226
235
  if (buf.caps.ttcFieldVersion >= constants.TNS_CCAP_FIELD_VERSION_23_1) {
227
236
  numBytes = buf.readUB4();
228
237
  if (numBytes > 0) {
@@ -254,6 +263,19 @@ class MessageWithData extends Message {
254
263
  buf.skipUB4(); // flags
255
264
  }
256
265
  }
266
+ if (buf.caps.ttcFieldVersion >= constants.TNS_CCAP_FIELD_VERSION_23_4) {
267
+ const dimensions = buf.readUB4();
268
+ const vectorFormat = buf.readUInt8();
269
+ const vectorFlags = buf.readUInt8();
270
+ if (fetchInfo.dbType === types.DB_TYPE_VECTOR) {
271
+ if (!(vectorFlags & constants.VECTOR_META_FLAG_FLEXIBLE_DIM)) {
272
+ fetchInfo.vectorDimensions = dimensions;
273
+ }
274
+ if (vectorFormat !== constants.VECTOR_FORMAT_FLEX) {
275
+ fetchInfo.vectorFormat = vectorFormat;
276
+ }
277
+ }
278
+ }
257
279
 
258
280
  switch (fetchInfo.dbType) {
259
281
  case types.DB_TYPE_VARCHAR:
@@ -481,6 +503,8 @@ class MessageWithData extends Message {
481
503
  }
482
504
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_JSON) {
483
505
  colValue = buf.readOson();
506
+ } else if (oraTypeNum === constants.TNS_DATA_TYPE_VECTOR) {
507
+ colValue = buf.readVector();
484
508
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_INT_NAMED) {
485
509
  const obj = buf.readDbObject();
486
510
  if (obj.packedData) {
@@ -589,7 +613,8 @@ class MessageWithData extends Message {
589
613
  if (variable.type === types.DB_TYPE_CLOB ||
590
614
  variable.type === types.DB_TYPE_NCLOB ||
591
615
  variable.type === types.DB_TYPE_BLOB ||
592
- variable.type === types.DB_TYPE_JSON) {
616
+ variable.type === types.DB_TYPE_JSON ||
617
+ variable.type === types.DB_TYPE_VECTOR) {
593
618
  if (variable.type !== variable.fetchInfo.fetchType) {
594
619
  variable.type = variable.fetchInfo.fetchType;
595
620
  variable.maxSize = constants.TNS_MAX_LONG_LENGTH;
@@ -674,6 +699,9 @@ class MessageWithData extends Message {
674
699
  } else if (variable.type === types.DB_TYPE_JSON) {
675
700
  contFlag = constants.TNS_LOB_PREFETCH_FLAG;
676
701
  maxSize = lobPrefetchLength = constants.TNS_JSON_MAX_LENGTH;
702
+ } else if (variable.type === types.DB_TYPE_VECTOR) {
703
+ contFlag = constants.TNS_LOB_PREFETCH_FLAG;
704
+ maxSize = lobPrefetchLength = constants.TNS_VECTOR_MAX_LENGTH;
677
705
  }
678
706
  buf.writeUInt8(oraTypeNum);
679
707
  buf.writeUInt8(flag);
@@ -794,8 +822,10 @@ class MessageWithData extends Message {
794
822
  ) {
795
823
  buf.writeOracleDate(value, variable.type);
796
824
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_BINARY_DOUBLE) {
825
+ buf.writeUInt8(8);
797
826
  buf.writeBinaryDouble(value);
798
827
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_BINARY_FLOAT) {
828
+ buf.writeUInt8(4);
799
829
  buf.writeBinaryFloat(value);
800
830
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_CURSOR) {
801
831
  let cursor = value;
@@ -823,6 +853,8 @@ class MessageWithData extends Message {
823
853
  buf.writeBytesWithLength(Buffer.from(value));
824
854
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_JSON) {
825
855
  buf.writeOson(value);
856
+ } else if (oraTypeNum === constants.TNS_DATA_TYPE_VECTOR) {
857
+ buf.writeVector(value);
826
858
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_INT_NAMED) {
827
859
  buf.writeDbObject(value);
828
860
  } else {
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -26,11 +26,12 @@
26
26
 
27
27
  'use strict';
28
28
 
29
- const { BaseBuffer } = require('./buffer.js');
29
+ const { BaseBuffer } = require('../../impl/datahandlers/buffer.js');
30
30
  const { Buffer } = require('buffer');
31
- const constants = require("./constants.js");
32
- const oson = require('./oson.js');
31
+ const constants = require('./constants.js');
32
+ const oson = require('../../impl/datahandlers/oson.js');
33
33
  const utils = require('./utils.js');
34
+ const vector = require('../../impl/datahandlers/vector.js');
34
35
  const errors = require("../../errors.js");
35
36
 
36
37
  const TNS_BASE64_ALPHABET_ARRAY = Buffer.from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 'utf8');
@@ -395,6 +396,22 @@ class ReadPacket extends BaseBuffer {
395
396
  return {rba, partitionID, blockNum, slotNum};
396
397
  }
397
398
 
399
+ /**
400
+ * Reads VECTOR data (QLocator followed by data) and decodes it into a
401
+ * JavaScript object.
402
+ */
403
+ readVector() {
404
+ const numBytes = this.readUB4();
405
+ if (numBytes === 0) {
406
+ return null;
407
+ }
408
+ this.skipUB8(); // size (unused)
409
+ this.skipUB4(); // chunk size (unused)
410
+ const decoder = new vector.VectorDecoder(this.readBytesWithLength());
411
+ this.skipBytesChunked(); // locator (unused)
412
+ return decoder.decode();
413
+ }
414
+
398
415
  skipBytesChunked() {
399
416
  const numBytes = this.readUInt8();
400
417
  if (numBytes === 0 || numBytes === constants.TNS_NULL_LENGTH_INDICATOR) {
@@ -479,6 +496,9 @@ class WritePacket extends BaseBuffer {
479
496
  buf = Buffer.from(buf);
480
497
  this.startPacket();
481
498
  }
499
+ if (!this.nsi.ntAdapter) {
500
+ errors.throwErr(errors.ERR_INVALID_CONNECTION);
501
+ }
482
502
  this.nsi.ntAdapter.send(buf);
483
503
  }
484
504
 
@@ -537,6 +557,40 @@ class WritePacket extends BaseBuffer {
537
557
  this.protocol.sequenceId = (this.protocol.sequenceId + 1) % 256;
538
558
  }
539
559
 
560
+ /**
561
+ * Encodes a JavaScript object into VECTOR data and then writes it (QLocator
562
+ * followed by data) to the buffer.
563
+ */
564
+ writeVector(value) {
565
+ const encoder = new vector.VectorEncoder();
566
+ const buf = encoder.encode(value);
567
+ this.writeQLocator(buf.length);
568
+ this.writeBytesWithLength(buf);
569
+ }
570
+
571
+ //---------------------------------------------------------------------------
572
+ // writeQLocator()
573
+ //
574
+ // Writes a QLocator. QLocators are always 40 bytes in length.
575
+ //---------------------------------------------------------------------------
576
+ writeQLocator(numBytes) {
577
+ this.writeUB4(40); // QLocator length
578
+ this.writeUInt8(40); // repeated length
579
+ this.writeUInt16BE(38); // internal length
580
+ this.writeUInt16BE(constants.TNS_LOB_QLOCATOR_VERSION);
581
+ this.writeUInt8(constants.TNS_LOB_LOC_FLAGS_VALUE_BASED |
582
+ constants.TNS_LOB_LOC_FLAGS_BLOB | constants.TNS_LOB_LOC_FLAGS_ABSTRACT);
583
+ this.writeUInt8(constants.TNS_LOB_LOC_FLAGS_INIT);
584
+ this.writeUInt16BE(0); // additional flags
585
+ this.writeUInt16BE(1); // byt1
586
+ this.writeUInt64BE(numBytes);
587
+ this.writeUInt16BE(0); // unused
588
+ this.writeUInt16BE(0); // csid
589
+ this.writeUInt16BE(0); // unused
590
+ this.writeUInt64BE(0); // unused
591
+ this.writeUInt64BE(0); // unused
592
+ }
593
+
540
594
  }
541
595
 
542
596
  module.exports = {
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -530,6 +530,18 @@ class NetworkSession {
530
530
  await this.connect1(address, addressNode, userConfig);
531
531
  }
532
532
 
533
+ /**
534
+ * Force Disconnect the stream, primarily used
535
+ * to disconnect dead/hung connections.
536
+ */
537
+ forceDisconnect(err) {
538
+ if (!this.connected) {
539
+ return;
540
+ }
541
+ this.ntAdapter.stream.destroy(err);
542
+ this.connected = false;
543
+ }
544
+
533
545
  /**
534
546
  * Disconnect
535
547
  * @param {int} type of disconnect
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -219,10 +219,10 @@ class Parser {
219
219
 
220
220
  /**
221
221
  * Single line comments consist of two dashes and all characters up to the
222
- * next line break. This method is called when the first dash is detected
223
- * and checks for the subsequent dash. If found, the single line comment
224
- * is traversed and the current position is updated; otherwise, the
225
- * current position is left untouched.
222
+ * next line break (or the end of the data). This method is called when
223
+ * the first dash is detected and checks for the subsequent dash. If found,
224
+ * the single line comment is traversed and the current position is updated;
225
+ * otherwise, the current position is left untouched.
226
226
  */
227
227
  _parseSingleLineComment() {
228
228
  let inComment = false;
@@ -233,15 +233,15 @@ class Parser {
233
233
  ch = this.sqlData[pos];
234
234
  if (!inComment) {
235
235
  if (ch !== '-') {
236
- break;
236
+ return;
237
237
  }
238
238
  inComment = true;
239
239
  } else if (ch === '\n') {
240
- this.pos = pos;
241
240
  break;
242
241
  }
243
242
  pos += 1;
244
243
  }
244
+ this.pos = pos;
245
245
  }
246
246
 
247
247
  /**
@@ -0,0 +1,167 @@
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
+ // Node file defining the StatementCache class used to manage cached statements
26
+ //-----------------------------------------------------------------------------
27
+
28
+ const errors = require("../errors.js");
29
+ const { Statement } = require("./statement");
30
+
31
+ class StatementCache {
32
+ constructor(maxSize) {
33
+ this._cachedStatements = new Map();
34
+ this._maxSize = maxSize;
35
+ this._cursorsToClose = new Set();
36
+ this._openCursors = new Set();
37
+ }
38
+
39
+ //---------------------------------------------------------------------------
40
+ // _addCursorToClose()
41
+ //
42
+ // Add the statement's cursor to the list of cursors that need to be closed.
43
+ //---------------------------------------------------------------------------
44
+ _addCursorToClose(statement) {
45
+ if (this._cursorsToClose.has(statement.cursorId)) {
46
+ const reason = `attempt to close cursor ${statement.cursorId} twice`;
47
+ errors.throwErr(errors.ERR_INTERNAL, reason);
48
+ }
49
+ if (statement.cursorId != 0) {
50
+ this._cursorsToClose.add(statement.cursorId);
51
+ }
52
+ this._openCursors.delete(statement);
53
+ }
54
+
55
+ //---------------------------------------------------------------------------
56
+ // _adjustCache()
57
+ // Adjust the cache so that no more than the maximum number of statements
58
+ // are cached by removing least recently used statements
59
+ //---------------------------------------------------------------------------
60
+ _adjustCache() {
61
+ while (this._cachedStatements.size > this._maxSize) {
62
+ const sql = this._cachedStatements.keys().next().value;
63
+ const stmt = this._cachedStatements.get(sql);
64
+ this._cachedStatements.delete(sql);
65
+ if (stmt.inUse) {
66
+ stmt.returnToCache = false;
67
+ } else if (stmt.cursorId !== 0) {
68
+ this._addCursorToClose(stmt);
69
+ }
70
+ }
71
+ }
72
+
73
+ //---------------------------------------------------------------------------
74
+ //clearOpenCursors() {
75
+ // Clears the list of open cursors and removes the list of cursors that
76
+ // need to be closed. This is required when a DRCP session change has
77
+ // taken place as the cursor ID values are invalidated.
78
+ //---------------------------------------------------------------------------
79
+ clearOpenCursors() {
80
+ const newOpenCursors = new Set();
81
+ for (const stmt of this._openCursors) {
82
+ stmt.cursorId = 0;
83
+ if (stmt.inUse) {
84
+ newOpenCursors.add(stmt);
85
+ }
86
+ }
87
+ this._openCursors = newOpenCursors;
88
+ }
89
+
90
+ //---------------------------------------------------------------------------
91
+ // get_statement()
92
+ // Get a statement from the statement cache, or prepare a new statement
93
+ // for use. If a statement is already in use or the statement is not
94
+ // supposed to be cached, a copy will be made (and not returned to the
95
+ // cache).
96
+ //---------------------------------------------------------------------------
97
+ getStatement(sql, cacheStatement = false) {
98
+ let stmt = null;
99
+ if (sql) {
100
+ stmt = this._cachedStatements.get(sql);
101
+ if (!cacheStatement) {
102
+ this._openCursors.add(stmt);
103
+ this._cachedStatements.delete(sql);
104
+ }
105
+ }
106
+ if (!stmt) {
107
+ stmt = new Statement();
108
+ if (sql) {
109
+ stmt._prepare(sql);
110
+ }
111
+ if (cacheStatement && !stmt.isDdl && this._maxSize > 0) {
112
+ stmt.returnToCache = true;
113
+ this._cachedStatements.set(sql, stmt);
114
+ this._adjustCache();
115
+ }
116
+ this._openCursors.add(stmt);
117
+ } else if (stmt.inUse || !cacheStatement) {
118
+ stmt = stmt._copy();
119
+ this._openCursors.add(stmt);
120
+ } else {
121
+ this._cachedStatements.delete(sql);
122
+ this._cachedStatements.set(sql, stmt);
123
+ }
124
+ stmt.inUse = true;
125
+ return stmt;
126
+ }
127
+
128
+ //---------------------------------------------------------------------------
129
+ // returnStatement()
130
+ // Return the statement to the statement cache, if applicable. If the
131
+ // statement must not be returned to the statement cache, add the cursor
132
+ // id to the list of cursor ids to close on the next round trip to the
133
+ // database. Clear all bind variables and fetch variables in order to
134
+ // ensure that unnecessary references are not retained.
135
+ //---------------------------------------------------------------------------
136
+ returnStatement(statement) {
137
+ if (statement.bindInfoList) {
138
+ statement.bindInfoList.forEach(bindInfo => {
139
+ bindInfo.bindVar = null;
140
+ });
141
+ }
142
+ if (statement.queryVars) {
143
+ statement.queryVars.forEach(queryVar => {
144
+ queryVar.values.fill(null);
145
+ });
146
+ }
147
+ if (statement.returnToCache) {
148
+ statement.inUse = false;
149
+ } else if (statement.cursorId !== 0) {
150
+ this._addCursorToClose(statement);
151
+ }
152
+ }
153
+
154
+ //---------------------------------------------------------------------------
155
+ // writeCursorsToClose()
156
+ // Write the list of cursors to close to the buffer.
157
+ //---------------------------------------------------------------------------
158
+ writeCursorsToClose(buf) {
159
+ buf.writeUB4(this._cursorsToClose.size);
160
+ for (const cursorNum of this._cursorsToClose.keys()) {
161
+ buf.writeUB4(cursorNum);
162
+ }
163
+ this._cursorsToClose.clear();
164
+ }
165
+ }
166
+
167
+ module.exports = StatementCache;
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2023, 2024, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -34,6 +34,7 @@ const constants = require('./constants.js');
34
34
  const errors = require('./errors.js');
35
35
  const util = require('util');
36
36
  const types = require('./types.js');
37
+ const nodbUtil = require('./util.js');
37
38
 
38
39
  //-----------------------------------------------------------------------------
39
40
  // checkType()
@@ -86,9 +87,9 @@ function transformJsonValue(value) {
86
87
 
87
88
  // handle simple scalars
88
89
  if (value === undefined || value === null ||
89
- typeof value === 'number' || typeof value === 'string' ||
90
- typeof value === 'boolean' || Buffer.isBuffer(value) ||
91
- util.isDate(value))
90
+ typeof value === 'number' || typeof value === 'string' ||
91
+ typeof value === 'boolean' || Buffer.isBuffer(value) ||
92
+ util.isDate(value) || nodbUtil.isVectorValue(value))
92
93
  return value;
93
94
 
94
95
  // arrays are transformed to a new array with processed values
@@ -220,6 +221,14 @@ function transformValueIn(info, value, options) {
220
221
  checkType(info, options, types.DB_TYPE_OBJECT);
221
222
  return value._impl;
222
223
 
224
+ // handle vectors
225
+ } else if (value instanceof Float32Array || value instanceof Float64Array ||
226
+ value instanceof Int8Array) {
227
+ checkType(info, options, types.DB_TYPE_VECTOR);
228
+ return value;
229
+ } else if (info.type === types.DB_TYPE_VECTOR && Array.isArray(value)) {
230
+ return new Float64Array(value);
231
+
223
232
  // handle arrays
224
233
  } else if (options.allowArray && Array.isArray(value)) {
225
234
  info.isArray = true;
package/lib/types.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -199,6 +199,9 @@ const DB_TYPE_VARCHAR = new DbType(2001,
199
199
  const DB_TYPE_XMLTYPE = new DbType(2032,
200
200
  "DB_TYPE_XMLTYPE", "XMLTYPE",
201
201
  { oraTypeNum: 109, csfrm: constants.CSFRM_IMPLICIT, bufferSizeFactor: 2147483647 });
202
+ const DB_TYPE_VECTOR = new DbType(2033,
203
+ "DB_TYPE_VECTOR", "VECTOR",
204
+ { oraTypeNum: 127 });
202
205
 
203
206
  // database type conversion map: the top level key refers to the database
204
207
  // type being fetched and the value is another map; this map's key is the
@@ -276,6 +279,11 @@ const DB_TYPE_CONVERSION_MAP = new Map([
276
279
  [DB_TYPE_UROWID, new Map([
277
280
  [DB_TYPE_VARCHAR, DB_TYPE_ROWID]
278
281
  ])],
282
+ [DB_TYPE_VECTOR, new Map([
283
+ [DB_TYPE_VARCHAR, DB_TYPE_LONG],
284
+ [DB_TYPE_LONG, DB_TYPE_LONG],
285
+ [DB_TYPE_CLOB, DB_TYPE_CLOB]
286
+ ])],
279
287
  ]);
280
288
 
281
289
  // default fetch type map
@@ -308,7 +316,8 @@ const DB_TYPE_FETCH_TYPE_MAP = new Map([
308
316
  [DB_TYPE_TIMESTAMP_TZ, DB_TYPE_TIMESTAMP_TZ],
309
317
  [DB_TYPE_UROWID, DB_TYPE_UROWID],
310
318
  [DB_TYPE_VARCHAR, DB_TYPE_VARCHAR],
311
- [DB_TYPE_XMLTYPE, DB_TYPE_XMLTYPE]
319
+ [DB_TYPE_XMLTYPE, DB_TYPE_XMLTYPE],
320
+ [DB_TYPE_VECTOR, DB_TYPE_VECTOR]
312
321
  ]);
313
322
 
314
323
  // additional aliases for types by column type name
@@ -353,6 +362,7 @@ module.exports = {
353
362
  DB_TYPE_TIMESTAMP_TZ,
354
363
  DB_TYPE_UROWID,
355
364
  DB_TYPE_VARCHAR,
365
+ DB_TYPE_VECTOR,
356
366
  DB_TYPE_CONVERSION_MAP,
357
367
  DB_TYPE_FETCH_TYPE_MAP,
358
368
  DB_TYPE_XMLTYPE,