oracledb 5.5.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +1 -1875
  2. package/LICENSE.txt +8 -222
  3. package/NOTICE.txt +1 -1
  4. package/README.md +38 -30
  5. package/SECURITY.md +38 -0
  6. package/build/Release/oracledb-6.0.0-darwin-x64.node +0 -0
  7. package/build/Release/oracledb-6.0.0-darwin-x64.node-buildinfo.txt +1 -0
  8. package/build/Release/oracledb-6.0.0-js-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-6.0.0-linux-arm64.node +0 -0
  10. package/build/Release/oracledb-6.0.0-linux-arm64.node-buildinfo.txt +1 -0
  11. package/build/Release/oracledb-6.0.0-linux-x64.node +0 -0
  12. package/build/Release/oracledb-6.0.0-linux-x64.node-buildinfo.txt +1 -0
  13. package/build/Release/oracledb-6.0.0-win32-x64.node +0 -0
  14. package/build/Release/oracledb-6.0.0-win32-x64.node-buildinfo.txt +1 -0
  15. package/examples/README.md +113 -0
  16. package/examples/dbconfig.js +100 -0
  17. package/examples/example.js +156 -0
  18. package/index.js +26 -0
  19. package/lib/aqDeqOptions.js +146 -7
  20. package/lib/aqEnqOptions.js +59 -7
  21. package/lib/aqMessage.js +118 -7
  22. package/lib/aqQueue.js +242 -53
  23. package/lib/connection.js +1324 -465
  24. package/lib/constants.js +203 -0
  25. package/lib/dbObject.js +346 -32
  26. package/lib/errors.js +781 -0
  27. package/lib/impl/aqDeqOptions.js +197 -0
  28. package/lib/impl/aqEnqOptions.js +89 -0
  29. package/lib/impl/aqMessage.js +134 -0
  30. package/lib/impl/aqQueue.js +81 -0
  31. package/lib/impl/connection.js +529 -0
  32. package/lib/impl/dbObject.js +159 -0
  33. package/lib/impl/index.js +59 -0
  34. package/lib/impl/lob.js +85 -0
  35. package/lib/impl/pool.js +195 -0
  36. package/lib/impl/resultset.js +223 -0
  37. package/lib/impl/sodaCollection.js +174 -0
  38. package/lib/impl/sodaDatabase.js +81 -0
  39. package/lib/impl/sodaDocCursor.js +63 -0
  40. package/lib/impl/sodaDocument.js +98 -0
  41. package/lib/impl/sodaOperation.js +109 -0
  42. package/lib/lob.js +156 -68
  43. package/lib/oracledb.js +1047 -345
  44. package/lib/pool.js +654 -465
  45. package/lib/poolStatistics.js +23 -9
  46. package/lib/queryStream.js +14 -7
  47. package/lib/resultset.js +291 -183
  48. package/lib/settings.js +191 -0
  49. package/lib/sodaCollection.js +214 -201
  50. package/lib/sodaDatabase.js +107 -71
  51. package/lib/sodaDocCursor.js +44 -32
  52. package/lib/sodaDocument.js +76 -14
  53. package/lib/sodaOperation.js +131 -118
  54. package/lib/thin/connection.js +1038 -0
  55. package/lib/thin/dbObject.js +629 -0
  56. package/lib/thin/index.js +40 -0
  57. package/lib/thin/lob.js +188 -0
  58. package/lib/thin/pool.js +481 -0
  59. package/lib/thin/protocol/buffer.js +1040 -0
  60. package/lib/thin/protocol/capabilities.js +116 -0
  61. package/lib/thin/protocol/constants.js +764 -0
  62. package/lib/thin/protocol/encryptDecrypt.js +185 -0
  63. package/lib/thin/protocol/messages/auth.js +348 -0
  64. package/lib/thin/protocol/messages/base.js +475 -0
  65. package/lib/thin/protocol/messages/commit.js +56 -0
  66. package/lib/thin/protocol/messages/dataType.js +382 -0
  67. package/lib/thin/protocol/messages/execute.js +312 -0
  68. package/lib/thin/protocol/messages/fetch.js +60 -0
  69. package/lib/thin/protocol/messages/index.js +53 -0
  70. package/lib/thin/protocol/messages/lobOp.js +200 -0
  71. package/lib/thin/protocol/messages/logOff.js +53 -0
  72. package/lib/thin/protocol/messages/ping.js +56 -0
  73. package/lib/thin/protocol/messages/protocol.js +78 -0
  74. package/lib/thin/protocol/messages/rollback.js +56 -0
  75. package/lib/thin/protocol/messages/sessionRelease.js +60 -0
  76. package/lib/thin/protocol/messages/withData.js +811 -0
  77. package/lib/thin/protocol/oson.js +660 -0
  78. package/lib/thin/protocol/packet.js +520 -0
  79. package/lib/thin/protocol/protocol.js +202 -0
  80. package/lib/thin/protocol/utils.js +88 -0
  81. package/lib/thin/resultSet.js +122 -0
  82. package/lib/thin/sqlnet/connStrategy.js +175 -0
  83. package/lib/thin/sqlnet/constants.js +199 -0
  84. package/lib/thin/sqlnet/ezConnectResolver.js +488 -0
  85. package/lib/thin/sqlnet/navNodes.js +870 -0
  86. package/lib/thin/sqlnet/networkSession.js +620 -0
  87. package/lib/thin/sqlnet/ntTcp.js +525 -0
  88. package/lib/thin/sqlnet/nvStrToNvPair.js +783 -0
  89. package/lib/thin/sqlnet/packet.js +405 -0
  90. package/lib/thin/sqlnet/paramParser.js +205 -0
  91. package/lib/thin/sqlnet/sessionAtts.js +160 -0
  92. package/lib/thin/statement.js +301 -0
  93. package/lib/thin/util.js +289 -0
  94. package/lib/transformer.js +250 -0
  95. package/lib/types.js +357 -0
  96. package/lib/util.js +194 -110
  97. package/lib/version.js +36 -0
  98. package/package/README.md +33 -30
  99. package/package/install.js +89 -64
  100. package/package/prunebinaries.js +60 -12
  101. package/package.json +3 -3
  102. package/build/Release/oracledb-5.5.0-darwin-x64.node +0 -0
  103. package/build/Release/oracledb-5.5.0-darwin-x64.node-buildinfo.txt +0 -1
  104. package/build/Release/oracledb-5.5.0-linux-x64.node +0 -0
  105. package/build/Release/oracledb-5.5.0-linux-x64.node-buildinfo.txt +0 -1
  106. package/build/Release/oracledb-5.5.0-win32-x64.node +0 -0
  107. package/build/Release/oracledb-5.5.0-win32-x64.node-buildinfo.txt +0 -1
