oracledb 6.4.0 → 6.5.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/README.md +2 -2
  2. package/build/Release/oracledb-6.5.0-darwin-x64.node +0 -0
  3. package/build/Release/oracledb-6.5.0-darwin-x64.node-buildinfo.txt +1 -0
  4. package/build/Release/oracledb-6.5.0-js-buildinfo.txt +1 -0
  5. package/build/Release/oracledb-6.5.0-linux-arm64.node +0 -0
  6. package/build/Release/oracledb-6.5.0-linux-arm64.node-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-6.5.0-linux-x64.node +0 -0
  8. package/build/Release/oracledb-6.5.0-linux-x64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-6.5.0-win32-x64.node +0 -0
  10. package/build/Release/oracledb-6.5.0-win32-x64.node-buildinfo.txt +1 -0
  11. package/lib/aqQueue.js +1 -1
  12. package/lib/connection.js +11 -14
  13. package/lib/errors.js +24 -2
  14. package/lib/impl/connection.js +2 -1
  15. package/lib/impl/datahandlers/constants.js +6 -5
  16. package/lib/impl/datahandlers/oson.js +301 -150
  17. package/lib/impl/datahandlers/vector.js +3 -22
  18. package/lib/oracledb.js +11 -1
  19. package/lib/pool.js +7 -1
  20. package/lib/settings.js +1 -0
  21. package/lib/thin/connection.js +23 -52
  22. package/lib/thin/pool.js +62 -6
  23. package/lib/thin/protocol/capabilities.js +17 -5
  24. package/lib/thin/protocol/constants.js +14 -2
  25. package/lib/thin/protocol/messages/auth.js +1 -1
  26. package/lib/thin/protocol/messages/base.js +8 -10
  27. package/lib/thin/protocol/messages/dataType.js +1 -0
  28. package/lib/thin/protocol/messages/execute.js +5 -1
  29. package/lib/thin/protocol/messages/{connectionCookie.js → fastAuth.js} +18 -16
  30. package/lib/thin/protocol/messages/fetch.js +4 -0
  31. package/lib/thin/protocol/messages/index.js +2 -2
  32. package/lib/thin/protocol/messages/protocol.js +9 -1
  33. package/lib/thin/protocol/messages/withData.js +2 -5
  34. package/lib/thin/protocol/packet.js +35 -4
  35. package/lib/thin/protocol/protocol.js +10 -5
  36. package/lib/thin/protocol/utils.js +11 -18
  37. package/lib/thin/sqlnet/constants.js +5 -2
  38. package/lib/thin/sqlnet/ezConnectResolver.js +9 -5
  39. package/lib/thin/sqlnet/navNodes.js +4 -3
  40. package/lib/thin/sqlnet/networkSession.js +14 -3
  41. package/lib/thin/sqlnet/packet.js +2 -5
  42. package/lib/thin/statement.js +36 -0
  43. package/lib/thin/statementCache.js +34 -11
  44. package/lib/thin/util.js +2 -2
  45. package/lib/transformer.js +9 -3
  46. package/lib/types.js +9 -1
  47. package/lib/util.js +1 -1
  48. package/lib/version.js +1 -1
  49. package/package.json +1 -1
  50. package/build/Release/oracledb-6.4.0-darwin-x64.node +0 -0
  51. package/build/Release/oracledb-6.4.0-darwin-x64.node-buildinfo.txt +0 -1
  52. package/build/Release/oracledb-6.4.0-js-buildinfo.txt +0 -1
  53. package/build/Release/oracledb-6.4.0-linux-arm64.node +0 -0
  54. package/build/Release/oracledb-6.4.0-linux-arm64.node-buildinfo.txt +0 -1
  55. package/build/Release/oracledb-6.4.0-linux-x64.node +0 -0
  56. package/build/Release/oracledb-6.4.0-linux-x64.node-buildinfo.txt +0 -1
  57. package/build/Release/oracledb-6.4.0-win32-x64.node +0 -0
  58. package/build/Release/oracledb-6.4.0-win32-x64.node-buildinfo.txt +0 -1
@@ -37,12 +37,12 @@ const PingMessage = require('./ping.js');
37
37
  const ProtocolMessage = require('./protocol.js');
38
38
  const RollbackMessage = require('./rollback.js');
39
39
  const SessionReleaseMessage = require('./sessionRelease.js');
40
- const ConnectionCookieMessage = require('./connectionCookie.js');
40
+ const FastAuthMessage = require('./fastAuth.js');
41
41
 
