oracledb 6.0.2 → 6.1.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 (67) hide show
  1. package/README.md +15 -17
  2. package/SECURITY.md +1 -1
  3. package/build/Release/oracledb-6.1.0-darwin-x64.node +0 -0
  4. package/build/Release/oracledb-6.1.0-darwin-x64.node-buildinfo.txt +1 -0
  5. package/build/Release/oracledb-6.1.0-js-buildinfo.txt +1 -0
  6. package/build/Release/oracledb-6.1.0-linux-arm64.node +0 -0
  7. package/build/Release/oracledb-6.1.0-linux-arm64.node-buildinfo.txt +1 -0
  8. package/build/Release/oracledb-6.1.0-linux-x64.node +0 -0
  9. package/build/Release/oracledb-6.1.0-linux-x64.node-buildinfo.txt +1 -0
  10. package/build/Release/oracledb-6.1.0-win32-x64.node +0 -0
  11. package/build/Release/oracledb-6.1.0-win32-x64.node-buildinfo.txt +1 -0
  12. package/examples/example.js +0 -1
  13. package/lib/aqQueue.js +40 -26
  14. package/lib/connection.js +15 -3
  15. package/lib/errors.js +15 -10
  16. package/lib/impl/connection.js +9 -0
  17. package/lib/impl/resultset.js +7 -4
  18. package/lib/oracledb.js +15 -12
  19. package/lib/pool.js +11 -2
  20. package/lib/settings.js +1 -1
  21. package/lib/sodaCollection.js +1 -1
  22. package/lib/thin/connection.js +92 -30
  23. package/lib/thin/dbObject.js +2 -2
  24. package/lib/thin/lob.js +2 -2
  25. package/lib/thin/pool.js +153 -71
  26. package/lib/thin/protocol/buffer.js +24 -6
  27. package/lib/thin/protocol/constants.js +8 -18
  28. package/lib/thin/protocol/encryptDecrypt.js +17 -17
  29. package/lib/thin/protocol/messages/auth.js +27 -31
  30. package/lib/thin/protocol/messages/base.js +30 -53
  31. package/lib/thin/protocol/messages/connectionCookie.js +76 -0
  32. package/lib/thin/protocol/messages/dataType.js +13 -9
  33. package/lib/thin/protocol/messages/execute.js +11 -11
  34. package/lib/thin/protocol/messages/index.js +2 -0
  35. package/lib/thin/protocol/messages/lobOp.js +3 -3
  36. package/lib/thin/protocol/messages/protocol.js +31 -20
  37. package/lib/thin/protocol/messages/withData.js +60 -53
  38. package/lib/thin/protocol/oson.js +4 -4
  39. package/lib/thin/protocol/packet.js +51 -28
  40. package/lib/thin/protocol/protocol.js +12 -7
  41. package/lib/thin/protocol/utils.js +2 -2
  42. package/lib/thin/resultSet.js +6 -6
  43. package/lib/thin/sqlnet/connStrategy.js +134 -7
  44. package/lib/thin/sqlnet/constants.js +2 -0
  45. package/lib/thin/sqlnet/ezConnectResolver.js +29 -24
  46. package/lib/thin/sqlnet/navNodes.js +35 -28
  47. package/lib/thin/sqlnet/networkSession.js +45 -40
  48. package/lib/thin/sqlnet/ntTcp.js +70 -35
  49. package/lib/thin/sqlnet/nvStrToNvPair.js +16 -16
  50. package/lib/thin/sqlnet/packet.js +17 -10
  51. package/lib/thin/sqlnet/paramParser.js +9 -9
  52. package/lib/thin/sqlnet/sessionAtts.js +4 -0
  53. package/lib/thin/statement.js +4 -3
  54. package/lib/thin/util.js +28 -8
  55. package/lib/transformer.js +3 -1
  56. package/lib/util.js +0 -13
  57. package/lib/version.js +2 -2
  58. package/package.json +1 -1
  59. package/build/Release/oracledb-6.0.2-darwin-x64.node +0 -0
  60. package/build/Release/oracledb-6.0.2-darwin-x64.node-buildinfo.txt +0 -1
  61. package/build/Release/oracledb-6.0.2-js-buildinfo.txt +0 -1
  62. package/build/Release/oracledb-6.0.2-linux-arm64.node +0 -0
  63. package/build/Release/oracledb-6.0.2-linux-arm64.node-buildinfo.txt +0 -1
  64. package/build/Release/oracledb-6.0.2-linux-x64.node +0 -0
  65. package/build/Release/oracledb-6.0.2-linux-x64.node-buildinfo.txt +0 -1
  66. package/build/Release/oracledb-6.0.2-win32-x64.node +0 -0
  67. package/build/Release/oracledb-6.0.2-win32-x64.node-buildinfo.txt +0 -1