@@ -0,0 +1,1038 @@
1
+ // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
2
+
3
+ //-----------------------------------------------------------------------------
4
+ //
5
+ // This software is dual-licensed to you under the Universal Permissive License
6
+ // (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
7
+ // 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
8
+ // either license.
9
+ //
10
+ // If you elect to accept the software under the Apache License, Version 2.0,
11
+ // the following applies:
12
+ //
13
+ // Licensed under the Apache License, Version 2.0 (the "License");
14
+ // you may not use this file except in compliance with the License.
15
+ // You may obtain a copy of the License at
16
+ //
17
+ // https://www.apache.org/licenses/LICENSE-2.0
18
+ //
19
+ // Unless required by applicable law or agreed to in writing, software
20
+ // distributed under the License is distributed on an "AS IS" BASIS,
21
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
+ // See the License for the specific language governing permissions and
23
+ // limitations under the License.
24
+ //
25
+ //-----------------------------------------------------------------------------
26
+
27
+ 'use strict';
28
+
29
+ const ConnectionImpl = require('../impl/connection.js');
30
+ const ThinResultSetImpl = require('./resultSet.js');
31
+ const ThinLobImpl = require("./lob.js");
32
+ const Protocol = require("./protocol/protocol.js");
33
+ const { BaseBuffer } = require('./protocol/buffer.js');
34
+ const {NetworkSession:nsi, getConnectionInfo} = require("./sqlnet/networkSession.js");
35
+ const { Statement } = require("./statement");
36
+ const thinUtil = require('./util');
37
+ const sqlNetConstants = require('./sqlnet/constants.js');
38
+ const constants = require('./protocol/constants.js');
39
+ const types = require('../types.js');
40
+ const errors = require("../errors.js");
41
+ const messages = require('./protocol/messages');
42
+
43
+ const finalizationRegistry = new global.FinalizationRegistry((heldValue) => {
44
+ heldValue.disconnect();
45
+ });
46
+
47
+ class TDSBuffer extends BaseBuffer {
48
+ }
49
+
50
+ class ThinConnectionImpl extends ConnectionImpl {
51
+
52
+ /**
53
+ * Terminates the connection
54
+ *
55
+ * @return {Promise}
56
+ */
57
+ async close() {
58
+ if (this._protocol.txnInProgress) {
59
+ await this.rollback();
60
+ }
61
+ this._protocol.callTimeout = 0; // not applicable for close
62
+ if (this._drcpEnabled) {
63
+ await this._sessRelease();
64
+ this._drcpEstablishSession = true;
65
+ }
66
+
67
+ if (this._pool && !this._dropSess) {
68
+ await this._pool.release(this);
69
+ } else {
70
+ if (!this._drcpEnabled) {
71
+ const message = new messages.LogOffMessage(this);
72
+ await this._protocol._processMessage(message);
73
+ }
74
+ this.nscon.disconnect();
75
+ }
76
+ }
77
+
78
+ async _sessRelease() {
79
+ const message = new messages.SessionReleaseMessage(this);
80
+ if (!this.isPooled()) {
81
+ message.sessReleaseMode = constants.DRCP_DEAUTHENTICATE;
82
+ }
83
+ await this._protocol._processMessage(message);
84
+ }
85
+
86
+ //---------------------------------------------------------------------------
87
+ // _determineElementObjType()
88
+ //
89
+ // Determine the element type's object type. This is needed when processing
90
+ // collections with an object as the element type since this information is
91
+ // not available in the TDS.
92
+ //---------------------------------------------------------------------------
93
+ async _determineElementObjType(info) {
94
+ const binds = [
95
+ {
96
+ name: "owner",
97
+ type: types.DB_TYPE_VARCHAR,
98
+ dir: constants.BIND_IN,
99
+ maxSize: 128,
100
+ values: [info.schema]
101
+ },
102
+ {
103
+ name: "name",
104
+ type: types.DB_TYPE_VARCHAR,
105
+ dir: constants.BIND_IN,
106
+ maxSize: 128,
107
+ values: [info.name]
108
+ },
109
+ {
110
+ name: "package_name",
111
+ type: types.DB_TYPE_VARCHAR,
112
+ dir: constants.BIND_IN,
113
+ maxSize: 128,
114
+ values: [info.packageName]
115
+ }
116
+ ];
117
+ let sql;
118
+ if (info.packageName) {
119
+ sql = `
120
+ select
121
+ elem_type_owner,
122
+ elem_type_name,
123
+ elem_type_package
124
+ from all_plsql_coll_types
125
+ where owner = :owner
126
+ and type_name = :name
127
+ and package_name = :package_name`;
128
+ } else {
129
+ binds.pop();
130
+ sql = `
131
+ select
132
+ elem_type_owner,
133
+ elem_type_name
134
+ from all_coll_types
135
+ where owner = :owner
136
+ and type_name = :name`;
137
+ }
138
+ const options = {
139
+ connection: { _impl: this },
140
+ prefetchRows: 2
141
+ };
142
+ const result = await this.execute(sql, 1, binds, options, false);
143
+ const rows = await result.resultSet.getRows(1, options);
144
+ await result.resultSet.close();
145
+ const row = rows[0];
146
+ info.elementTypeClass = this._getDbObjectType(row[0], row[1], row[2]);
147
+ if (info.elementTypeClass.partial) {
148
+ this._partialDbObjectTypes.push(info.elementTypeClass);
149
+ }
150
+ }
151
+
152
+ //---------------------------------------------------------------------------
153
+ // _parseElementType()
154
+ //
155
+ // Parses the element type from the TDS buffer.
156
+ //---------------------------------------------------------------------------
157
+ async _parseElementType(buf, info) {
158
+ let oraTypeNum, csfrm;
159
+ const attrType = buf.readUInt8();
160
+ switch (attrType) {
161
+ case constants.TNS_OBJ_TDS_TYPE_NUMBER:
162
+ case constants.TNS_OBJ_TDS_TYPE_FLOAT:
163
+ info.elementType = types.DB_TYPE_NUMBER;
164
+ break;
165
+ case constants.TNS_OBJ_TDS_TYPE_VARCHAR:
166
+ case constants.TNS_OBJ_TDS_TYPE_CHAR:
167
+ info.maxSize = buf.readUInt16BE();
168
+ oraTypeNum = (attrType === constants.TNS_OBJ_TDS_TYPE_VARCHAR) ?
169
+ constants.TNS_DATA_TYPE_VARCHAR : constants.TNS_DATA_TYPE_CHAR;
170
+ csfrm = buf.readUInt8();
171
+ info.elementType = types.getTypeByOraTypeNum(oraTypeNum, csfrm);
172
+ break;
173
+ case constants.TNS_OBJ_TDS_TYPE_RAW:
174
+ info.elementType = types.DB_TYPE_RAW;
175
+ break;
176
+ case constants.TNS_OBJ_TDS_TYPE_BINARY_FLOAT:
177
+ info.elementType = types.DB_TYPE_BINARY_FLOAT;
178
+ break;
179
+ case constants.TNS_OBJ_TDS_TYPE_BINARY_DOUBLE:
180
+ info.elementType = types.DB_TYPE_BINARY_DOUBLE;
181
+ break;
182
+ case constants.TNS_OBJ_TDS_TYPE_DATE:
183
+ info.elementType = types.DB_TYPE_DATE;
184
+ break;
185
+ case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP:
186
+ info.elementType = types.DB_TYPE_TIMESTAMP;
187
+ break;
188
+ case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_LTZ:
189
+ info.elementType = types.DB_TYPE_TIMESTAMP_LTZ;
190
+ break;
191
+ case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_TZ:
192
+ info.elementType = types.DB_TYPE_TIMESTAMP_TZ;
193
+ break;
194
+ case constants.TNS_OBJ_TDS_TYPE_BOOLEAN:
195
+ info.elementType = types.DB_TYPE_BOOLEAN;
196
+ break;
197
+ case constants.TNS_OBJ_TDS_TYPE_CLOB:
198
+ this._determineElementTypeCharsetForm(info);
199
+ break;
200
+ case constants.TNS_OBJ_TDS_TYPE_BLOB:
201
+ info.elementType = types.DB_TYPE_BLOB;
202
+ break;
203
+ case constants.TNS_OBJ_TDS_TYPE_OBJ:
204
+ info.elementType = types.DB_TYPE_OBJECT;
205
+ await this._determineElementObjType(info);
206
+ break;
207
+ default:
208
+ errors.throwErr(errors.ERR_TDS_TYPE_NOT_SUPPORTED, attrType);
209
+ }
210
+ }
211
+
212
+ //---------------------------------------------------------------------------
213
+ // _parseTDS()
214
+ //
215
+ // Parses the TDS for the type. This is only needed for collection types, so
216
+ // if the TDS is determined to be for an object type, the remaining
217
+ // information is ignored.
218
+ //---------------------------------------------------------------------------
219
+ async _parseTDS(tds, info) {
220
+
221
+ // parse initial TDS bytes
222
+ const buf = new TDSBuffer(tds);
223
+ buf.skipBytes(4); // end offset
224
+ buf.skipBytes(2); // version op code and version
225
+ buf.skipBytes(2); // unknown
226
+
227
+ // if the number of attributes exceeds 1, the type cannot refer to a
228
+ // collection, so nothing further needs to be done
229
+ const numAttrs = buf.readUInt16BE();
230
+ if (numAttrs > 1) {
231
+ info.isCollection = false;
232
+ return;
233
+ }
234
+
235
+ // continue parsing TDS bytes to discover if type refers to a collection
236
+ buf.skipBytes(1); // TDS attributes?
237
+ buf.skipBytes(1); // start ADT op code
238
+ buf.skipBytes(2); // ADT number (always zero)
239
+ buf.skipBytes(4); // offset to index table
240
+
241
+ // if type of first attribute is not a collection, nothing further needs
242
+ // to be done
243
+ const attrType = buf.readUInt8();
244
+ info.isCollection = (attrType === constants.TNS_OBJ_TDS_TYPE_COLL);
245
+ if (!info.isCollection)
246
+ return;
247
+
248
+ // continue parsing TDS to determine element type
249
+ const elementPos = buf.readUInt32BE();
250
+ info.maxNumElements = buf.readUInt32BE();
251
+ info.collectionType = buf.readUInt8();
252
+ if (info.collectionType === constants.TNS_OBJ_PLSQL_INDEX_TABLE) {
253
+ info.collectionFlags = constants.TNS_OBJ_HAS_INDEXES;
254
+ }
255
+ buf.pos = elementPos;
256
+
257
+ await this._parseElementType(buf, info);
258
+ }
259
+
260
+ //---------------------------------------------------------------------------
261
+ // _populateDbObjectTypeInfo()
262
+ //
263
+ // Poplates type information given the name of the type.
264
+ //---------------------------------------------------------------------------
265
+ async _populateDbObjectTypeInfo(name) {
266
+
267
+ // get type information from the database
268
+ const sql = `
269
+ declare
270
+ t_Instantiable varchar2(3);
271
+ t_SuperTypeOwner varchar2(128);
272
+ t_SuperTypeName varchar2(128);
273
+ t_SubTypeRefCursor sys_refcursor;
274
+ t_Pos pls_integer;
275
+ begin
276
+ :ret_val := dbms_pickler.get_type_shape(:full_name, :oid,
277
+ :version, :tds, t_Instantiable, t_SuperTypeOwner,
278
+ t_SuperTypeName, :attrs_rc, t_SubTypeRefCursor);
279
+ :package_name := null;
280
+ if substr(:full_name, length(:full_name) - 7) = '%ROWTYPE' then
281
+ t_Pos := instr(:full_name, '.');
282
+ :schema := substr(:full_name, 1, t_Pos - 1);
283
+ :name := substr(:full_name, t_Pos + 1);
284
+ else
285
+ begin
286
+ select owner, type_name
287
+ into :schema, :name
288
+ from all_types
289
+ where type_oid = :oid;
290
+ exception
291
+ when no_data_found then
292
+ begin
293
+ select owner, package_name, type_name
294
+ into :schema, :package_name, :name
295
+ from all_plsql_types
296
+ where type_oid = :oid;
297
+ exception
298
+ when no_data_found then
299
+ null;
300
+ end;
301
+ end;
302
+ end if;
303
+ end;`;
304
+ const binds = [
305
+ {
306
+ name: "full_name",
307
+ type: types.DB_TYPE_VARCHAR,
308
+ dir: constants.BIND_INOUT,
309
+ maxSize: 500,
310
+ values: [name]
311
+ },
312
+ {
313
+ name: "ret_val",
314
+ type: types.DB_TYPE_BINARY_INTEGER,
315
+ dir: constants.BIND_OUT,
316
+ values: []
317
+ },
318
+ {
319
+ name: "oid",
320
+ type: types.DB_TYPE_RAW,
321
+ maxSize: 16,
322
+ dir: constants.BIND_OUT,
323
+ values: []
324
+ },
325
+ {
326
+ name: "version",
327
+ type: types.DB_TYPE_BINARY_INTEGER,
328
+ dir: constants.BIND_OUT,
329
+ values: []
330
+ },
331
+ {
332
+ name: "tds",
333
+ type: types.DB_TYPE_RAW,
334
+ maxSize: 2000,
335
+ dir: constants.BIND_OUT,
336
+ values: []
337
+ },
338
+ {
339
+ name: "attrs_rc",
340
+ type: types.DB_TYPE_CURSOR,
341
+ dir: constants.BIND_OUT,
342
+ values: []
343
+ },
344
+ {
345
+ name: "package_name",
346
+ type: types.DB_TYPE_VARCHAR,
347
+ maxSize: 128,
348
+ dir: constants.BIND_OUT,
349
+ values: []
350
+ },
351
+ {
352
+ name: "schema",
353
+ type: types.DB_TYPE_VARCHAR,
354
+ maxSize: 128,
355
+ dir: constants.BIND_OUT,
356
+ values: []
357
+ },
358
+ {
359
+ name: "name",
360
+ type: types.DB_TYPE_VARCHAR,
361
+ maxSize: 128,
362
+ dir: constants.BIND_OUT,
363
+ values: []
364
+ }
365
+ ];
366
+ const options = {
367
+ connection: { _impl: this },
368
+ nullifyInvalidCursor: true
369
+ };
370
+ const result = await this.execute(sql, 1, binds, options, false);
371
+ if (result.outBinds.ret_val !== 0) {
372
+ errors.throwErr(errors.ERR_INVALID_OBJECT_TYPE_NAME, name);
373
+ }
374
+
375
+ // check cache; if already present, nothing more to do!
376
+ const info = this._getDbObjectType(result.outBinds.schema,
377
+ result.outBinds.name, result.outBinds.package_name, result.outBinds.oid);
378
+ if (!info.partial)
379
+ return info;
380
+
381
+ // process TDS and attributes cursor
382
+ info.version = result.outBinds.version;
383
+ await this._parseTDS(result.outBinds.tds, info);
384
+ const attrRows = await result.outBinds.attrs_rc.getRows(1000, {});
385
+ if (attrRows.length > 0) {
386
+ info.attributes = [];
387
+ for (const row of attrRows) {
388
+ const attr = { name: row[1] };
389
+ if (row[4]) {
390
+ attr.type = types.DB_TYPE_OBJECT;
391
+ attr.typeClass = this._getDbObjectType(row[4], row[3], row[5], row[6]);
392
+ if (attr.typeClass.partial) {
393
+ this._partialDbObjectTypes.push(attr.typeClass);
394
+ }
395
+ } else {
396
+ attr.type = types.getTypeByColumnTypeName(row[3]);
397
+ }
398
+ info.attributes.push(attr);
399
+ }
400
+ }
401
+ info.partial = false;
402
+
403
+ return info;
404
+
405
+ }
406
+
407
+ //---------------------------------------------------------------------------
408
+ // _populatePartialDbObjectTypes()
409
+ //
410
+ // Populates partial types that were discovered earlier. Since populating an
411
+ // object type might result in additional object types being discovered,
412
+ // object types are popped from the partial types list until the list is
413
+ // empty.
414
+ //---------------------------------------------------------------------------
415
+ async _populatePartialDbObjectTypes() {
416
+ while (this._partialDbObjectTypes.length > 0) {
417
+ const info = this._partialDbObjectTypes.pop();
418
+ let suffix = "%ROWTYPE";
419
+ let name = info.name;
420
+ if (name.endsWith(suffix)) {
421
+ name = name.substring(0, name.length - suffix.length);
422
+ } else {
423
+ suffix = "";
424
+ }
425
+ let fullName;
426
+ if (info.packageName) {
427
+ fullName = `"${info.schema}"."${info.packageName}"."${name}"${suffix}`;
428
+ } else {
429
+ fullName = `"${info.schema}"."${name}"${suffix}`;
430
+ }
431
+ await this._populateDbObjectTypeInfo(fullName);
432
+ }
433
+ }
434
+
435
+ async commit() {
436
+ const message = new messages.CommitMessage(this);
437
+ await this._protocol._processMessage(message);
438
+ }
439
+
440
+ async breakExecution() {
441
+ await this._protocol.breakMessage();
442
+ }
443
+
444
+ _healthStatus() {
445
+ return this.nscon.recvInbandNotif();
446
+ }
447
+
448
+ isHealthy() {
449
+ try {
450
+ if (this.nscon.recvInbandNotif() === 0)
451
+ return true;
452
+ return false;
453
+ } catch {
454
+ return false;
455
+ }
456
+ }
457
+
458
+ isPooled() {
459
+ return (this._pool) ? true : false;
460
+ }
461
+
462
+ /**
463
+ *
464
+ * @param {object} params Configuration of the connection
465
+ *
466
+ * @return {Promise}
467
+ */
468
+ async connect(params) {
469
+ if (!params.connectString) {
470
+ errors.throwErr(errors.ERR_EMPTY_CONNECT_STRING);
471
+ }
472
+ thinUtil.checkCredentials(params);
473
+
474
+ this.sessionID = 0;
475
+ this.serialNum = 0;
476
+ this.autoCommit = false;
477
+ this.serverVersion = "";
478
+ this.statementCache = null;
479
+ this.currentSchema = "";
480
+ this.invokeSessionCallback = true;
481
+ this.statementCache = new Map();
482
+ this.statementCacheSize = params.stmtCacheSize;
483
+ this._numCursorsToClose = 0;
484
+ this._currentSchemaModified = false;
485
+ this._cursorsToClose = new Set();
486
+ this._tempLobsToClose = [];
487
+ this._tempLobsTotalSize = 0;
488
+ this._drcpEstablishSession = false;
489
+ this._cclass = null;
490
+ this._clientIdentifier = "";
491
+ this._clientIdentifierModified = false;
492
+ this._action = "";
493
+ this._actionModified = false;
494
+ this._dbOp = "";
495
+ this._dbOpModified = false;
496
+ this._clientInfo = "";
497
+ this._clientInfoModified = false;
498
+ this._module = "";
499
+ this._moduleModified = false;
500
+ this.serviceName = '';
501
+ this.remoteAddress = '';
502
+ this.privilege = params.privilege;
503
+ this.comboKey = null; // used in changePassword API
504
+
505
+ this.nscon = new nsi();
506
+ finalizationRegistry.register(this, this.nscon);
507
+ await this.nscon.connect(params);
508
+
509
+ this._connInfo = (this.isPooled()) ? params._connInfo.map((x) => x) :
510
+ await getConnectionInfo(params);
511
+ this._drcpEnabled = String(this._connInfo[0]).toLowerCase() === 'pooled';
512
+ this.serviceName = this._connInfo[2];
513
+ this.purity = this._connInfo[3] | constants.PURITY_DEFAULT;
514
+ this.remoteAddress = this.nscon.getOption(sqlNetConstants.REMOTEADDR);
515
+ this.connClass = params.connectionClass;
516
+
517
+ /*
518
+ * if drcp is used, use purity = NEW as the default purity for
519
+ * standalone connections and purity = SELF for connections that belong
520
+ * to a pool
521
+ */
522
+ if (this.purity === constants.PURITY_DEFAULT && this._drcpEnabled) {
523
+ if (this.isPooled()) {
524
+ this.purity = constants.PURITY_SELF;
525
+ } else {
526
+ this.purity = constants.PURITY_NEW;
527
+ }
528
+ }
529
+
530
+ this._protocol = new Protocol(this);
531
+ try {
532
+ let message = new messages.ProtocolMessage(this);
533
+ await this._protocol._processMessage(message);
534
+ message = new messages.DataTypeMessage(this);
535
+ await this._protocol._processMessage(message);
536
+ message = new messages.AuthMessage(this, params);
537
+ await this._protocol._processMessage(message); // OSESSKEY
538
+ await this._protocol._processMessage(message); // OAUTH
539
+ } catch (err) {
540
+ this.nscon.disconnect();
541
+ throw err;
542
+ }
543
+
544
+ // maintain a list of partially populated database object types
545
+ this._partialDbObjectTypes = [];
546
+
547
+ if (params.debugJDWP) {
548
+ this.jdwpData = Buffer.from(params.debugJDWP);
549
+ } else if (process.env.ORA_DEBUG_JDWP) {
550
+ this.jdwpData = Buffer.from(process.env.ORA_DEBUG_JDWP);
551
+ }
552
+ this._protocol.connInProgress = false;
553
+ }
554
+
555
+ //---------------------------------------------------------------------------
556
+ // Sets that a statement is no longer in use
557
+ //---------------------------------------------------------------------------
558
+ _returnStatement(statement) {
559
+ if (statement.bindInfoList) {
560
+ statement.bindInfoList.forEach(bindInfo => {
561
+ bindInfo.bindVar = null;
562
+ });
563
+ }
564
+ if (statement.queryVars) {
565
+ statement.queryVars.forEach(queryVar => {
566
+ queryVar.values.fill(null);
567
+ });
568
+ }
569
+ if (statement.returnToCache) {
570
+ statement.inUse = false;
571
+ this._adjustStatementCache();
572
+ } else if (statement.cursorId !== 0) {
573
+ this._addCursorToClose(statement);
574
+ }
575
+ }
576
+
577
+ //---------------------------------------------------------------------------
578
+ // Adds the cursors that needs to be closed to the _cursorsToClose set
579
+ //---------------------------------------------------------------------------
580
+ _addCursorToClose(stmt) {
581
+ if (stmt.cursorId > 0 && this._cursorsToClose.has(stmt.cursorId)) {
582
+ const reason = `attempt to close cursor ${stmt.cursorId} twice`;
583
+ errors.throwErr(errors.ERR_INTERNAL, reason);
584
+ }
585
+
586
+ if (this.statementCache.has(stmt.sql)) {
587
+ this.statementCache.delete(stmt.sql);
588
+ this._cursorsToClose.add(stmt.cursorId);
589
+ } else if (stmt.cursorId > 0) {
590
+ this._cursorsToClose.add(stmt.cursorId);
591
+ }
592
+ }
593
+
594
+ //---------------------------------------------------------------------------
595
+ // Adjusts the statement cache to remove least recently used statements
596
+ //---------------------------------------------------------------------------
597
+ _adjustStatementCache() {
598
+ while (this.statementCache.size >= this.statementCacheSize) {
599
+ let stmt = this.statementCache.get(this.statementCache.keys().next().value);
600
+ this.statementCache.delete(this.statementCache.keys().next().value);
601
+ if (stmt.inUse) {
602
+ stmt.returnToCache = false;
603
+ } else if (stmt.cursorId !== 0) {
604
+ this._addCursorToClose(stmt);
605
+ }
606
+ }
607
+ }
608
+
609
+ //---------------------------------------------------------------------------
610
+ // Parses the sql statement and puts it into cache if keepInStmtCache
611
+ // option is true
612
+ //---------------------------------------------------------------------------
613
+ _prepare(sql, options) {
614
+ let statement = this._getStatement(sql, options.keepInStmtCache);
615
+ statement.bufferRowIndex = 0;
616
+ statement.bufferRowCount = 0;
617
+ statement.lastRowIndex = 0;
618
+ statement.moreRowsToFetch = true;
619
+ return statement;
620
+ }
621
+
622
+ //---------------------------------------------------------------------------
623
+ // Binds the values by user to the statement object
624
+ //---------------------------------------------------------------------------
625
+ async _bind(stmt, variable, pos = 0) {
626
+ let bindInfoDict = stmt.bindInfoDict;
627
+ let bindInfoList = stmt.bindInfoList;
628
+
629
+ /*
630
+ * For PL/SQL blocks, if the size of a string or bytes object exceeds
631
+ * 32,767 bytes it is converted to a BLOB/CLOB; and conversion
632
+ * needs to be established as well to return the string in the way that
633
+ * the user expects to get it
634
+ */
635
+ if (stmt.isPlSql && variable.maxSize > 32767) {
636
+ if (variable.type === types.DB_TYPE_RAW ||
637
+ variable.type === types.DB_TYPE_LONG_RAW) {
638
+ variable.type = types.DB_TYPE_BLOB;
639
+ } else if (variable.type._csfrm === constants.CSFRM_NCHAR) {
640
+ variable.type = types.DB_TYPE_NCLOB;
641
+ } else {
642
+ variable.type = types.DB_TYPE_CLOB;
643
+ }
644
+ const maxSize = variable.maxSize;
645
+ delete variable.maxSize;
646
+ variable.outConverter = async function(val) {
647
+ if (val === null) {
648
+ return null;
649
+ }
650
+ let data = await val.getData();
651
+ let len = val._length;
652
+ if (data && len > maxSize) {
653
+ errors.throwErr(errors.ERR_INSUFFICIENT_BUFFER_FOR_BINDS);
654
+ }
655
+ return data;
656
+ };
657
+ }
658
+
659
+ if (variable.type === types.DB_TYPE_CLOB ||
660
+ variable.type === types.DB_TYPE_NCLOB ||
661
+ variable.type === types.DB_TYPE_BLOB) {
662
+ for (let [index, val] of variable.values.entries()) {
663
+ if (!(val instanceof ThinLobImpl)) {
664
+ if (val && val.length > 0) {
665
+ const lobImpl = new ThinLobImpl();
666
+ await lobImpl.create(this, variable.type);
667
+ await lobImpl.write(1, val);
668
+ variable.values[index] = lobImpl;
669
+ } else {
670
+ variable.values[index] = null;
671
+ }
672
+ }
673
+ }
674
+ }
675
+
676
+ if (variable.name) {
677
+ let normalizedName;
678
+ if (variable.name.startsWith('"') && variable.name.endsWith('"')) {
679
+ normalizedName = variable.name.substring(1, variable.name.length - 1);
680
+ } else {
681
+ normalizedName = variable.name.toUpperCase();
682
+ }
683
+ if (normalizedName.startsWith(':')) {
684
+ normalizedName = variable.name.substring(1);
685
+ }
686
+ if (!bindInfoDict.has(normalizedName)) {
687
+ errors.throwErr(errors.ERR_INVALID_BIND_NAME, normalizedName);
688
+ }
689
+ bindInfoDict.get(normalizedName).forEach((bindInfo) => {
690
+ stmt._setVariable(bindInfo, variable);
691
+ });
692
+ } else {
693
+ let bindInfo = bindInfoList[pos - 1];
694
+ stmt._setVariable(bindInfo, variable);
695
+ }
696
+ }
697
+
698
+ //---------------------------------------------------------------------------
699
+ // _createResultSet()
700
+ //
701
+ // Creates a result set and performs any necessary initialization.
702
+ //---------------------------------------------------------------------------
703
+ _createResultSet(options, statement) {
704
+ const resultSet = new ThinResultSetImpl();
705
+ if (!statement) {
706
+ statement = new Statement();
707
+ }
708
+ resultSet._resultSetNew(this, statement, options);
709
+ if (statement.queryVars.length > 0) {
710
+ const metadata = thinUtil.getMetadataMany(statement.queryVars);
711
+ resultSet._setup(options, metadata);
712
+ }
713
+ return resultSet;
714
+ }
715
+
716
+ //---------------------------------------------------------------------------
717
+ // Prepares the sql given by user and binds the value to the statement object
718
+ //---------------------------------------------------------------------------
719
+ async _prepareAndBind(sql, binds, options, isParse = false) {
720
+ const stmt = this._prepare(sql, options);
721
+ let position = 0;
722
+ if (!isParse) {
723
+ const numBinds = stmt.bindInfoList.length;
724
+ const numVars = binds.length;
725
+ if (numBinds !== numVars && (numVars === 0 || !binds[0].name)) {
726
+ errors.throwErr(errors.ERR_WRONG_NUMBER_OF_POSITIONAL_BINDS, numBinds, numVars);
727
+ }
728
+ for (const variable of binds) {
729
+ await this._bind(stmt, variable, position + 1);
730
+ position += 1;
731
+ }
732
+ }
733
+ return stmt;
734
+ }
735
+
736
+ //---------------------------------------------------------------------------
737
+ // Clears the statement cache for the connection
738
+ //---------------------------------------------------------------------------
739
+ resetStatmentCache() {
740
+ this.statementCache.clear();
741
+ this._cursorsToClose.clear();
742
+ }
743
+
744
+ //---------------------------------------------------------------------------
745
+ // getDbObjectClass()
746
+ //
747
+ // Returns a database object class given its name.
748
+ //---------------------------------------------------------------------------
749
+ async getDbObjectClass(name) {
750
+ const info = await this._populateDbObjectTypeInfo(name);
751
+ await this._populatePartialDbObjectTypes();
752
+ return info;
753
+ }
754
+
755
+ //---------------------------------------------------------------------------
756
+ // Parses the sql given by User
757
+ // calls the OAL8 RPC that parses the SQL statement and returns the metadata
758
+ // information for a statment.
759
+ //---------------------------------------------------------------------------
760
+ async getStatementInfo(sql) {
761
+ let options = {};
762
+ let result = {};
763
+ let statement = await this._prepareAndBind(sql, null, options, true);
764
+ options.connection = this;
765
+ // parse the statement (but not for DDL which doesn't support it)
766
+ if (!statement.isDdl) {
767
+ const message = new messages.ExecuteMessage(this, statement, options);
768
+ message.parseOnly = true;
769
+ await this._protocol._processMessage(message);
770
+ }
771
+ if (statement.numQueryVars > 0) {
772
+ result.metaData = thinUtil.getMetadataMany(statement.queryVars);
773
+ }
774
+ result.bindNames = Array.from(statement.bindInfoDict.keys());
775
+ result.statementType = statement.statementType;
776
+ return result;
777
+ }
778
+
779
+ //---------------------------------------------------------------------------
780
+ // Prepares the sql given by the user,
781
+ // calls the OAL8 RPC that executes a SQL statement and returns the results.
782
+ //---------------------------------------------------------------------------
783
+ async execute(sql, numIters, binds, options, executeManyFlag) {
784
+ const result = {};
785
+ if (executeManyFlag) {
786
+ return await this.executeMany(sql, numIters, binds, options);
787
+ }
788
+ const statement = await this._prepareAndBind(sql, binds, options);
789
+
790
+ // send the initial request to the database
791
+ const message = new messages.ExecuteMessage(this, statement, options);
792
+ message.numExecs = 1;
793
+ await this._protocol._processMessage(message);
794
+ statement.requiresFullExecute = false;
795
+
796
+ // if a define is required, send an additional request to the database
797
+ if (statement.requiresDefine && statement.sql) {
798
+ statement.requiresFullExecute = true;
799
+ await this._protocol._processMessage(message);
800
+ statement.requiresFullExecute = false;
801
+ statement.requiresDefine = false;
802
+ }
803
+
804
+ // process message results
805
+ if (statement.numQueryVars > 0) {
806
+ result.resultSet = message.resultSet;
807
+ } else {
808
+ statement.bufferRowIndex = 0;
809
+ const bindVars = thinUtil.getBindVars(statement);
810
+ const outBinds = thinUtil.getExecuteOutBinds(bindVars);
811
+ if (outBinds) {
812
+ result.outBinds = outBinds;
813
+ }
814
+ if (statement.isPlSql) {
815
+ if (options.implicitResultSet) {
816
+ result.implicitResults = options.implicitResultSet;
817
+ }
818
+ }
819
+ if (statement.lastRowid) {
820
+ result.lastRowid = statement.lastRowid;
821
+ delete statement.lastRowid;
822
+ }
823
+ if (statement.isPlSql) {
824
+ if (statement.rowCount) {
825
+ result.rowsAffected = statement.rowCount;
826
+ }
827
+ } else {
828
+ result.rowsAffected = statement.rowCount || 0;
829
+ }
830
+ if (statement.rowCount) {
831
+ delete statement.rowCount;
832
+ }
833
+ this._returnStatement(statement);
834
+ }
835
+
836
+ return result;
837
+ }
838
+
839
+ //---------------------------------------------------------------------------
840
+ // executeMany()
841
+ //
842
+ // Prepares the sql given by the user, calls the OAL8 RPC that executes a SQL
843
+ // statement multiple times and returns the results.
844
+ //---------------------------------------------------------------------------
845
+ async executeMany(sql, numIters, binds, options) {
846
+ const statement = await this._prepareAndBind(sql, binds, options);
847
+ if (statement.isPlSql && (options.batchErrors || options.dmlRowCounts)) {
848
+ errors.throwErr(errors.ERR_EXEC_MODE_ONLY_FOR_DML);
849
+ }
850
+
851
+ // send database request
852
+ const message = new messages.ExecuteMessage(this, statement, options);
853
+ message.numExecs = numIters;
854
+ message.arrayDmlRowCounts = options.dmlRowCounts;
855
+ message.batchErrors = options.batchErrors;
856
+ if (statement.isPlSql && statement.requiresFullExecute) {
857
+ message.numExecs = 1;
858
+ await this._protocol._processMessage(message);
859
+ statement.requiresFullExecute = false;
860
+ message.numExecs = numIters - 1;
861
+ message.offset = 1;
862
+ }
863
+ if (message.numExecs > 0) {
864
+ await this._protocol._processMessage(message);
865
+ }
866
+
867
+ // process results
868
+ const returnObj = {};
869
+ statement.bufferRowIndex = 0;
870
+ const bindVars = thinUtil.getBindVars(statement);
871
+ const outBinds = thinUtil.getExecuteManyOutBinds(bindVars, numIters);
872
+ if (outBinds) {
873
+ returnObj.outBinds = outBinds;
874
+ }
875
+ const rowsAffected = !statement.isPlSql ? statement.rowCount : undefined;
876
+ if (rowsAffected) {
877
+ returnObj.rowsAffected = rowsAffected;
878
+ delete statement.rowCount;
879
+ }
880
+ if (options.dmlRowCounts) {
881
+ returnObj.dmlRowCounts = options.dmlRowCounts;
882
+ }
883
+ if (options.batchErrors) {
884
+ returnObj.batchErrors = options.batchErrors;
885
+ }
886
+ this._returnStatement(statement);
887
+
888
+ return returnObj;
889
+ }
890
+
891
+ //---------------------------------------------------------------------------
892
+ // Get the statement object from the statement cache for the SQL if it exists
893
+ // else prepare a new statement object for the SQL. If a statement is already
894
+ // in use a copy will be made and returned (and will not be returned to the
895
+ // cache). If a statement is being executed for the first time after releasing
896
+ // a DRCP session, a copy will also be made (and will not be returned to the
897
+ // cache) since it is unknown at this point whether the original session or a
898
+ // new session is going to be used.
899
+ //---------------------------------------------------------------------------
900
+ _getStatement(sql, cacheStatement = false) {
901
+ let statement = this.statementCache.get(sql);
902
+ if (!statement) {
903
+ statement = new Statement();
904
+ statement._prepare(sql);
905
+ if (cacheStatement && this.statementCache.size < this.statementCacheSize && !this._drcpEstablishSession && !statement.isDdl) {
906
+ this.statementCache.set(sql, statement);
907
+ statement.returnToCache = true;
908
+ this._adjustStatementCache();
909
+ }
910
+ } else if (statement.inUse || !cacheStatement || this._drcpEstablishSession) {
911
+ if (!cacheStatement) {
912
+ this.statementCache.delete(sql);
913
+ statement.returnToCache = false;
914
+ }
915
+ if (statement.inUse || this._drcpEstablishSession) {
916
+ statement = statement._copy();
917
+ }
918
+ } else {
919
+ this.statementCache.delete(sql);
920
+ this.statementCache.set(sql, statement);
921
+ }
922
+ statement.inUse = true;
923
+ return statement;
924
+ }
925
+
926
+ //---------------------------------------------------------------------------
927
+ // Calls the ping RPC for Oracle Database
928
+ //---------------------------------------------------------------------------
929
+ async ping() {
930
+ const message = new messages.PingMessage(this);
931
+ await this._protocol._processMessage(message);
932
+ }
933
+
934
+ //---------------------------------------------------------------------------
935
+ // Calls the Rollback RPC for Oracle Database
936
+ //---------------------------------------------------------------------------
937
+ async rollback() {
938
+ const message = new messages.RollbackMessage(this);
939
+ await this._protocol._processMessage(message);
940
+ }
941
+
942
+ //---------------------------------------------------------------------------
943
+ // Returns the Oracle Server version
944
+ //---------------------------------------------------------------------------
945
+ getOracleServerVersion() {
946
+ return this.serverVersion;
947
+ }
948
+
949
+ //---------------------------------------------------------------------------
950
+ // Returns the Oracle Server version string
951
+ //---------------------------------------------------------------------------
952
+ getOracleServerVersionString() {
953
+ return this.serverVersionString;
954
+ }
955
+
956
+ setCurrentSchema(schema) {
957
+ this._currentSchemaModified = true;
958
+ this.currentSchema = schema;
959
+ }
960
+
961
+ getCurrentSchema() {
962
+ return this.currentSchema;
963
+ }
964
+
965
+ setClientId(clientId) {
966
+ this._clientIdentifierModified = true;
967
+ this._clientIdentifier = clientId;
968
+ }
969
+
970
+ setDbOp(dbOp) {
971
+ this._dbOpModified = true;
972
+ this._dbOp = dbOp;
973
+ }
974
+
975
+ setClientInfo(clientInfo) {
976
+ this._clientInfoModified = true;
977
+ this._clientInfo = clientInfo;
978
+ }
979
+
980
+ setModule(module) {
981
+ this._moduleModified = true;
982
+ this._module = module;
983
+
984
+ /*
985
+ * setting the module by itself results in an error so always force
986
+ * action to be set as well (which eliminates this error)
987
+ */
988
+ this._actionModified = true;
989
+ }
990
+
991
+ setAction(action) {
992
+ this._actionModified = true;
993
+ this._action = action;
994
+ }
995
+
996
+ async changePassword(user, password, newPassword) {
997
+ const config = {
998
+ user: user,
999
+ newPassword: newPassword,
1000
+ password: password,
1001
+ changePassword: true
1002
+ };
1003
+ const message = new messages.AuthMessage(this, config);
1004
+ await this._protocol._processMessage(message); // OAUTH
1005
+ }
1006
+
1007
+ async createLob(dbType) {
1008
+ const lobImpl = new ThinLobImpl();
1009
+ await lobImpl.create(this, dbType);
1010
+ return lobImpl;
1011
+ }
1012
+
1013
+ //---------------------------------------------------------------------------
1014
+ // Returns the statement cache size for the statement cache maintained by
1015
+ // the connection object
1016
+ //---------------------------------------------------------------------------
1017
+ getStmtCacheSize() {
1018
+ return this.statementCacheSize;
1019
+ }
1020
+
1021
+ setCallTimeout(timeout) {
1022
+ this._protocol.callTimeout = timeout;
1023
+ }
1024
+
1025
+ getCallTimeout() {
1026
+ return this._protocol.callTimeout;
1027
+ }
1028
+
1029
+ //---------------------------------------------------------------------------
1030
+ // Returns getTag. Actual tag returned by db must be a string.
1031
+ //---------------------------------------------------------------------------
1032
+ getTag() {
1033
+ return '';
1034
+ }
1035
+
1036
+ }
1037
+
1038
+ module.exports = ThinConnectionImpl;