oracledb 7.0.0 → 26.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 (84) hide show
  1. package/README.md +16 -6
  2. package/build/Release/oracledb-26.0.0-darwin-arm64.node +0 -0
  3. package/build/Release/oracledb-26.0.0-darwin-arm64.node-buildinfo.txt +1 -0
  4. package/build/Release/oracledb-26.0.0-js-buildinfo.txt +1 -0
  5. package/build/Release/oracledb-26.0.0-linux-arm64.node +0 -0
  6. package/build/Release/oracledb-26.0.0-linux-arm64.node-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-26.0.0-linux-x64.node +0 -0
  8. package/build/Release/oracledb-26.0.0-linux-x64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-26.0.0-win32-x64.node +0 -0
  10. package/build/Release/oracledb-26.0.0-win32-x64.node-buildinfo.txt +1 -0
  11. package/lib/aqDeqOptions.js +4 -4
  12. package/lib/aqEnqOptions.js +2 -2
  13. package/lib/aqQueue.js +11 -6
  14. package/lib/connection.js +73 -18
  15. package/lib/constants.js +4 -0
  16. package/lib/endUserSecurityContext.js +30 -25
  17. package/lib/errors.js +92 -5
  18. package/lib/impl/connection.js +36 -0
  19. package/lib/impl/datahandlers/buffer.js +91 -18
  20. package/lib/impl/datahandlers/oson.js +3 -2
  21. package/lib/impl/parserHelpers.js +6 -2
  22. package/lib/impl/resultset.js +32 -11
  23. package/lib/oracledb.js +34 -0
  24. package/lib/pool.js +6 -2
  25. package/lib/queryStream.js +13 -5
  26. package/lib/resultset.js +10 -3
  27. package/lib/securityContextProvider.js +206 -0
  28. package/lib/thin/aq.js +2 -3
  29. package/lib/thin/connection.js +380 -24
  30. package/lib/thin/dbObject.js +20 -11
  31. package/lib/thin/pool.js +4 -3
  32. package/lib/thin/protocol/capabilities.js +37 -4
  33. package/lib/thin/protocol/constants.js +49 -1
  34. package/lib/thin/protocol/messages/aqArray.js +2 -2
  35. package/lib/thin/protocol/messages/aqBase.js +2 -2
  36. package/lib/thin/protocol/messages/auth.js +17 -6
  37. package/lib/thin/protocol/messages/base.js +75 -17
  38. package/lib/thin/protocol/messages/dataType.js +46 -0
  39. package/lib/thin/protocol/messages/directPathLoadStream.js +3 -2
  40. package/lib/thin/protocol/messages/execute.js +10 -4
  41. package/lib/thin/protocol/messages/index.js +4 -0
  42. package/lib/thin/protocol/messages/notification.js +281 -0
  43. package/lib/thin/protocol/messages/subscribe.js +181 -0
  44. package/lib/thin/protocol/messages/withData.js +23 -10
  45. package/lib/thin/protocol/packet.js +2 -4
  46. package/lib/thin/protocol/protocol.js +7 -4
  47. package/lib/thin/resultSet.js +40 -20
  48. package/lib/thin/sqlnet/ezConnectResolver.js +9 -2
  49. package/lib/thin/sqlnet/navNodes.js +7 -5
  50. package/lib/thin/sqlnet/networkSession.js +17 -11
  51. package/lib/thin/sqlnet/ntTcp.js +24 -8
  52. package/lib/thin/sqlnet/packet.js +24 -4
  53. package/lib/thin/sqlnet/sessionAtts.js +2 -2
  54. package/lib/thin/statement.js +6 -1
  55. package/lib/thin/statementCache.js +34 -6
  56. package/lib/thin/subscr.js +363 -0
  57. package/lib/transformer.js +60 -17
  58. package/lib/util.js +46 -8
  59. package/lib/version.js +1 -1
  60. package/package.json +1 -1
  61. package/plugins/configProviders/gcpstorage/index.js +121 -0
  62. package/plugins/configProviders/ociobject/index.js +1 -1
  63. package/plugins/token/cache.js +154 -0
  64. package/plugins/token/endUserSecurityProvider/index.js +377 -0
  65. package/plugins/token/extensionAzure/index.js +160 -28
  66. package/plugins/token/extensionOci/index.js +221 -16
  67. package/plugins/vectorsdk/OracleVecDB.js +346 -0
  68. package/plugins/vectorsdk/index.js +49 -0
  69. package/plugins/vectorsdk/utils.js +370 -0
  70. package/plugins/vectorsdk/vectorEmbedding.js +223 -0
  71. package/plugins/vectorsdk/vectorIndex.js +177 -0
  72. package/plugins/vectorsdk/vectorRecords.js +406 -0
  73. package/plugins/vectorsdk/vectorSchema.js +408 -0
  74. package/plugins/vectorsdk/vectorSearch.js +538 -0
  75. package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
  76. package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +0 -1
  77. package/build/Release/oracledb-7.0.0-js-buildinfo.txt +0 -1
  78. package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
  79. package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +0 -1
  80. package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
  81. package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +0 -1
  82. package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
  83. package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +0 -1
  84. package/eslint.config.js +0 -39
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -290,12 +290,15 @@ class EZConnectResolver {
290
290
  // by default for TCPS protocol.
291
291
  const serverDNMatch = this.urlProps.get("SSL_SERVER_DN_MATCH");
292
292
  const serverCertDN = this.urlProps.get("SSL_SERVER_CERT_DN");
293
+ const allowWeakDNMatch = this.urlProps.get("SSL_ALLOW_WEAK_DN_MATCH");
293
294
  const walletDir = this.urlProps.get("MY_WALLET_DIRECTORY");
294
295
  if (serverDNMatch != null)
295
296
  securityInfo.push(`(SSL_SERVER_DN_MATCH=${serverDNMatch})`);
296
297
  if (serverCertDN != null)
297
298
  securityInfo.push(`(SSL_SERVER_CERT_DN=${serverCertDN})`);
298
- if (walletDir != null)
299
+ if (allowWeakDNMatch != null)
300
+ securityInfo.push(`(SSL_ALLOW_WEAK_DN_MATCH=${allowWeakDNMatch})`);
301
+ if (walletDir != null && walletDir.toUpperCase() !== 'SYSTEM')
299
302
  securityInfo.push(`(MY_WALLET_DIRECTORY=${walletDir})`);
300
303
  }