42
42
  module.exports = {
43
43
  AuthMessage,
44
44
  CommitMessage,
45
- ConnectionCookieMessage,
45
+ FastAuthMessage,
46
46
  DataTypeMessage,
47
47
  ExecuteMessage,
48
48
  FetchMessage,
@@ -53,6 +53,7 @@ class ProtocolMessage extends Message {
53
53
  processMessage(buf, messageType) {
54
54
  if (messageType === constants.TNS_MSG_TYPE_PROTOCOL) {
55
55
  this.processProtocolInfo(buf);
56
+ this.endOfResponse = !this.connection.nscon.endOfRequestSupport;
56
57
  } else {
57
58
  super.processMessage(buf, messageType);
58
59
  }
@@ -75,7 +76,14 @@ class ProtocolMessage extends Message {
75
76
  const serverCompileCaps = buf.readBytesWithLength();
76
77
  if (serverCompileCaps) {
77
78
  this.serverCompileCaps = Buffer.from(serverCompileCaps);
78
- buf.caps.adjustForServerCompileCaps(this.serverCompileCaps);
79
+ buf.caps.adjustForServerCompileCaps(this.serverCompileCaps, this.connection.nscon);
80
+
81
+ // Set the maximum OSON field name size
82
+ if (buf.caps.ttcFieldVersion >= constants.TNS_CCAP_FIELD_VERSION_23_1) {
83
+ this.connection._osonMaxFieldNameSize = 65535;
84
+ } else {
85
+ this.connection._osonMaxFieldNameSize = 255;
86
+ }
79
87
  }
80
88
  const serverRunTimeCaps = buf.readBytesWithLength();
81
89
  if (serverRunTimeCaps) {
@@ -85,6 +85,7 @@ class MessageWithData extends Message {
85
85
  this.processIOVector(buf);
86
86
  } else if (messageType === constants.TNS_MSG_TYPE_FLUSH_OUT_BINDS) {
87
87
  this.flushOutBinds = true;
88
+ this.endOfResponse = true;
88
89
  } else if (messageType === constants.TNS_MSG_TYPE_ERROR) {
89
90
  this.processErrorInfo(buf);
90
91
  } else {
@@ -92,10 +93,6 @@ class MessageWithData extends Message {
92
93
  }
93
94
  }
94
95
 
95
- hasMoreData() {
96
- return !this.processedError && !this.flushOutBinds;
97
- }
98
-
99
96
  processErrorInfo(buf) {
100
97
  super.processErrorInfo(buf);
101
98
  if (this.errorInfo.cursorId !== 0) {
@@ -852,7 +849,7 @@ class MessageWithData extends Message {
852
849
  } else if ([constants.TNS_DATA_TYPE_ROWID, constants.TNS_DATA_TYPE_UROWID].includes(oraTypeNum)) {
853
850
  buf.writeBytesWithLength(Buffer.from(value));
854
851
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_JSON) {
855
- buf.writeOson(value);
852
+ buf.writeOson(value, this.connection._osonMaxFieldNameSize);
856
853
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_VECTOR) {
857
854
  buf.writeVector(value);
858
855
  } else if (oraTypeNum === constants.TNS_DATA_TYPE_INT_NAMED) {
@@ -35,6 +35,9 @@ const vector = require('../../impl/datahandlers/vector.js');
35
35
  const errors = require("../../errors.js");
36
36
 
37
37
  const TNS_BASE64_ALPHABET_ARRAY = Buffer.from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 'utf8');
38
+ const FAST_AUTH_END_OF_RPC_VALUE = 0x800;
39
+ const FAST_AUTH_END_OF_RPC_OFFSET = 0x8;
40
+ const MSG_TYPE_OFFSET = 11;
38
41
 
39
42
  /**
40
43
  * Class used for byte chunks used in the ChunkedBytesBuffer.
@@ -290,14 +293,38 @@ class ReadPacket extends BaseBuffer {
290
293
  this.packetNum = packet.num;
291
294
  }
292
295
 
293
- async waitForPackets() {
294
- const packet = await this.nsi.recvPacket();
296
+ /**
297
+ * Read packets from network.
298
+ * If checkRequestBoundary is passed as true, it
299
+ * would read all packets until end of request
300
+ * boundary is seen in nwk header.
301
+ */
302
+ async waitForPackets(checkRequestBoundary = false) {
303
+ let packet = await this.nsi.recvPacket();
295
304
  if (!this.savedPackets) {
296
305
  this.savedPackets = [packet];
297
306
  this.savedPacketPos = 0;
298
307
  } else {
299
308
  this.savedPackets.push(packet);
300
309
  }
310
+ if (checkRequestBoundary && this.nsi.endOfRequestSupport) {
311
+ while (packet.type === constants.TNS_PACKET_TYPE_DATA) {
312
+ // End Marker
313
+ if ((packet.buf.readUInt16BE(8) &
314
+ constants.TNS_DATA_FLAGS_END_OF_REQUEST)) {
315
+ break;
316
+ }
317
+
318
+ // Single Byte 1D packet
319
+ if (packet.buf.length === MSG_TYPE_OFFSET &&
320
+ packet.buf[MSG_TYPE_OFFSET - 1] ===
321
+ constants.TNS_MSG_TYPE_END_OF_REQUEST) {
322
+ break;
323
+ }
324
+ packet = await this.nsi.recvPacket();
325
+ this.savedPackets.push(packet);
326
+ }
327
+ }
301
328
  this.startPacket(this.savedPackets[this.savedPacketPos++]);
302
329
  }
303
330
 
@@ -495,6 +522,10 @@ class WritePacket extends BaseBuffer {
495
522
  if (!finalPacket) {
496
523
  buf = Buffer.from(buf);
497
524
  this.startPacket();
525
+ } else {
526
+ // Write End of RPC bit in last packet used only for fastAuth Message.
527
+ this.buf.writeUInt16BE(FAST_AUTH_END_OF_RPC_VALUE,
528
+ FAST_AUTH_END_OF_RPC_OFFSET);
498
529
  }
499
530
  if (!this.nsi.ntAdapter) {
500
531
  errors.throwErr(errors.ERR_INVALID_CONNECTION);
@@ -545,9 +576,9 @@ class WritePacket extends BaseBuffer {
545
576
  * Encodes a JavaScript object into OSON and then writes it (QLocator
546
577
  * followed by data) to the buffer.
547
578
  */
548
- writeOson(value) {
579
+ writeOson(value, osonMaxFieldSize) {
549
580
  const encoder = new oson.OsonEncoder();
550
- const buf = encoder.encode(value);
581
+ const buf = encoder.encode(value, osonMaxFieldSize);
551
582
  this.writeQLocator(buf.length);
552
583
  this.writeBytesWithLength(buf);
553
584
  }
@@ -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
  //
@@ -49,7 +49,7 @@ class Protocol {
49
49
  * Compile and Runtime capabilities negotiated with Server
50
50
  * @type {object}
51
51
  */
52
- this.caps = new Capabilities(conn.nscon.sAtts.version);
52
+ this.caps = new Capabilities(conn.nscon);
53
53
  this.writeBuf = new WritePacket(conn.nscon, this.caps, this);
54
54
  this.readBuf = new ReadPacket(conn.nscon, this.caps);
55
55
  this.callTimeout = 0;
@@ -67,11 +67,11 @@ class Protocol {
67
67
  */
68
68
  async _decodeMessage(message) {
69
69
  message.preProcess();
70
- await this.readBuf.waitForPackets();
70
+ await this.readBuf.waitForPackets(true);
71
71
  while (true) { // eslint-disable-line
72
72
  if (this.nsi.isBreak) {
73
73
  await this.resetMessage();
74
- delete this.readBuf.savedBuffers;
74
+ delete this.readBuf.savedPackets;
75
75
  await this.readBuf.waitForPackets();
76
76
  }
77
77
  try {
@@ -164,7 +164,12 @@ class Protocol {
164
164
  if (message.flushOutBinds) {
165
165
  await this.flushOutBindMessage(message);
166
166
  }
167
- this.txnInProgress = message.callStatus & constants.TNS_TXN_IN_PROGRESS;
167
+ this.txnInProgress = Boolean(message.callStatus & constants.TNS_EOCS_FLAGS_TXN_IN_PROGRESS);
168
+ // processes the call status flags returned by the server.
169
+ if (message.callStatus & constants.TNS_EOCS_FLAGS_SESS_RELEASE) {
170
+ message.connection.statementCache.clearCursors();
171
+ }
172
+
168
173
  if (message.errorOccurred) {
169
174
  if (callTimeoutExpired) {
170
175
  errors.throwErr(errors.ERR_CALL_TIMEOUT_EXCEEDED, this.callTimeout);
@@ -52,30 +52,23 @@ function encodeRowID(rowID) {
52
52
  }
53
53
  }
54
54
 
55
- // obfuscate value and clear memory for Buffers as they are from Buffer pool
56
- // and are possible to stay longer
55
+ // obfuscate value
57
56
  function setObfuscatedValue(value) {
58
- const buf = crypto.randomBytes(Buffer.byteLength(value));
59
- const bytes = Buffer.from(value, 'utf8');
60
- const len = Buffer.byteLength(value);
61
-
62
- const arr = [];
63
- for (let i = 0; i < len; i++) {
64
- arr.push(buf[i] ^ bytes[i]);
57
+ const valueBytes = Buffer.from(value);
58
+ const obfuscatedBytes = crypto.randomBytes(valueBytes.length);
59
+ for (let i = 0; i < valueBytes.length; i++) {
60
+ valueBytes[i] = obfuscatedBytes[i] ^ valueBytes[i];
65
61
  }
66
- bytes.fill(0);
67
- return {obfuscatedValue: buf, value: arr};
62
+ return {obfuscatedValue: obfuscatedBytes, value: valueBytes};
68
63
  }
69
64
 
70
- // returns the Deobfuscated value, after removing the obfuscation
65
+ // returns the deobfuscated value, after removing the obfuscation
71
66
  // and clear memory of temporary Buffers coming from Buffer pool
72
- function getDeobfuscatedValue(value, obfuscatedValue) {
73
- const arr = [];
74
- for (let i = 0; i < value.length; i++) {
75
- arr.push(value[i] ^ obfuscatedValue[i]);
67
+ function getDeobfuscatedValue(valueBytes, obfuscatedBytes) {
68
+ const buf = Buffer.from(valueBytes);
69
+ for (let i = 0; i < valueBytes.length; i++) {
70
+ buf[i] = valueBytes[i] ^ obfuscatedBytes[i];
76
71
  }
77
- const buf = Buffer.from(arr);
78
- arr.fill(0);
79
72
  const retVal = buf.toString();
80
73
  buf.fill(0);
81
74
  return retVal;
@@ -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
  //
@@ -107,7 +107,8 @@ module.exports = {
107
107
  NSGRAW: 0x1000, // I/O is direct to/from transport
108
108
  TNS_VERSION_DESIRED: 319,
109
109
  TNS_VERSION_MINIMUM: 300,
110
- TNS_VERSION_MIN_UUID: 319,
110
+ TNS_VERSION_MIN_DATA_FLAGS: 318,
111
+ TNS_VERSION_MIN_END_OF_RESPONSE: 319,
111
112
  TNS_UUID_OFFSET: 45,
112
113
 
113
114
  /* Accept Packet */
@@ -143,6 +144,8 @@ module.exports = {
143
144
  // Accept flag2
144
145
  NSPACOOB: 0x00000001, // OOB support check at connection time
145
146
  NSGPCHKSCMD: 0x01000000, // Support for Poll and Check logic
147
+ TNS_ACCEPT_FLAG_HAS_END_OF_REQUEST: 0x02000000,
148
+ TNS_ACCEPT_FLAG_FAST_AUTH: 0x10000000, // Support Fast Auth
146
149
 
147
150
  /* Redirect packet */
148
151
  NSPRDLEN: 8, // Length of redirect data
@@ -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
  //
@@ -164,6 +164,7 @@ class EZConnectResolver {
164
164
  const poolConnectionClass = this.urlProps.get("POOL_CONNECTION_CLASS");
165
165
  const poolPurity = this.urlProps.get("POOL_PURITY");
166
166
  const serviceTag = this.urlProps.get("SERVICE_TAG");
167
+ const poolBoundary = this.urlProps.get("POOL_BOUNDARY");
167
168
 
168
169
  const parts = [];
169
170
  if (serviceName)
@@ -182,6 +183,8 @@ class EZConnectResolver {
182
183
  parts.push(`(SERVICE_TAG=${serviceTag})`);
183
184
  if (connectionIdPrefix)
184
185
  parts.push(`(CONNECTION_ID_PREFIX=${connectionIdPrefix})`);
186
+ if (poolBoundary)
187
+ parts.push(`(POOL_BOUNDARY=${poolBoundary})`);
185
188
  return `(CONNECT_DATA=${parts.join("")})`;
186
189
  }
187
190
 
@@ -211,17 +214,17 @@ class EZConnectResolver {
211
214
  let naddr = 0;
212
215
  // ; groups the user into a ADDRESS_LIST
213
216
  const addressLists = hostInfo.split(";");
214
- for (const addressList in addressLists) {
217
+ for (const addressList of addressLists) {
215
218
  let addressNodeCount = 0;
216
219
  const addressListBuilder = new Array();
217
- for (const match of addressLists[addressList].matchAll(HOSTNAMES_PATTERN)) {
220
+ for (const match of addressList.matchAll(HOSTNAMES_PATTERN)) {
218
221
  const hostnames = (match.groups.hostnames).split(',');
219
222
  let port = match.groups.port;
220
223
  if (port == null) {
221
224
  port = '1521'; //default port
222
225
  }
223
- for (const hname in hostnames) {
224
- addressListBuilder.push(this.getAddrStr(hostnames[hname], port, protocol, proxyInfo));
226
+ for (const hname of hostnames) {
227
+ addressListBuilder.push(this.getAddrStr(hname, port, protocol, proxyInfo));
225
228
  addressNodeCount++;
226
229
  }
227
230
  }
@@ -455,6 +458,7 @@ class EZConnectResolver {
455
458
  aliasMap.set("pool_purity", "POOL_PURITY");
456
459
  aliasMap.set("service_tag", "SERVICE_TAG");
457
460
  aliasMap.set("connection_id_prefix", "CONNECTION_ID_PREFIX");
461
+ aliasMap.set("pool_boundary", "POOL_BOUNDARY");
458
462
  return aliasMap;
459
463
  }
460
464
  }module.exports = EZConnectResolver;
@@ -486,7 +486,7 @@ class NavAddress extends Address {
486
486
  if (!net.isIP(this.host)) {
487
487
  try {
488
488
  addresses = await dnsPromises.lookup(this.host, options);
489
- for (const addr in addresses) {
489
+ for (const addr of addresses) {
490
490
  const co = new ConnOption();
491
491
  co.hostname = this.host;
492
492
  co.port = this.port;
@@ -499,7 +499,7 @@ class NavAddress extends Address {
499
499
  co.CNdata.push('(address=(protocol=' + this.prot + ')(host=' + this.host + ')(port=' + this.port + '))');
500
500
  else
501
501
  co.CNdata.push(this.toString());
502
- co.host = addresses[addr].address;
502
+ co.host = addr.address;
503
503
  co.addr = this.addr;
504
504
  cs.getcurrentDescription().addConnectOption(co);
505
505
  }
@@ -777,7 +777,8 @@ class NavDescription extends Description {
777
777
  if (this.connectData == null) {
778
778
  this.connectData = "(SERVICE_NAME=)";
779
779
  }
780
- const cid = `(CID=(PROGRAM=${cInfo.program})(HOST=${cInfo.hostName})(USER=${cInfo.userName}))`;
780
+ const pgmName = "\"'" + cInfo.program + "'\"";
781
+ const cid = `(CID=(PROGRAM=${pgmName})(HOST=${cInfo.hostName})(USER=${cInfo.userName}))`;
781
782
  cOpts[i].CNdata.push(NavSchemaObject.CD);
782
783
  cOpts[i].CNdata.push(this.connectData);
783
784
  cOpts[i].CNdata.push(cid);
@@ -59,7 +59,7 @@ async function getConnectionInfo(userConfig) {
59
59
  const sid = findValue(nvpair, ["DESCRIPTION", "CONNECT_DATA", "SID"]);
60
60
  const poolPurity = findValue(nvpair, ["DESCRIPTION", "CONNECT_DATA", "POOL_PURITY"]);
61
61
 
62
- return [serverVal, connClass, svcname, poolPurity, sid, addressNode ];
62
+ return [serverVal, connClass, svcname, poolPurity, sid, addressNode];
63
63
  }
64
64
 
65
65
  /**
@@ -83,6 +83,12 @@ async function resolveConnectStr(connectString, configDir) {
83
83
  resolvedVal = p.get(connStr.toUpperCase());
84
84
  if (!resolvedVal)
85
85
  errors.throwErr(errors.ERR_TNS_ENTRY_NOT_FOUND, connStr, configDir ? configDir + '/tnsnames.ora' : process.env.TNS_ADMIN + '/tnsnames.ora');
86
+ if (resolvedVal.rhsType == 1) {
87
+ const rString = resolvedVal.atom;
88
+ if ((rString.indexOf(':') != -1) || (rString.indexOf('/') != -1)) {
89
+ return new EzConnect(rString).getResolvedUrl();
90
+ }
91
+ }
86
92
  resolvedVal = resolvedVal.getListElement(0);
87
93
  }
88
94
 
@@ -354,8 +360,13 @@ class NetworkSession {
354
360
  break;
355
361
  case constants.NSPTAC: /* ACCEPT */
356
362
  Packet.AcceptPacket(packet, this.sAtts);
357
- this.dbUUID = packet.dbUUID;
358
- packet.dbUUID = null;
363
+ if (this.sAtts.version >= constants.TNS_VERSION_MIN_END_OF_RESPONSE
364
+ && (packet.flags & constants.TNS_ACCEPT_FLAG_HAS_END_OF_REQUEST)) {
365
+ this.endOfRequestSupport = true;
366
+ }
367
+ if (packet.flags & constants.TNS_ACCEPT_FLAG_FAST_AUTH) {
368
+ this.supportsFastAuth = true;
369
+ }
359
370
  break;
360
371
  case constants.NSPTRF: /* REFUSE */
361
372
  this.refusePkt = new Packet.RefusePacket(packet);
@@ -246,11 +246,8 @@ function AcceptPacket(packet, sAtts) {
246
246
  this.flag0 = packet.buf.readUInt8(constants.NSPACFL0);
247
247
  this.flag1 = packet.buf.readUInt8(constants.NSPACFL1);
248
248
 
249
- /* UUID - CDB Instance ID */
250
- if (sAtts.version >= constants.TNS_VERSION_MIN_UUID) {
251
- packet.dbUUID = packet.buf.subarray(constants.TNS_UUID_OFFSET,
252
- constants.TNS_UUID_OFFSET + 16);
253
- packet.dbUUID = packet.dbUUID.toString('base64');
249
+ if (sAtts.version >= constants.TNS_VERSION_MIN_DATA_FLAGS) {
250
+ packet.flags = packet.buf.readUInt32BE(constants.NSPACFL2);
254
251
  }
255
252
  }
256
253
 
@@ -28,6 +28,7 @@
28
28
 
29
29
  const { Buffer } = require('buffer');
30
30
  const constants = require('../constants');
31
+ const errors = require('../errors');
31
32
 
32
33
  /**
33
34
  * It is used to cache the metadata about bind information
@@ -359,6 +360,7 @@ class Statement {
359
360
  this.inUse = false;
360
361
  this.bufferRowIndex = 0;
361
362
  this.bufferRowCount = 0;
363
+ this.pendingClear = false;
362
364
  this.statementType = constants.STMT_TYPE_UNKNOWN;
363
365
  }
364
366
 
@@ -495,6 +497,12 @@ class Statement {
495
497
  const info = new BindInfo(name, this.isReturning);
496
498
  this.bindInfoList.push(info);
497
499
  if (this.bindInfoDict.has(info.bindName)) {
500
+ if (this.isReturning) {
501
+ const origInfo = this.bindInfoDict.get(info.bindName)[0];
502
+ if (!origInfo.isReturnBind) {
503
+ errors.throwErr(errors.ERR_DML_RETURNING_DUP_BINDS, name);
504
+ }
505
+ }
498
506
  this.bindInfoDict.get(info.bindName).push(info);
499
507
  } else {
500
508
  this.bindInfoDict.set(info.bindName, [info]);
@@ -528,6 +536,34 @@ class Statement {
528
536
 
529
537
  bindInfo.bindVar = variable;
530
538
  }
539
+
540
+ //---------------------------------------------------------------------------
541
+ // _clearAllState
542
+ //
543
+ // clear all state associated with the cursor
544
+ //---------------------------------------------------------------------------
545
+ _clearAllState() {
546
+ this.cursorId = 0;
547
+ this.requiresDefine = false;
548
+ this.noPrefetch = false;
549
+ this.requiresFullExecute = false;
550
+ this.queryVars = [];
551
+ this.numQueryVars = 0;
552
+ this.bufferRowCount = 0;
553
+ this.bufferRowIndex = 0;
554
+ }
555
+
556
+ //---------------------------------------------------------------------------
557
+ // _clearState
558
+ //
559
+ // clear some state associated with the cursor
560
+ //---------------------------------------------------------------------------
561
+ _clearState() {
562
+ this.cursorId = 0;
563
+ this.requiresDefine = false;
564
+ this.noPrefetch = false;
565
+ this.requiresFullExecute = false;
566
+ }
531
567
  }
532
568
 
533
569
  module.exports.Statement = Statement;
@@ -71,20 +71,37 @@ class StatementCache {
71
71
  }
72
72
 
73
73
  //---------------------------------------------------------------------------
74
- //clearOpenCursors() {
74
+ //clearCursors() {
75
75
  // Clears the list of open cursors and removes the list of cursors that
76
76
  // need to be closed. This is required when a DRCP session change has
77
77
  // taken place as the cursor ID values are invalidated.
78
78
  //---------------------------------------------------------------------------
79
- clearOpenCursors() {
79
+ clearCursors() {
80
80
  const newOpenCursors = new Set();
81
81
  for (const stmt of this._openCursors) {
82
- stmt.cursorId = 0;
83
- if (stmt.inUse) {
82
+ if (stmt.inUse || stmt.returnToCache) {
83
+ stmt.pendingClear = true;
84
84
  newOpenCursors.add(stmt);
85
85
  }
86
+ stmt._clearState();
86
87
  }
87
88
  this._openCursors = newOpenCursors;
89
+ this._cursorsToClose.clear();
90
+ }
91
+
92
+ //---------------------------------------------------------------------------
93
+ //clearPendingState() {
94
+ // Clears state for statment with pending clear flag set and not in use.
95
+ // This will clear all state for open cursors.
96
+ // Called after rows processing is completed.
97
+ //---------------------------------------------------------------------------
98
+ clearPendingStatus() {
99
+ for (const stmt of this._openCursors) {
100
+ if (stmt.pendingClear && !stmt.inUse) {
101
+ stmt._clearAllState();
102
+ stmt.pendingClear = false;
103
+ }
104
+ }
88
105
  }
89
106
 
90
107
  //---------------------------------------------------------------------------
@@ -94,14 +111,10 @@ class StatementCache {
94
111
  // supposed to be cached, a copy will be made (and not returned to the
95
112
  // cache).
96
113
  //---------------------------------------------------------------------------
97
- getStatement(sql, cacheStatement = false) {
114
+ getStatement(sql, cacheStatement = false, forceNew = false) {
98
115
  let stmt = null;
99
116
  if (sql) {
100
117
  stmt = this._cachedStatements.get(sql);
101
- if (!cacheStatement) {
102
- this._openCursors.add(stmt);
103
- this._cachedStatements.delete(sql);
104
- }
105
118
  }
106
119
  if (!stmt) {
107
120
  stmt = new Statement();
@@ -114,13 +127,21 @@ class StatementCache {
114
127
  this._adjustCache();
115
128
  }
116
129
  this._openCursors.add(stmt);
117
- } else if (stmt.inUse || !cacheStatement) {
130
+ } else if (forceNew || stmt.inUse) {
131
+ if (!cacheStatement) {
132
+ this._addCursorToClose(stmt);
133
+ this._cachedStatements.delete(sql);
134
+ }
118
135
  stmt = stmt._copy();
119
136
  this._openCursors.add(stmt);
137
+ } else if (!cacheStatement) {
138
+ this._cachedStatements.delete(sql);
139
+ stmt.returnToCache = false;
120
140
  } else {
121
141
  this._cachedStatements.delete(sql);
122
142
  this._cachedStatements.set(sql, stmt);
123
143
  }
144
+ this._openCursors.add(stmt);
124
145
  stmt.inUse = true;
125
146
  return stmt;
126
147
  }
@@ -146,9 +167,11 @@ class StatementCache {
146
167
  }
147
168
  if (statement.returnToCache) {
148
169
  statement.inUse = false;
149
- } else if (statement.cursorId !== 0) {
170
+ } else {
150
171
  this._addCursorToClose(statement);
151
172
  }
173
+ // clear all state for statement which is having flag set and not in use
174
+ this.clearPendingStatus();
152
175
  }
153
176
 
154
177
  //---------------------------------------------------------------------------
package/lib/thin/util.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
  //
@@ -254,5 +254,5 @@ module.exports = {
254
254
  getOutBinds,
255
255
  checkProxyUserValidity,
256
256
  checkCredentials,
257
- normalizePrivateKey
257
+ normalizePrivateKey,
258
258
  };
@@ -89,7 +89,7 @@ function transformJsonValue(value) {
89
89
  if (value === undefined || value === null ||
90
90
  typeof value === 'number' || typeof value === 'string' ||
91
91
  typeof value === 'boolean' || Buffer.isBuffer(value) ||
92
- util.isDate(value) || nodbUtil.isVectorValue(value))
92
+ util.types.isDate(value) || nodbUtil.isVectorValue(value))
93
93
  return value;
94
94
 
95
95
  // arrays are transformed to a new array with processed values
@@ -105,6 +105,12 @@ function transformJsonValue(value) {
105
105
  if (value instanceof BaseDbObject)
106
106
  return {fields: [], values: []};
107
107
 
108
+ // JsonId is a special type to represent autogenerated id
109
+ // for SODA documents.
110
+ if (value instanceof types.JsonId) {
111
+ return value;
112
+ }
113
+
108
114
  // all other objects are transformed to an object with two arrays (fields
109
115
  // and values)
110
116
  const outValue = {};
@@ -166,7 +172,7 @@ function transformValueIn(info, value, options) {
166
172
  return value;
167
173
 
168
174
  // handle numbers
169
- } else if (typeof value === 'number') {
175
+ } else if (typeof value === 'number' || typeof value === 'bigint') {
170
176
  checkType(info, options,
171
177
  types.DB_TYPE_NUMBER,
172
178
  types.DB_TYPE_BINARY_INTEGER,
@@ -183,7 +189,7 @@ function transformValueIn(info, value, options) {
183
189
  return value;
184
190
 
185
191
  // handle dates
186
- } else if (util.isDate(value)) {
192
+ } else if (util.types.isDate(value)) {
187
193
  checkType(info, options,
188
194
  types.DB_TYPE_TIMESTAMP,
189
195
  types.DB_TYPE_TIMESTAMP_TZ,
package/lib/types.js CHANGED
@@ -332,6 +332,13 @@ dbTypeByColumnTypeName.set("SMALLINT", DB_TYPE_NUMBER);
332
332
  dbTypeByColumnTypeName.set("TIMESTAMP WITH LOCAL TZ", DB_TYPE_TIMESTAMP_LTZ);
333
333
  dbTypeByColumnTypeName.set("TIMESTAMP WITH TZ", DB_TYPE_TIMESTAMP_TZ);
334
334
 
335
+ // It abstracts the autogenerated SODA Document key.
336
+ class JsonId extends Uint8Array {
337
+ toJSON() {
338
+ return (Buffer.from(this.buffer).toString('hex'));
339
+ }
340
+ }
341
+
335
342
  module.exports = {
336
343
  DbType,
337
344
  DB_TYPE_BFILE,
@@ -368,5 +375,6 @@ module.exports = {
368
375
  DB_TYPE_XMLTYPE,
369
376
  getTypeByColumnTypeName,
370
377
  getTypeByNum,
371
- getTypeByOraTypeNum
378
+ getTypeByOraTypeNum,
379
+ JsonId
372
380
  };
package/lib/util.js CHANGED
@@ -231,7 +231,7 @@ function isShardingKey(value) {
231
231
  const element = value[i];
232
232
  const ok = typeof element === 'string' ||
233
233
  typeof element === 'number' || Buffer.isBuffer(element) ||
234
- util.isDate(element);
234
+ util.types.isDate(element);
235
235
  if (!ok)
236
236
  return false;
237
237
  }
package/lib/version.js CHANGED
@@ -30,7 +30,7 @@
30
30
 
31
31
  module.exports = {
32
32
  VERSION_MAJOR: 6,
33
- VERSION_MINOR: 4,
33
+ VERSION_MINOR: 5,
34
34
  VERSION_PATCH: 0,
35
35
  VERSION_SUFFIX: ''
36
36
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oracledb",
3
- "version": "6.4.0",
3
+ "version": "6.5.0",
4
4
  "description": "A Node.js module for Oracle Database access from JavaScript and TypeScript",
5
5
  "license": "(Apache-2.0 OR UPL-1.0)",
6
6
  "homepage": "http://oracle.github.io/node-oracledb/",
@@ -1 +0,0 @@
1
- oracledb-6.4.0-darwin-x64.node v18.17.0 df971fa2503fcf9ab4a63f2f2508f5fb5780c0e1 31cd3b6682f32ad860df63d1dd06e3bf1c5e62aa Thu, 29 Feb 2024 13:53:47 GMT
@@ -1 +0,0 @@
1
- df971fa2503fcf9ab4a63f2f2508f5fb5780c0e1
@@ -1 +0,0 @@
1
- oracledb-6.4.0-linux-arm64.node v18.19.0 df971fa2503fcf9ab4a63f2f2508f5fb5780c0e1 31cd3b6682f32ad860df63d1dd06e3bf1c5e62aa Thu, 29 Feb 2024 15:01:23 GMT