@@ -32,7 +32,7 @@ const ThinResultSetImpl = require('./resultSet.js');
32
32
  const ThinLobImpl = require("./lob.js");
33
33
  const Protocol = require("./protocol/protocol.js");
34
34
  const { BaseBuffer } = require('./protocol/buffer.js');
35
- const {NetworkSession:nsi, getConnectionInfo} = require("./sqlnet/networkSession.js");
35
+ const {NetworkSession:nsi} = require("./sqlnet/networkSession.js");
36
36
  const { Statement } = require("./statement");
37
37
  const thinUtil = require('./util');
38
38
  const sqlNetConstants = require('./sqlnet/constants.js');
@@ -49,6 +49,8 @@ const finalizationRegistry = new global.FinalizationRegistry((heldValue) => {
49
49
  class TDSBuffer extends BaseBuffer {
50
50
  }
51
51
 
52
+ const connectionCookies = new Map();
53
+
52
54
  class ThinConnectionImpl extends ConnectionImpl {
53
55
 
54
56
  /**
@@ -443,10 +445,6 @@ class ThinConnectionImpl extends ConnectionImpl {
443
445
  await this._protocol.breakMessage();
444
446
  }
445
447
 
446
- _healthStatus() {
447
- return this.nscon.recvInbandNotif();
448
- }
449
-
450
448
  isHealthy() {
451
449
  try {
452
450
  if (this.nscon.recvInbandNotif() === 0)
@@ -461,6 +459,26 @@ class ThinConnectionImpl extends ConnectionImpl {
461
459
  return (this._pool) ? true : false;
462
460
  }
463
461
 
462
+ //---------------------------------------------------------------------------
463
+ // getConnectionCookieByUUID()
464
+ //
465
+ // It fetches from map which keeps keyname as UUID+ServiceName.
466
+ // UUID identifies the CDB instance and ServiceName identifies the
467
+ // entity within a PDB which uniquely identifies a pdb instance.
468
+ //---------------------------------------------------------------------------
469
+ getConnectionCookieByUUID(uuid) {
470
+ let cookie;
471
+ if (uuid) {
472
+ const key = uuid + ((this.serviceName) ? this.serviceName : this.sid);
473
+ cookie = connectionCookies.get(key);
474
+ if (!cookie) {
475
+ cookie = {};
476
+ connectionCookies.set(key, cookie);
477
+ }
478
+ }
479
+ return cookie;
480
+ }
481
+
464
482
  /**
465
483
  *
466
484
  * @param {object} params Configuration of the connection
@@ -499,22 +517,32 @@ class ThinConnectionImpl extends ConnectionImpl {
499
517
  this._clientInfoModified = false;
500
518
  this._module = "";
501
519
  this._moduleModified = false;
520
+ this._drcpEnabled = false;
502
521
  this.serviceName = '';
503
522
  this.remoteAddress = '';
504
- this.privilege = params.privilege;
505
523
  this.comboKey = null; // used in changePassword API
506
524
 
507
525
  this.nscon = new nsi();
508
526
  finalizationRegistry.register(this, this.nscon);
509
527
  await this.nscon.connect(params);
510
528
 
511
- this._connInfo = (this.isPooled()) ? params._connInfo.map((x) => x) :
512
- await getConnectionInfo(params);
513
- this._drcpEnabled = String(this._connInfo[0]).toLowerCase() === 'pooled';
514
- this.serviceName = this._connInfo[2];
515
- this.purity = this._connInfo[3] | constants.PURITY_DEFAULT;
529
+ let serverType;
530
+ if (this.isPooled()) {
531
+ serverType = params._connInfo[0];
532
+ this.serviceName = params._connInfo[2];
533
+ this.purity = params._connInfo[3] | constants.PURITY_DEFAULT;
534
+ this.sid = params._connInfo[4];
535
+ } else {
536
+ serverType = this.nscon.getOption(sqlNetConstants.SERVERTYPE);
537
+ this.serviceName = this.nscon.getOption(sqlNetConstants.SVCNAME);
538
+ this.sid = this.nscon.getOption(sqlNetConstants.SID);
539
+ this.purity = this.nscon.getOption(sqlNetConstants.PURITY) | constants.PURITY_DEFAULT;
540
+ }
541
+ if (serverType) {
542
+ this._drcpEnabled = serverType.toLowerCase() === 'pooled';
543
+ }
516
544
  this.remoteAddress = this.nscon.getOption(sqlNetConstants.REMOTEADDR);
517
- this.connClass = params.connectionClass;
545
+ this.connectionClass = params.connectionClass;
518
546
 
519
547
  /*
520
548
  * if drcp is used, use purity = NEW as the default purity for
@@ -538,13 +566,41 @@ class ThinConnectionImpl extends ConnectionImpl {
538
566
  }
539
567
 
540
568
  try {
541
- let message = new messages.ProtocolMessage(this);
542
- await this._protocol._processMessage(message);
543
- message = new messages.DataTypeMessage(this);
544
- await this._protocol._processMessage(message);
545
- message = new messages.AuthMessage(this, params);
546
- await this._protocol._processMessage(message); // OSESSKEY
547
- await this._protocol._processMessage(message); // OAUTH
569
+ const cookie = this.getConnectionCookieByUUID(this.nscon.dbUUID);
570
+ this.nscon.dbUUID = null;
571
+ const protocolMessage = new messages.ProtocolMessage(this);
572
+ const dataTypeMessage = new messages.DataTypeMessage(this);
573
+ const authMessage = new messages.AuthMessage(this, params);
574
+ let sentCookie = false;
575
+ if (cookie && cookie.populated) {
576
+ const cookieMessage = new messages.ConnectionCookieMessage(this);
577
+ cookieMessage.cookie = cookie;
578
+ cookieMessage.protocolMessage = protocolMessage;
579
+ cookieMessage.dataTypeMessage = dataTypeMessage;
580
+ cookieMessage.authMessage = authMessage;
581
+ await this._protocol._processMessage(cookieMessage);
582
+ sentCookie = true;
583
+ } else {
584
+ await this._protocol._processMessage(protocolMessage);
585
+ }
586
+ if (!sentCookie || !cookie.populated) {
587
+ await this._protocol._processMessage(dataTypeMessage);
588
+ // Does OSESSKEY for non-token Authentication else OAUTH
589
+ await this._protocol._processMessage(authMessage);
590
+ if (cookie && !cookie.populated) {
591
+ cookie.protocolVersion = protocolMessage.serverVersion;
592
+ cookie.serverBanner = protocolMessage.serverBanner;
593
+ cookie.charsetID = this._protocol.caps.charSetID;
594
+ cookie.ncharsetID = this._protocol.caps.nCharsetId;
595
+ cookie.flags = protocolMessage.serverFlags;
596
+ cookie.compileCaps = Buffer.from(protocolMessage.serverCompileCaps);
597
+ cookie.runtimeCaps = Buffer.from(protocolMessage.serverRunTimeCaps);
598
+ cookie.populated = true;
599
+ }
600
+ }
601
+ if (!params.token) { // non-token Authentication
602
+ await this._protocol._processMessage(authMessage); // OAUTH
603
+ }
548
604
  } catch (err) {
549
605
  this.nscon.disconnect();
550
606
  throw err;
@@ -614,7 +670,7 @@ class ThinConnectionImpl extends ConnectionImpl {
614
670
  // option is true
615
671
  //---------------------------------------------------------------------------
616
672
  _prepare(sql, options) {
617
- let statement = this._getStatement(sql, options.keepInStmtCache);
673
+ const statement = this._getStatement(sql, options.keepInStmtCache);
618
674
  statement.bufferRowIndex = 0;
619
675
  statement.bufferRowCount = 0;
620
676
  statement.lastRowIndex = 0;
@@ -626,8 +682,8 @@ class ThinConnectionImpl extends ConnectionImpl {
626
682
  // Binds the values by user to the statement object
627
683
  //---------------------------------------------------------------------------
628
684
  async _bind(stmt, variable, pos = 0) {
629
- let bindInfoDict = stmt.bindInfoDict;
630
- let bindInfoList = stmt.bindInfoList;
685
+ const bindInfoDict = stmt.bindInfoDict;
686
+ const bindInfoList = stmt.bindInfoList;
631
687
 
632
688
  /*
633
689
  * For PL/SQL blocks, if the size of a string or bytes object exceeds
@@ -650,8 +706,8 @@ class ThinConnectionImpl extends ConnectionImpl {
650
706
  if (val === null) {
651
707
  return null;
652
708
  }
653
- let data = await val.getData();
654
- let len = val._length;
709
+ const data = await val.getData();
710
+ const len = val._length;
655
711
  if (data && len > maxSize) {
656
712
  errors.throwErr(errors.ERR_INSUFFICIENT_BUFFER_FOR_BINDS);
657
713
  }
@@ -662,7 +718,7 @@ class ThinConnectionImpl extends ConnectionImpl {
662
718
  if (variable.type === types.DB_TYPE_CLOB ||
663
719
  variable.type === types.DB_TYPE_NCLOB ||
664
720
  variable.type === types.DB_TYPE_BLOB) {
665
- for (let [index, val] of variable.values.entries()) {
721
+ for (const [index, val] of variable.values.entries()) {
666
722
  if (!(val instanceof ThinLobImpl)) {
667
723
  if (val && val.length > 0) {
668
724
  const lobImpl = new ThinLobImpl();
@@ -693,7 +749,7 @@ class ThinConnectionImpl extends ConnectionImpl {
693
749
  stmt._setVariable(bindInfo, variable);
694
750
  });
695
751
  } else {
696
- let bindInfo = bindInfoList[pos - 1];
752
+ const bindInfo = bindInfoList[pos - 1];
697
753
  stmt._setVariable(bindInfo, variable);
698
754
  }
699
755
  }
@@ -739,7 +795,7 @@ class ThinConnectionImpl extends ConnectionImpl {
739
795
  //---------------------------------------------------------------------------
740
796
  // Clears the statement cache for the connection
741
797
  //---------------------------------------------------------------------------
742
- resetStatmentCache() {
798
+ resetStatementCache() {
743
799
  this.statementCache.clear();
744
800
  this._cursorsToClose.clear();
745
801
  }
@@ -761,9 +817,9 @@ class ThinConnectionImpl extends ConnectionImpl {
761
817
  // information for a statment.
762
818
  //---------------------------------------------------------------------------
763
819
  async getStatementInfo(sql) {
764
- let options = {};
765
- let result = {};
766
- let statement = await this._prepareAndBind(sql, null, options, true);
820
+ const options = {};
821
+ const result = {};
822
+ const statement = await this._prepareAndBind(sql, null, options, true);
767
823
  options.connection = this;
768
824
  // parse the statement (but not for DDL which doesn't support it)
769
825
  if (!statement.isDdl) {
@@ -1038,6 +1094,12 @@ class ThinConnectionImpl extends ConnectionImpl {
1038
1094
  return '';
1039
1095
  }
1040
1096
 
1097
+ //---------------------------------------------------------------------------
1098
+ // Returns the Oracle Database instance name associated with the connection.
1099
+ //---------------------------------------------------------------------------
1100
+ getInstanceName() {
1101
+ return this.instanceName;
1102
+ }
1041
1103
  }
1042
1104
 
1043
1105
  module.exports = ThinConnectionImpl;
@@ -362,10 +362,10 @@ class ThinDbObjectImpl extends DbObjectImpl {
362
362
  return buf.readBinaryInteger();
363
363
  case types.DB_TYPE_VARCHAR:
364
364
  case types.DB_TYPE_CHAR:
365
- return buf.readStr(constants.TNS_CS_IMPLICIT);
365
+ return buf.readStr(constants.CSFRM_IMPLICIT);
366
366
  case types.DB_TYPE_NVARCHAR:
367
367
  case types.DB_TYPE_NCHAR:
368
- return buf.readStr(constants.TNS_CS_NCHAR);
368
+ return buf.readStr(constants.CSFRM_NCHAR);
369
369
  case types.DB_TYPE_RAW:
370
370
  return Buffer.from(buf.readBytesWithLength());
371
371
  case types.DB_TYPE_BINARY_DOUBLE:
package/lib/thin/lob.js CHANGED
@@ -121,10 +121,10 @@ class ThinLobImpl extends LobImpl {
121
121
  }
122
122
 
123
123
  getCsfrm() {
124
- if (this.dbType._csfrm !== constants.TNS_CS_NCHAR) {
124
+ if (this.dbType._csfrm !== constants.CSFRM_NCHAR) {
125
125
  if (this._locator[constants.TNS_LOB_LOC_OFFSET_FLAG_3] &
126
126
  constants.TNS_LOB_LOC_FLAGS_VAR_LENGTH_CHARSET) {
127
- return constants.TNS_CS_NCHAR;
127
+ return constants.CSFRM_NCHAR;
128
128
  }
129
129
  }
130
130
  return this.dbType._csfrm;
package/lib/thin/pool.js CHANGED
@@ -36,6 +36,7 @@ const thinUtil = require('./util.js');
36
36
  const {getConnectionInfo} = require('./sqlnet/networkSession.js');
37
37
  const crypto = require('crypto');
38
38
  const EventEmitter = require('events');
39
+ const Timers = require('timers');
39
40
 
40
41
  class ThinPoolImpl extends PoolImpl {
41
42
 
@@ -69,30 +70,33 @@ class ThinPoolImpl extends PoolImpl {
69
70
  this._privateKey = params.privateKey;
70
71
  this._obfuscatedPrivateKey = [];
71
72
  this._schedulerJob = null;
73
+ this._poolCloseWaiter = null;
74
+ this._pendingRequests = [];
75
+
72
76
  // password obfuscation
73
77
  if (this._password !== undefined) {
74
- let obj = protocolUtil.setObfuscatedValue(this._password);
78
+ const obj = protocolUtil.setObfuscatedValue(this._password);
75
79
  this._password = obj.value;
76
80
  this._obfuscatedPassword = obj.obfuscatedValue;
77
81
  this._userConfig.password = null;
78
82
  }
79
83
  // wallet password obfuscation
80
84
  if (this._walletPassword !== undefined) {
81
- let obj = protocolUtil.setObfuscatedValue(this._walletPassword);
85
+ const obj = protocolUtil.setObfuscatedValue(this._walletPassword);
82
86
  this._walletPassword = obj.value;
83
87
  this._obfuscatedWalletPassword = obj.obfuscatedValue;
84
88
  this._userConfig.walletPassword = null;
85
89
  }
86
90
  // token obfuscation
87
91
  if (this._token !== undefined) {
88
- let obj = protocolUtil.setObfuscatedValue(this._token);
92
+ const obj = protocolUtil.setObfuscatedValue(this._token);
89
93
  this._token = obj.value;
90
94
  this._obfuscatedToken = obj.obfuscatedValue;
91
95
  this._userConfig.token = null;
92
96
  }
93
97
  // privateKey obfuscation
94
98
  if (this._privateKey !== undefined) {
95
- let obj = protocolUtil.setObfuscatedValue(this._privateKey);
99
+ const obj = protocolUtil.setObfuscatedValue(this._privateKey);
96
100
  this._privateKey = obj.value;
97
101
  this._obfuscatedPrivateKey = obj.obfuscatedValue;
98
102
  this._userConfig.privateKey = null;
@@ -121,8 +125,9 @@ class ThinPoolImpl extends PoolImpl {
121
125
  this._generateConnectionClass();
122
126
  }
123
127
 
124
- // create minimum connections
125
- await this._growPool(this._poolMin);
128
+ // create a background task. It will create minimum connections in the pool
129
+ // and expand the pool as required.
130
+ this.bgThreadFunc();
126
131
  }
127
132
 
128
133
  //---------------------------------------------------------------------------
@@ -131,13 +136,13 @@ class ThinPoolImpl extends PoolImpl {
131
136
  setAccessToken(params) {
132
137
  if (params.token) {
133
138
  this._token = params.token;
134
- let objToken = protocolUtil.setObfuscatedValue(this._token);
139
+ const objToken = protocolUtil.setObfuscatedValue(this._token);
135
140
  this._token = objToken.value;
136
141
  this._obfuscatedToken = objToken.obfuscatedValue;
137
142
  }
138
143
  if (params.privateKey) {
139
144
  this._privateKey = params.privateKey;
140
- let objKey = protocolUtil.setObfuscatedValue(this._privateKey);
145
+ const objKey = protocolUtil.setObfuscatedValue(this._privateKey);
141
146
  this._privateKey = objKey.value;
142
147
  this._obfuscatedPrivateKey = objKey.obfuscatedValue;
143
148
  }
@@ -151,15 +156,21 @@ class ThinPoolImpl extends PoolImpl {
151
156
  async _getConnAttrs() {
152
157
  let accessToken;
153
158
  const clonedAttrs = Object.assign({}, this._userConfig);
159
+ // deobfuscate password
154
160
  if (clonedAttrs.password === null) {
155
161
  clonedAttrs.password = protocolUtil.getDeobfuscatedValue(this._password,
156
162
  this._obfuscatedPassword);
157
163
  }
164
+
165
+ // deobfuscate wallet password
158
166
  if (clonedAttrs.walletPassword === null) {
159
167
  clonedAttrs.walletPassword =
160
168
  protocolUtil.getDeobfuscatedValue(this._walletPassword,
161
169
  this._obfuscatedWalletPassword);
162
170
  }
171
+
172
+ // deobfuscate token and private key
173
+ // check for token expiry
163
174
  if (clonedAttrs.token === null) {
164
175
  clonedAttrs.token =
165
176
  protocolUtil.getDeobfuscatedValue(this._token, this._obfuscatedToken);
@@ -173,7 +184,7 @@ class ThinPoolImpl extends PoolImpl {
173
184
  errors.throwErr(errors.ERR_TOKEN_HAS_EXPIRED);
174
185
  } else {
175
186
  // update pool with OAuth2 token
176
- let obj = protocolUtil.setObfuscatedValue(clonedAttrs.token);
187
+ const obj = protocolUtil.setObfuscatedValue(clonedAttrs.token);
177
188
  this._token = obj.value;
178
189
  this._obfuscatedToken = obj.obfuscatedValue;
179
190
  }
@@ -185,11 +196,11 @@ class ThinPoolImpl extends PoolImpl {
185
196
  errors.throwErr(errors.ERR_TOKEN_HAS_EXPIRED);
186
197
  } else {
187
198
  // update pool with IAM token and private key
188
- let objToken = protocolUtil.setObfuscatedValue(clonedAttrs.token);
199
+ const objToken = protocolUtil.setObfuscatedValue(clonedAttrs.token);
189
200
  this._token = objToken.value;
190
201
  this._obfuscatedToken = objToken.obfuscatedValue;
191
202
 
192
- let objKey = protocolUtil.setObfuscatedValue(clonedAttrs.privateKey);
203
+ const objKey = protocolUtil.setObfuscatedValue(clonedAttrs.privateKey);
193
204
  this._privateKey = objKey.value;
194
205
  this._obfuscatedPrivateKey = objKey.obfuscatedValue;
195
206
  }
@@ -212,9 +223,6 @@ class ThinPoolImpl extends PoolImpl {
212
223
  // create new connection and return it
213
224
  //---------------------------------------------------------------------------
214
225
  async getConnection() {
215
- if (settings.connectionClass !== '') {
216
- this._userConfig.connectionClass = settings.connectionClass;
217
- }
218
226
  return await this.acquire();
219
227
  }
220
228
 
@@ -231,13 +239,34 @@ class ThinPoolImpl extends PoolImpl {
231
239
  //---------------------------------------------------------------------------
232
240
  // close pool by destroying available connections
233
241
  //---------------------------------------------------------------------------
234
- async close(obj) {
242
+ async close() {
243
+
244
+ // wait till background task for pool expansion is finished; if it is not
245
+ // currently running, wake it up!
246
+ await new Promise((resolve) => {
247
+ this._poolCloseWaiter = resolve;
248
+ if (this.bgWaiter) {
249
+ this.bgWaiter();
250
+ }
251
+ });
252
+
235
253
  // clear scheduled job
236
254
  if (this._schedulerJob) {
237
255
  clearTimeout(this._schedulerJob);
238
256
  this._schedulerJob = null;
239
257
  }
240
- return await this.destroyAllNow(obj);
258
+
259
+ // destroy all free connections
260
+ for (const conn of this._freeConnectionList) {
261
+ await this._destroy(conn);
262
+ }
263
+
264
+ // destroy all used connections
265
+ for (const conn of this._usedConnectionList) {
266
+ await this._destroy(conn);
267
+ }
268
+
269
+ this.eventEmitter.removeAllListeners();
241
270
  }
242
271
 
243
272
  //---------------------------------------------------------------------------
@@ -310,25 +339,6 @@ class ThinPoolImpl extends PoolImpl {
310
339
  return this._stmtCacheSize;
311
340
  }
312
341
 
313
- //---------------------------------------------------------------------------
314
- // _growPool()
315
- //
316
- // Grows the pool to include the specified number of connections.
317
- //---------------------------------------------------------------------------
318
- async _growPool(numConns) {
319
- const clonedAttrs = await this._getConnAttrs();
320
- while (numConns > 0) {
321
- const conn = new ThinConnectionImpl();
322
- conn._pool = this;
323
- await conn.connect(clonedAttrs);
324
- conn._newSession = true;
325
- conn._dropSess = false;
326
- conn._lastTimeUsed = Date.now();
327
- this._freeConnectionList.push(conn);
328
- numConns--;
329
- }
330
- }
331
-
332
342
  //---------------------------------------------------------------------------
333
343
  // _setScheduler()
334
344
  //
@@ -366,6 +376,94 @@ class ThinPoolImpl extends PoolImpl {
366
376
  this._setScheduler();
367
377
  }
368
378
 
379
+ //---------------------------------------------------------------------------
380
+ // _getNumConns()
381
+ //
382
+ // get number of connections need to be created
383
+ //---------------------------------------------------------------------------
384
+ _getNumConns() {
385
+ const usedConns = this._freeConnectionList.length + this._usedConnectionList.size;
386
+ // less connections in the pool than poolMin? restore to poolMin
387
+ if (usedConns < this._poolMin) {
388
+ return this._poolMin - usedConns;
389
+ // connections need to be created? create up to poolIncrement without exceeding poolMax
390
+ } else if (this._pendingRequests.length > 0) {
391
+ return Math.min(this._poolIncrement, this._poolMax - usedConns);
392
+ // no pending requests and we are already at poolMin so nothing to do!
393
+ } else {
394
+ return 0;
395
+ }
396
+ }
397
+
398
+ //---------------------------------------------------------------------------
399
+ // bgThreadFunc()
400
+ //
401
+ // method which runs in a background thread and is used to create connections.
402
+ // When first started, it creates poolMin connections. After that, it creates
403
+ // poolIncrement connections up to the value of poolMax when needed.
404
+ // The thread terminates automatically when the pool is closed.
405
+ //---------------------------------------------------------------------------
406
+ async bgThreadFunc() {
407
+
408
+ // continue until a close request is received
409
+ while (!this._poolCloseWaiter) {
410
+ // get count for connections to be created
411
+ const numConns = this._getNumConns();
412
+
413
+ // connection creation is going on serially and not concurrently
414
+ for (let i = 0; i < numConns; i++) {
415
+ try {
416
+ // get deobfuscated value
417
+ const config = await this._getConnAttrs();
418
+ const conn = new ThinConnectionImpl();
419
+ conn._pool = this;
420
+ await conn.connect(config);
421
+ conn._newSession = true;
422
+ conn._dropSess = false;
423
+ conn._lastTimeUsed = Date.now();
424
+ this._freeConnectionList.push(conn);
425
+ } catch (err) {
426
+ this._bgErr = err;
427
+ }
428
+
429
+ if (this._poolIncrement > 1 && (this._poolMax - this._usedConnectionList.size
430
+ - this._freeConnectionList.length) > 1) {
431
+ this._setScheduler();
432
+ }
433
+
434
+ // resolve pending request
435
+ if (this._pendingRequests.length > 0) {
436
+ const payload = this._pendingRequests.shift();
437
+ payload.resolve();
438
+ }
439
+
440
+ // give an opportunity for other "threads" to do their work.
441
+ await new Promise((resolve) => Timers.setImmediate(resolve));
442
+
443
+ // break loop when pool is closing
444
+ if (this._poolCloseWaiter) {
445
+ break;
446
+ }
447
+ }
448
+
449
+ // when pool is closing, break from while loop
450
+ if (this._poolCloseWaiter) {
451
+ break;
452
+ }
453
+
454
+ // if no pending requests, wait for pending requests to appear!
455
+ if (this._pendingRequests.length == 0 || this._bgErr) {
456
+ await new Promise((resolve) => {
457
+ this.bgWaiter = resolve;
458
+ });
459
+ this.bgWaiter = null;
460
+ }
461
+ }
462
+
463
+ // notify the closer that the close can actually take place
464
+ this._poolCloseWaiter();
465
+ }
466
+
369
467
  //---------------------------------------------------------------------------
370
468
  // acquire()
371
469
  //
@@ -413,17 +511,28 @@ class ThinPoolImpl extends PoolImpl {
413
511
 
414
512
  // no free connections exist at this point; if less than poolMin
415
513
  // connections exist, grow the pool to poolMin again; otherwise, increase
416
- // the pool by poolIncrement up to poolMax
417
- if (this._usedConnectionList.size < this._poolMin) {
418
- await this._growPool(this._poolMin - this._usedConnectionList.size);
419
- } else {
420
- const sizeAvailable = this._poolMax - this._usedConnectionList.size;
421
- await this._growPool(Math.min(this._poolIncrement, sizeAvailable));
422
- if (this._poolIncrement > 1 && sizeAvailable > 1) {
423
- this._setScheduler();
514
+ // the pool by poolIncrement up to poolMax. We are deferring this
515
+ // to the background thread function!
516
+ await new Promise((resolve) => {
517
+ this._pendingRequests.push({resolve: resolve});
518
+ if (this.bgWaiter) {
519
+ // this wakes up the function to do some more work
520
+ this.bgWaiter();
424
521
  }
425
- }
522
+ });
523
+
524
+ if (this._bgErr) {
525
+ const err = this._bgErr;
526
+ this._bgErr = null;
426
527
 
528
+ // if an error has occurred in the background thread we clear it and then,
529
+ // if there are more pending requests we request the background thread
530
+ // function to try again.
531
+ if (this._pendingRequests.length > 0 && this.bgWaiter) {
532
+ this.bgWaiter();
533
+ }
534
+ throw err;
535
+ }
427
536
  // return a connection from the ones that were just built
428
537
  const conn = this._freeConnectionList.pop();
429
538
  this._usedConnectionList.add(conn);
@@ -441,33 +550,6 @@ class ThinPoolImpl extends PoolImpl {
441
550
  this._setScheduler();
442
551
  }
443
552
 
444
- //---------------------------------------------------------------------------
445
- // destroyAllNow()
446
- //
447
- // called during pool close operation and close any available connection
448
- // available in connection pool
449
- //---------------------------------------------------------------------------
450
- async destroyAllNow(obj) {
451
- let conn;
452
- while (this._freeConnectionList.length !== 0) {
453
- conn = this._freeConnectionList.pop();
454
- await this._destroy(conn);
455
- }
456
-
457
- if (obj.forceClose) {
458
- for (const item of this._usedConnectionList) {
459
- await this._destroy(item);
460
- this._usedConnectionList.delete(item);
461
- }
462
- }
463
-
464
- if (this._usedConnectionList.size !== 0) {
465
- errors.throwErr(errors.ERR_POOL_HAS_BUSY_CONNECTIONS);
466
- }
467
-
468
- this.eventEmitter.removeAllListeners();
469
- }
470
-
471
553
  //---------------------------------------------------------------------------
472
554
  // _generateConnectionClass()
473
555
  //
@@ -92,13 +92,13 @@ class BaseBuffer {
92
92
  return 0;
93
93
  } else if (size & 0x80) {
94
94
  if (!signed) {
95
- errors.throwErr(errors.ERR_UNEXPECTED_NEGATIVE_INTEGER);
95
+ errors.throwErr(errors.ERR_UNEXPECTED_NEGATIVE_INTEGER, this.pos, this.packetNum);
96
96
  }
97
97
  isNegative = true;
98
98
  size = size & 0x7f;
99
99
  }
100
100
  if (size > maxSize) {
101
- errors.throwErr(errors.ERR_INTEGER_TOO_LARGE, size, maxSize);
101
+ errors.throwErr(errors.ERR_INTEGER_TOO_LARGE, size, maxSize, this.pos, this.packetNum);
102
102
  }
103
103
  if (skip) {
104
104
  this.skipBytes(size);
@@ -465,7 +465,7 @@ class BaseBuffer {
465
465
  if (!buf) {
466
466
  return null;
467
467
  }
468
- if (csfrm === constants.TNS_CS_IMPLICIT)
468
+ if (csfrm === constants.CSFRM_IMPLICIT)
469
469
  return buf.toString();
470
470
 
471
471
  // need a copy of the buffer since swap16() changes the buffer in place and
@@ -748,7 +748,7 @@ class BaseBuffer {
748
748
  const ptr = this.reserveBytes(length);
749
749
  if (type === types.DB_TYPE_DATE || type == types.DB_TYPE_TIMESTAMP) {
750
750
  const year = date.getFullYear();
751
- ptr[0] = Math.floor(year / 100) + 100;
751
+ ptr[0] = Math.trunc(year / 100) + 100;
752
752
  ptr[1] = year % 100 + 100;
753
753
  ptr[2] = date.getMonth() + 1;
754
754
  ptr[3] = date.getDate();
@@ -757,7 +757,7 @@ class BaseBuffer {
757
757
  ptr[6] = date.getSeconds() + 1;
758
758
  } else {
759
759
  const year = date.getUTCFullYear();
760
- ptr[0] = Math.floor(year / 100) + 100;
760
+ ptr[0] = Math.trunc(year / 100) + 100;
761
761
  ptr[1] = year % 100 + 100;
762
762
  ptr[2] = date.getUTCMonth() + 1;
763
763
  ptr[3] = date.getUTCDate();
@@ -847,7 +847,7 @@ class BaseBuffer {
847
847
  buf[0] = numPairs + 1 + appendSentinel;
848
848
  buf[1] = exponentOnWire;
849
849
  for (let i = 0, pos = 2; i < value.length; i += 2, pos++) {
850
- let base100Digit = Number(value.substring(i, i + 2));
850
+ const base100Digit = Number(value.substring(i, i + 2));
851
851
  if (isNegative) {
852
852
  buf[pos] = 101 - base100Digit;
853
853
  } else {
@@ -923,6 +923,24 @@ class BaseBuffer {
923
923
  }
924
924
  }
925
925
 
926
+ //---------------------------------------------------------------------------
927
+ // writeUB2()
928
+ //
929
+ // Writes an unsigned integer (up to 2 bytes in length) in variable length
930
+ // format to the buffer.
931
+ //---------------------------------------------------------------------------
932
+ writeUB2(value) {
933
+ if (value === 0) {
934
+ this.writeUInt8(0);
935
+ } else if (value <= 0xff) {
936
+ this.writeUInt8(1);
937
+ this.writeUInt8(value);
938
+ } else {
939
+ this.writeUInt8(2);
940
+ this.writeUInt16BE(value);
941
+ }
942
+ }
943
+
926
944
  //---------------------------------------------------------------------------
927
945
  // writeUB8()
928
946
  //