301
304
  if (securityInfo.length === 0)
@@ -454,6 +457,10 @@ class EZConnectResolver {
454
457
  aliasMap.set("recv_timeout", "RECV_TIMEOUT");
455
458
  aliasMap.set("ssl_server_cert_dn", "SSL_SERVER_CERT_DN");
456
459
  aliasMap.set("ssl_server_dn_match", "SSL_SERVER_DN_MATCH");
460
+ aliasMap.set("tls_server_cert_dn", "SSL_SERVER_CERT_DN");
461
+ aliasMap.set("tls_server_dn_match", "SSL_SERVER_DN_MATCH");
462
+ aliasMap.set("ssl_allow_weak_dn_match", "SSL_ALLOW_WEAK_DN_MATCH");
463
+ aliasMap.set("tls_allow_weak_dn_match", "SSL_ALLOW_WEAK_DN_MATCH");
457
464
  aliasMap.set("wallet_location", "MY_WALLET_DIRECTORY");
458
465
  aliasMap.set("pool_connection_class", "POOL_CONNECTION_CLASS");
459
466
  aliasMap.set("pool_purity", "POOL_PURITY");
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -315,18 +315,20 @@ class Description {
315
315
  let tmpnv;
316
316
  for (let i = 0; i < listsize; i++) {
317
317
  tmpnv = childnv.getListElement(i);
318
- if (tmpnv.name.toUpperCase() == "SSL_SERVER_CERT_DN") {
318
+ if (tmpnv.name.toUpperCase() == "SSL_SERVER_CERT_DN" || tmpnv.name.toUpperCase() == "TLS_SERVER_CERT_DN") {
319
319
  this.params.sslServerCertDN = tmpnv.valueToString();
320
- } else if (tmpnv.name.toUpperCase() == "SSL_SERVER_DN_MATCH") {
320
+ } else if (tmpnv.name.toUpperCase() == "SSL_SERVER_DN_MATCH" || tmpnv.name.toUpperCase() == "TLS_SERVER_DN_MATCH") {
321
321
  this.params.sslServerDNMatch = (tmpnv.atom.toLowerCase() == "yes"
322
322
  || tmpnv.atom.toLowerCase() == "on"
323
323
  || tmpnv.atom.toLowerCase() == "true");
324
- } else if (tmpnv.name.toUpperCase() == "SSL_ALLOW_WEAK_DN_MATCH") {
324
+ } else if (tmpnv.name.toUpperCase() == "SSL_ALLOW_WEAK_DN_MATCH" || tmpnv.name.toUpperCase() == "TLS_ALLOW_WEAK_DN_MATCH") {
325
325
  this.params.sslAllowWeakDNMatch = (tmpnv.atom.toLowerCase() == "yes"
326
326
  || tmpnv.atom.toLowerCase() == "on"
327
327
  || tmpnv.atom.toLowerCase() == "true");
328
328
  } else if ((tmpnv.name.toUpperCase() == "WALLET_LOCATION") || (tmpnv.name.toUpperCase() == "MY_WALLET_DIRECTORY")) {
329
- this.params.walletLocation = tmpnv.atom;
329
+ if (!(typeof tmpnv.atom === 'string' && tmpnv.atom.trim().toUpperCase() === 'SYSTEM')) {
330
+ this.params.walletLocation = tmpnv.atom;
331
+ }
330
332
  }
331
333
  }
332
334
  }
@@ -157,14 +157,23 @@ class NetworkSession {
157
157
  * Make the transport level connection
158
158
  */
159
159
  async transportConnect(address) {
160
- if (address.protocol.toUpperCase() == 'TCP' && address.httpsProxy) {
161
- errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_PARAMETERS, 'https proxy requires protocol as', 'tcps ');
160
+ const protocol = address?.protocol ? address.protocol.toUpperCase() : undefined;
161
+ const effectiveHttpsProxy = address?.httpsProxy || this.sAtts?.nt?.httpsProxy;
162
+
163
+ // Enforce TCPS whenever an HTTPS proxy would be used (either at the address
164
+ // level or via the session-global proxy fallback in NTTCP).
165
+ if (protocol === 'TCP' && effectiveHttpsProxy) {
166
+ errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_PARAMETERS,
167
+ 'https proxy requires protocol as', 'tcps ');
162
168
  }
163
- if (address.protocol && (address.protocol.toUpperCase() == 'TCP' || address.protocol.toUpperCase() == 'TCPS')) {
169
+
170
+ if (protocol === 'TCP' || protocol === 'TCPS') {
164
171
  this.ntAdapter = new NTTCP(this.sAtts.nt);
165
172
  } else {
166
- errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_PARAMETERS, address.protocol + " protocol not supported");
173
+ errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_PARAMETERS,
174
+ address.protocol + " protocol not supported");
167
175
  }
176
+
168
177
  await this.ntAdapter.connect(address);
169
178
  this.ntAdapter.startRead();
170
179
  this.sAtts.ntCha = this.ntAdapter.cha;
@@ -390,7 +399,7 @@ class NetworkSession {
390
399
  } catch (err) {
391
400
  if (err.code == 'ERR_BUFFER_TOO_LARGE') {
392
401
  errors.throwErr(errors.ERR_INVALID_PACKET,
393
- `: decompressed packet exceeds SDU ${maxPayload}`);
402
+ `Decompressed packet exceeds SDU ${maxPayload}`);
394
403
  }
395
404
  errors.throwErr(errors.ERR_DATA_COMPRESSION, err.message);
396
405
  }
@@ -580,7 +589,7 @@ class NetworkSession {
580
589
  if (this.isBreak) {
581
590
  return;
582
591
  }
583
- let bytesCopied = 0;
592
+ let bytesCopied;
584
593
 
585
594
  this.sndDatapkt.dataLen = this.sndDatapkt.offset;
586
595
  if (this.sndDatapkt.dataLen < this.sndDatapkt.bufLen || !this.sndDatapkt.bufLen) {
@@ -724,10 +733,8 @@ class NetworkSession {
724
733
  * returns 0 if healthy connection
725
734
  */
726
735
  recvInbandNotif() {
727
- let error = 0;
728
736
  if (this.controlPkt.errno) { /* Control pkt already read */
729
- error = this.controlPkt.errno;
730
- return (error);
737
+ return this.controlPkt.errno;
731
738
  } else if (!this.getOption(constants.HEALTHCHECK)) {
732
739
  return errors.ERR_CONNECTION_CLOSED;
733
740
  } else {
@@ -736,8 +743,7 @@ class NetworkSession {
736
743
 
737
744
  if (packet.type == constants.NSPTCNL) {
738
745
  this.controlPkt.fromPacket(packet);
739
- error = this.controlPkt.errno;
740
- return (error);
746
+ return this.controlPkt.errno;
741
747
  } else {
742
748
  this.ntAdapter.packets.unshift(packet); /* Push packet back */
743
749
  return (0);
@@ -74,6 +74,7 @@ class NTTCP {
74
74
  this.largeSDU = false;
75
75
  this.streamNum = streamNum++;
76
76
  this.packetNum = 1;
77
+ this.proxyReq = null; // track in-flight proxy CONNECT request for abort on timeout
77
78
  this.doDNMatch = true;
78
79
  }
79
80
 
@@ -157,7 +158,7 @@ class NTTCP {
157
158
  address.port = DEFAULT_PORT;
158
159
  }
159
160
 
160
- let connectErrCause, proxyConnectErrCause, req;
161
+ let connectErrCause, proxyConnectErrCause;
161
162
  const httpsProxy = address.httpsProxy || this.atts.httpsProxy;
162
163
  let httpsProxyPort = address.httpsProxyPort || this.atts.httpsProxyPort;
163
164
 
@@ -166,13 +167,13 @@ class NTTCP {
166
167
  if (!httpsProxyPort) {
167
168
  httpsProxyPort = DEFAULT_HTTPS_PROXY_PORT;
168
169
  }
169
- req = http.request({
170
+ this.proxyReq = http.request({
170
171
  host: httpsProxy,
171
172
  port: httpsProxyPort,
172
173
  method: 'CONNECT',
173
174
  path: address.host + ':' + address.port,
174
175
  });
175
- req.once('connect', (res, socket) => {
176
+ this.proxyReq.once('connect', (res, socket) => {
176
177
  if (res.statusCode == 200) {
177
178
  this.connected = true;
178
179
  this.stream = socket;
@@ -181,11 +182,11 @@ class NTTCP {
181
182
  }
182
183
  resolve();
183
184
  });
184
- req.once('error', (err) => {
185
+ this.proxyReq.once('error', (err) => {
185
186
  proxyConnectErrCause = err.message;
186
187
  resolve();
187
188
  });
188
- req.end();
189
+ this.proxyReq.end();
189
190
  } else {
190
191
  this.stream = net.connect(address.port, address.host, () => {
191
192
  this.connected = true;
@@ -197,8 +198,10 @@ class NTTCP {
197
198
  });
198
199
  }
199
200
  });
200
- if (req)
201
- req.removeAllListeners();
201
+ if (this.proxyReq) {
202
+ this.proxyReq.removeAllListeners();
203
+ this.proxyReq = null;
204
+ }
202
205
  if (!this.connected) {
203
206
  if (proxyConnectErrCause) {
204
207
  errors.throwErr(errors.ERR_PROXY_CONNECTION_FAILURE, httpsProxy, httpsProxyPort, this.atts.connectionId, proxyConnectErrCause);
@@ -260,14 +263,27 @@ class NTTCP {
260
263
  * @returns Promise
261
264
  */
262
265
  disconnect(type) { /* Disconnect function for TCP sockets */
263
- if (this.connected && !this.err) {
266
+ // Always tear down the socket if present, even if not yet connected
267
+ if (this.stream) {
264
268
  if (type == constants.NSFIMM)
265
269
  this.stream.destroy();
266
270
  else
267
271
  this.stream.end();
268
272
  }
273
+
274
+ // Tear down any in-flight proxy CONNECT request after its socket. Keep an
275
+ // error listener for a socket error already pending when it is destroyed.
276
+ if (this.proxyReq) {
277
+ this.proxyReq.removeAllListeners();
278
+ this.proxyReq.once('error', () => {});
279
+ this.proxyReq.destroy();
280
+ this.proxyReq = null;
281
+ }
282
+
269
283
  this.stream = null;
270
284
  this.connected = false;
285
+ this.err = this.err || (type == constants.NSFIMM);
286
+ this._notifyWaiters();
271
287
  this.drainWaiter = null;
272
288
  this.readWaiter = null;
273
289
  }
@@ -32,6 +32,20 @@ const errors = require("../../errors.js");
32
32
  const MAX_CDATA_LEN = 230;
33
33
  const NSPCNL = 74;
34
34
 
35
+
36
+ function validateAcceptDataUnits(sdu, tdu, largeSDU) {
37
+ const maxSdu = largeSDU ? constants.NSPABSSDULN : constants.NSPMXSDULN;
38
+ if (sdu < constants.NSPMNSDULN || sdu > maxSdu) {
39
+ const reason = `Negotiated SDU ${sdu} is outside the valid range ` +
40
+ `${constants.NSPMNSDULN} to ${maxSdu}`;
41
+ errors.throwErr(errors.ERR_INVALID_PACKET, reason);
42
+ }
43
+ if (tdu < constants.NSPMNTDULN || tdu > constants.NSPMXTDULN) {
44
+ const reason = `Negotiated TDU ${tdu} is outside the valid range ` +
45
+ `${constants.NSPMNTDULN} to ${constants.NSPMXTDULN}`;
46
+ errors.throwErr(errors.ERR_INVALID_PACKET, reason);
47
+ }
48
+ }
35
49
  /**
36
50
  * Connect Packet (NSPTCN)
37
51
  * @param {Buffer} connectData Outgoing Connect Data
@@ -246,13 +260,16 @@ function AcceptPacket(packet, sAtts) {
246
260
  /* Set negotiated values */
247
261
  sAtts.version = packet.buf.readUInt16BE(constants.NSPACVSN);
248
262
  sAtts.options = packet.buf.readUInt16BE(constants.NSPACOPT);
249
- sAtts.sdu = packet.buf.readUInt16BE(constants.NSPACSDU);
250
- sAtts.tdu = packet.buf.readUInt16BE(constants.NSPACTDU);
263
+
264
+ let negotiatedSDU = packet.buf.readUInt16BE(constants.NSPACSDU);
265
+ let negotiatedTDU = packet.buf.readUInt16BE(constants.NSPACTDU);
266
+
267
+ sAtts.largeSDU = false;
251
268
 
252
269
  if (sAtts.version >= 315) {
253
270
  /* Large SDU Support */
254
- sAtts.sdu = packet.buf.readUInt32BE(constants.NSPACLSD);
255
- sAtts.tdu = packet.buf.readUInt32BE(constants.NSPACLTD);
271
+ negotiatedSDU = packet.buf.readUInt32BE(constants.NSPACLSD);
272
+ negotiatedTDU = packet.buf.readUInt32BE(constants.NSPACLTD);
256
273
  sAtts.largeSDU = true;
257
274
  this.cflag = packet.buf.readUInt8(constants.NSPACCFL); // 00001000
258
275
  if ((this.cflag & constants.NSPACCFON) != 0) { // compression ON
@@ -264,6 +281,9 @@ function AcceptPacket(packet, sAtts) {
264
281
  sAtts.networkCompressionEnabled = false;
265
282
  }
266
283
  }
284
+ validateAcceptDataUnits(negotiatedSDU, negotiatedTDU, sAtts.largeSDU);
285
+ sAtts.sdu = negotiatedSDU;
286
+ sAtts.tdu = negotiatedTDU;
267
287
 
268
288
  /* Accept flags */
269
289
  this.flag0 = packet.buf.readUInt8(constants.NSPACFL0);
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -88,7 +88,7 @@ class SessionAtts {
88
88
  this.networkCompressionThreshold = parseInt(params.networkCompressionThreshold);
89
89
  }
90
90
 
91
- if (typeof params.walletLocation === 'string') {
91
+ if (typeof params.walletLocation === 'string' && params.walletLocation.trim().toUpperCase() !== 'SYSTEM') {
92
92
  this.nt.walletFile = path.join(params.walletLocation, constants.PEM_WALLET_FILE_NAME);
93
93
  }
94
94
  if (typeof params.walletPassword === 'string') {
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
1
+ // Copyright (c) 2022, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -363,6 +363,9 @@ class Statement {
363
363
  this.bufferRowCount = 0;
364
364
  this.pendingClear = false;
365
365
  this.statementType = constants.STMT_TYPE_UNKNOWN;
366
+
367
+ // CQN registration id attached during query registration execute.
368
+ this._registrationId = 0n;
366
369
  }
367
370
 
368
371
  //---------------------------------------------------------------------------
@@ -394,6 +397,7 @@ class Statement {
394
397
  }
395
398
  }
396
399
  copiedStatement.returnToCache = false;
400
+ copiedStatement._registrationId = this._registrationId;
397
401
  return copiedStatement;
398
402
  }
399
403
 
@@ -555,6 +559,7 @@ class Statement {
555
559
  this.numQueryVars = 0;
556
560
  this.bufferRowCount = 0;
557
561
  this.bufferRowIndex = 0;
562
+ this._registrationId = 0n;
558
563
  }
559
564
 
560
565
  //---------------------------------------------------------------------------
@@ -32,10 +32,34 @@ class StatementCache {
32
32
  constructor(maxSize) {
33
33
  this._cachedStatements = new Map();
34
34
  this._maxSize = maxSize;
35
+ this._cursorsToCancel = new Set();
35
36
  this._cursorsToClose = new Set();
36
37
  this._openCursors = new Set();
37
38
  }
38
39
 
40
+ //---------------------------------------------------------------------------
41
+ // _addCursorToCancel()
42
+ //
43
+ // Add the statement's cursor to the list of cursors that need to be
44
+ // cancelled.
45
+ //---------------------------------------------------------------------------
46
+ _addCursorToCancel(statement) {
47
+ const cursorId = statement.cursorId;
48
+
49
+ if (cursorId <= 0 || !statement.isQuery || !statement.moreRowsToFetch) {
50
+ return;
51
+ }
52
+
53
+ if (this._cursorsToClose.has(cursorId)) {
54
+ return;
55
+ }
56
+
57
+ this._cursorsToCancel.add(cursorId);
58
+ // Keep the cached statement tracked in _openCursors so its cursor state can
59
+ // be invalidated if the underlying DRCP session changes.
60
+ statement.moreRowsToFetch = false;
61
+ }
62
+
39
63
  //---------------------------------------------------------------------------
40
64
  // _addCursorToClose()
41
65
  //
@@ -49,6 +73,8 @@ class StatementCache {
49
73
  if (statement.cursorId != 0) {
50
74
  this._cursorsToClose.add(statement.cursorId);
51
75
  }
76
+ // Closing the cursor supersedes a pending cancellation.
77
+ this._cursorsToCancel.delete(statement.cursorId);
52
78
  this._openCursors.delete(statement);
53
79
  }
54
80
 
@@ -86,6 +112,7 @@ class StatementCache {
86
112
  stmt._clearState();
87
113
  }
88
114
  this._openCursors = newOpenCursors;
115
+ this._cursorsToCancel.clear();
89
116
  this._cursorsToClose.clear();
90
117
  }
91
118
 
@@ -169,6 +196,7 @@ class StatementCache {
169
196
  });
170
197
  }
171
198
  if (statement.returnToCache) {
199
+ this._addCursorToCancel(statement);
172
200
  statement.inUse = false;
173
201
  } else {
174
202
  this._addCursorToClose(statement);
@@ -178,15 +206,15 @@ class StatementCache {
178
206
  }
179
207
 
180
208
  //---------------------------------------------------------------------------
181
- // writeCursorsToClose()
182
- // Write the list of cursors to close to the buffer.
209
+ // writeCursors()
210
+ // Write the specified list of cursors to the buffer and clear the list.
183
211
  //---------------------------------------------------------------------------
184
- writeCursorsToClose(buf) {
185
- buf.writeUB4(this._cursorsToClose.size);
186
- for (const cursorNum of this._cursorsToClose.keys()) {
212
+ writeCursors(buf, cursors) {
213
+ buf.writeUB4(cursors.size);
214
+ for (const cursorNum of cursors) {
187
215
  buf.writeUB4(cursorNum);
188
216
  }
189
- this._cursorsToClose.clear();
217
+ cursors.clear();
190
218
  }
191
219
  }
192
220