UNPKG

56.9 kBJavaScriptView Raw
1// Copyright (c) 2016, 2024, Oracle and/or its affiliates.
2
3//-----------------------------------------------------------------------------
4//
5// This software is dual-licensed to you under the Universal Permissive License
6// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
7// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
8// either license.
9//
10// If you elect to accept the software under the Apache License, Version 2.0,
11// the following applies:
12//
13// Licensed under the Apache License, Version 2.0 (the "License");
14// you may not use this file except in compliance with the License.
15// You may obtain a copy of the License at
16//
17// https://www.apache.org/licenses/LICENSE-2.0
18//
19// Unless required by applicable law or agreed to in writing, software
20// distributed under the License is distributed on an "AS IS" BASIS,
21// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22// See the License for the specific language governing permissions and
23// limitations under the License.
24//
25//-----------------------------------------------------------------------------
26
27'use strict';
28
29const AqQueue = require('./aqQueue.js');
30const BaseDbObject = require('./dbObject.js');
31const { Buffer } = require('buffer');
32const Lob = require('./lob.js');
33const ResultSet = require('./resultset.js');
34const SodaDatabase = require('./sodaDatabase.js');
35const EventEmitter = require('events');
36const QueryStream = require('./queryStream.js');
37const errors = require('./errors.js');
38const nodbUtil = require('./util.js');
39const impl = require('./impl');
40const process = require('process');
41const util = require('util');
42const constants = require('./constants.js');
43const settings = require('./settings.js');
44const transformer = require('./transformer.js');
45const types = require('./types.js');
46const oson = require('./impl/datahandlers/oson.js');
47
48// global mapping of subscriptions; these cannot be tied to a particular
49// connection or pool since subscriptions can be created with one connection
50// and destroyed with another!
51const _subscriptions = new Map();
52
53// define class
54class Connection extends EventEmitter {
55
56 constructor() {
57 super();
58 this._dbObjectClasses = new Map();
59 this._closing = false;
60 }
61
62 //---------------------------------------------------------------------------
63 // _addDefaultsToExecOpts()
64 //
65 // Add values to the execute options from the global settings, if needed.
66 //---------------------------------------------------------------------------
67 _addDefaultsToExecOpts(options) {
68 options.connection = this;
69 if (options.keepInStmtCache === undefined)
70 options.keepInStmtCache = true;
71 settings.addToOptions(options,
72 "autoCommit",
73 "dbObjectAsPojo",
74 "fetchArraySize",
75 "fetchTypeHandler",
76 "maxRows",
77 "outFormat",
78 "prefetchRows");
79 }
80
81 //---------------------------------------------------------------------------
82 // _buildDbObjectClass()
83 //
84 // Builds and returns a database object class given the object type
85 // information supplied by the implementation.
86 //---------------------------------------------------------------------------
87 _buildDbObjectClass(objType) {
88 const DbObject = function(initialValue) {
89 this._impl = new impl.DbObjectImpl(objType);
90 if (this.isCollection) {
91 const proxy = new Proxy(this, BaseDbObject._collectionProxyHandler);
92 if (initialValue !== undefined) {
93 for (let i = 0; i < initialValue.length; i++) {
94 this.append(initialValue[i]);
95 }
96 }
97 return (proxy);
98 } else if (initialValue !== undefined) {
99 Object.assign(this, initialValue);
100 }
101 };
102 DbObject.prototype = Object.create(BaseDbObject.prototype);
103 DbObject.prototype.constructor = DbObject;
104 DbObject.prototype._objType = objType;
105 if (objType.elementTypeClass) {
106 const cls = this._getDbObjectClass(objType.elementTypeClass);
107 objType.elementTypeClass = cls;
108 }
109 if (objType.isCollection) {
110 nodbUtil.addTypeProperties(objType, "elementType");
111 objType.elementTypeInfo.type = objType.elementType;
112 }
113 if (objType.attributes) {
114 const props = {};
115 for (const attr of objType.attributes) {
116 if (attr.typeClass) {
117 attr.typeClass = this._getDbObjectClass(attr.typeClass);
118 }
119 nodbUtil.addTypeProperties(attr, "type");
120 const prop = {
121 get() {
122 return this._getAttrValue(attr);
123 },
124 set(value) {
125 this._setAttrValue(attr, value);
126 }
127 };
128 props[attr.name] = prop;
129 }
130 Object.defineProperties(DbObject.prototype, props);
131 }
132 DbObject.toString = function() {
133 return ('DbObjectClass [' + objType.fqn + ']');
134 };
135 return (DbObject);
136 }
137
138 //---------------------------------------------------------------------------
139 // _getDbObjectClass()
140 //
141 // Returns the database object class given the object type information
142 // supplied by the implementation. The cache is searched first to see if an
143 // object class has already been built.
144 //---------------------------------------------------------------------------
145 _getDbObjectClass(objType) {
146 if (objType.prototype instanceof BaseDbObject)
147 return objType;
148 let cls = this._dbObjectClasses.get(objType);
149 if (!cls) {
150 cls = this._buildDbObjectClass(objType);
151 cls._connection = this;
152 cls._objType = objType;
153 objType._connection = this._impl;
154 this._dbObjectClasses.set(objType, cls);
155 }
156 return (cls);
157 }
158
159 //---------------------------------------------------------------------------
160 // _getDbObjectClassForName()
161 //
162 // Returns the database object class given the name of the database object
163 // type. The cache is searched first to see if an object class has already
164 // been built.
165 //---------------------------------------------------------------------------
166 async _getDbObjectClassForName(name) {
167 let cls = this._dbObjectClasses.get(name);
168 if (!cls) {
169 const objType = await this._impl.getDbObjectClass(name);
170 cls = this._getDbObjectClass(objType);
171 this._dbObjectClasses.set(name, cls);
172 }
173 return cls;
174 }
175
176 //---------------------------------------------------------------------------
177 // _isBindDir()
178 //
179 // Returns a boolean indicating if the supplied value is a valid bind
180 // direction.
181 //---------------------------------------------------------------------------
182 _isBindDir(value) {
183 return (
184 value === constants.BIND_IN ||
185 value === constants.BIND_OUT ||
186 value === constants.BIND_INOUT
187 );
188 }
189
190 //---------------------------------------------------------------------------
191 // _isBindValue()
192 //
193 // Returns a boolean indicating if the supplied value is one that can be
194 // bound.
195 //---------------------------------------------------------------------------
196 _isBindValue(value) {
197 return (
198 value === null ||
199 value === undefined ||
200 typeof value === 'number' ||
201 typeof value === 'string' ||
202 typeof value === 'boolean' ||
203 Array.isArray(value) ||
204 value instanceof Float32Array ||
205 value instanceof Float64Array ||
206 value instanceof Int8Array ||
207 Buffer.isBuffer(value) ||
208 util.isDate(value) ||
209 value instanceof Lob ||
210 value instanceof ResultSet ||
211 value instanceof BaseDbObject
212 );
213 }
214
215 //---------------------------------------------------------------------------
216 // _processBindUnit()
217 //
218 // Processes a bind unit (object) supplied by the user and returns the value
219 // stored in it (if one is).
220 //---------------------------------------------------------------------------
221 async _processBindUnit(bindInfo, bindUnit, inExecuteMany) {
222 let okBindUnit = false;
223
224 // get and validate bind direction; if not specified, IN is assumed
225 if (bindUnit.dir === undefined) {
226 bindInfo.dir = constants.BIND_IN;
227 } else {
228 errors.assert(this._isBindDir(bindUnit.dir),
229 errors.ERR_INVALID_BIND_DIRECTION);
230 bindInfo.dir = bindUnit.dir;
231 okBindUnit = true;
232 }
233
234 // get and validate bind type; it must be one of the integer constants
235 // identifying types, a string identifying an object type or a constructor
236 // function identifying an object type
237 if (bindUnit.type !== undefined) {
238 if (typeof bindUnit.type === 'string') {
239 bindInfo.type = types.DB_TYPE_OBJECT;
240 bindInfo.typeClass = await this._getDbObjectClassForName(bindUnit.type);
241 bindInfo.objType = bindInfo.typeClass._objType;
242 } else if (bindUnit.type.prototype instanceof BaseDbObject) {
243 bindInfo.type = types.DB_TYPE_OBJECT;
244 bindInfo.typeClass = bindUnit.type;
245 bindInfo.objType = bindInfo.typeClass._objType;
246 } else {
247 errors.assert(bindUnit.type instanceof types.DbType,
248 errors.ERR_INVALID_BIND_DATA_TYPE, 2);
249 bindInfo.type = bindUnit.type;
250 }
251 okBindUnit = true;
252
253 // when calling executeMany(), bind type is mandatory
254 } else if (inExecuteMany) {
255 if (bindInfo.name)
256 errors.throwErr(errors.ERR_MISSING_TYPE_BY_NAME, bindInfo.name);
257 errors.throwErr(errors.ERR_MISSING_TYPE_BY_POS, bindInfo.pos);
258 }
259
260 // get and validate the maximum size for strings/buffers; this value is
261 // used for IN/OUT and OUT binds in execute() and at all times for
262 // executeMany()
263 if (bindInfo.dir !== constants.BIND_IN || inExecuteMany) {
264 if (bindUnit.maxSize !== undefined) {
265 errors.assertParamPropValue(Number.isInteger(bindUnit.maxSize) &&
266 bindUnit.maxSize > 0, 2, "maxSize");
267 bindInfo.maxSize = bindUnit.maxSize;
268 bindInfo.checkSize = true;
269 okBindUnit = true;
270 } else if (inExecuteMany) {
271 if (bindInfo.type === types.DB_TYPE_VARCHAR ||
272 bindInfo.type === types.DB_TYPE_RAW) {
273 if (bindInfo.name)
274 errors.throwErr(errors.ERR_MISSING_MAX_SIZE_BY_NAME, bindInfo.name);
275 errors.throwErr(errors.ERR_MISSING_MAX_SIZE_BY_POS, bindInfo.pos);
276 }
277 } else {
278 bindInfo.maxSize = constants.DEFAULT_MAX_SIZE_FOR_OUT_BINDS;
279 }
280 }
281
282 // get max array size (for array binds, not possible in executeMany())
283 bindInfo.isArray = false;
284 if (!inExecuteMany) {
285 if (bindUnit.maxArraySize !== undefined) {
286 errors.assertParamPropValue(Number.isInteger(bindUnit.maxArraySize) &&
287 bindUnit.maxArraySize > 0, 2, "maxArraySize");
288 bindInfo.maxArraySize = bindUnit.maxArraySize;
289 bindInfo.isArray = true;
290 }
291 }
292
293 // get the value, if specified (not used in executeMany())
294 if (!inExecuteMany && bindUnit.val !== undefined) {
295 return bindUnit.val;
296 }
297
298 if (!okBindUnit)
299 errors.throwErr(errors.ERR_INVALID_BIND_UNIT);
300 }
301
302 //---------------------------------------------------------------------------
303 // _processBindValue()
304 //
305 // Processes the bind value supplied by the caller. This performs all checks
306 // on the value and normalizes it for use by the implementation class. If no
307 // bind info has been defined yet, the value defines that.
308 //---------------------------------------------------------------------------
309 async _processBindValue(bindInfo, value, options) {
310 const transformed = transformer.transformValueIn(bindInfo, value, options);
311 if (bindInfo.isArray) {
312 bindInfo.values = transformed.concat(bindInfo.values.slice(transformed.length));
313 } else {
314 bindInfo.values[options.pos] = transformed;
315 }
316 if (bindInfo.type === types.DB_TYPE_OBJECT &&
317 bindInfo.typeClass === undefined) {
318 bindInfo.typeClass = await this._getDbObjectClass(value._objType);
319 bindInfo.objType = bindInfo.typeClass._objType;
320 }
321 }
322
323 //---------------------------------------------------------------------------
324 // _processExecuteBind()
325 //
326 // Processes a single execute bind supplied by the caller. This performs all
327 // checks on the bind and normalizes it for use by the implementation class.
328 //---------------------------------------------------------------------------
329 async _processExecuteBind(bindInfo, bindData) {
330
331 // setup defaults
332 bindInfo.isArray = false;
333
334 // if bind data is a value that can be bound directly, use it; otherwise,
335 // scan the bind unit for bind information and its value
336 let bindValue;
337 if (this._isBindValue(bindData)) {
338 bindInfo.dir = constants.BIND_IN;
339 bindValue = bindData;
340 } else {
341 bindValue = await this._processBindUnit(bindInfo, bindData, false);
342 }
343
344 // for IN and IN/OUT binds, process the value
345 if (bindInfo.dir !== constants.BIND_OUT) {
346 const options = {pos: 0, allowArray: true};
347 await this._processBindValue(bindInfo, bindValue, options);
348 }
349
350 // if only null values were found (or an OUT bind was specified), type
351 // information may not be set, so complete bind information as a string
352 // and set the maxSize to 1 if it has not already been set
353 if (bindInfo.type === undefined) {
354 bindInfo.type = types.DB_TYPE_VARCHAR;
355 if (bindInfo.maxSize === undefined)
356 bindInfo.maxSize = 1;
357 }
358
359 // check valid bind type for array binds
360 if (bindInfo.isArray &&
361 bindInfo.type !== types.DB_TYPE_VARCHAR &&
362 bindInfo.type !== types.DB_TYPE_NVARCHAR &&
363 bindInfo.type !== types.DB_TYPE_CHAR &&
364 bindInfo.type !== types.DB_TYPE_NCHAR &&
365 bindInfo.type !== types.DB_TYPE_NUMBER &&
366 bindInfo.type !== types.DB_TYPE_BINARY_FLOAT &&
367 bindInfo.type !== types.DB_TYPE_BINARY_DOUBLE &&
368 bindInfo.type !== types.DB_TYPE_DATE &&
369 bindInfo.type !== types.DB_TYPE_TIMESTAMP &&
370 bindInfo.type !== types.DB_TYPE_TIMESTAMP_LTZ &&
371 bindInfo.type !== types.DB_TYPE_TIMESTAMP_TZ &&
372 bindInfo.type !== types.DB_TYPE_RAW) {
373 errors.throwErr(errors.ERR_INVALID_TYPE_FOR_ARRAY_BIND);
374 }
375
376 }
377
378 //---------------------------------------------------------------------------
379 // _processExecuteBinds()
380 //
381 // Processes the binds supplied by the caller. This performs all checks on
382 // the binds and normalizes them for use by the implementation class.
383 //---------------------------------------------------------------------------
384 async _processExecuteBinds(binds) {
385 const normBinds = [];
386 if (Array.isArray(binds)) {
387 for (let i = 0; i < binds.length; i++) {
388 const bindInfo = normBinds[i] = {pos: i + 1, values: []};
389 await this._processExecuteBind(bindInfo, binds[i]);
390 }
391 } else {
392 errors.assertParamValue(nodbUtil.isObject(binds), 2);
393 const bindNames = Object.getOwnPropertyNames(binds);
394 for (let i = 0; i < bindNames.length; i++) {
395 const bindInfo = normBinds[i] = {name: bindNames[i], values: []};
396 await this._processExecuteBind(bindInfo, binds[bindNames[i]]);
397 }
398 }
399 return normBinds;
400 }
401
402 //---------------------------------------------------------------------------
403 // _processExecuteManyBinds()
404 //
405 // Processes the binds supplied by the caller. This performs all checks on
406 // the binds and normalizes them for use by the implementation class.
407 //---------------------------------------------------------------------------
408 async _processExecuteManyBinds(binds, bindDefs) {
409 const normBinds = [];
410 let byPosition;
411
412 // transform bindDefs into normalized binds, if available
413 if (bindDefs !== undefined) {
414 if (Array.isArray(bindDefs)) {
415 byPosition = true;
416 for (let i = 0; i < bindDefs.length; i++) {
417 const bindInfo = normBinds[i] = {pos: i + 1, values: []};
418 await this._processBindUnit(bindInfo, bindDefs[i], true);
419 }
420 } else {
421 byPosition = false;
422 const bindNames = Object.getOwnPropertyNames(bindDefs);
423 for (let i = 0; i < bindNames.length; i++) {
424 const bindInfo = normBinds[i] = {name: bindNames[i], values: []};
425 await this._processBindUnit(bindInfo, bindDefs[bindNames[i]], true);
426 }
427 }
428
429 // otherwise, use the first row to determine the binds to use
430 } else {
431 const row = binds[0];
432 errors.assertParamValue(nodbUtil.isObjectOrArray(row), 2);
433 if (Array.isArray(row)) {
434 byPosition = true;
435 for (let i = 0; i < row.length; i++) {
436 normBinds[i] = {pos: i + 1};
437 }
438 } else {
439 byPosition = false;
440 const bindNames = Object.getOwnPropertyNames(row);
441 for (let i = 0; i < bindNames.length; i++) {
442 normBinds[i] = {name: bindNames[i]};
443 }
444 }
445 for (let i = 0; i < normBinds.length; i++) {
446 normBinds[i].dir = constants.BIND_IN;
447 normBinds[i].isArray = false;
448 normBinds[i].values = [];
449 }
450 }
451
452 // process each of the rows
453 for (let i = 0; i < binds.length; i++) {
454 const row = binds[i];
455 const options = {pos: i, allowArray: false};
456 errors.assert((byPosition && Array.isArray(row)) ||
457 (!byPosition && nodbUtil.isObject(row)), errors.ERR_MIXED_BIND);
458 for (let j = 0; j < normBinds.length; j++) {
459 const bindInfo = normBinds[j];
460 const value = (byPosition) ? row[j] : row[bindInfo.name];
461 await this._processBindValue(bindInfo, value, options);
462 }
463 }
464
465 // set bind type and size to a string of size 1 if no bind type was
466 // specified (and all values are null)
467 for (let i = 0; i < normBinds.length; i++) {
468 const bindInfo = normBinds[i];
469 if (bindInfo.type === undefined) {
470 bindInfo.type = types.DB_TYPE_VARCHAR;
471 bindInfo.maxSize = 1;
472 }
473 }
474
475 return normBinds;
476 }
477
478 //---------------------------------------------------------------------------
479 // _transformOutBind()
480 //
481 // Transform an output bind value from an implementation value to a user
482 // facing value (for result sets and LOBs). DML returning output variables
483 // are always an array of values.
484 //---------------------------------------------------------------------------
485 _transformOutBind(val, options) {
486 let outVal = val;
487 if (Array.isArray(val)) {
488 outVal = [];
489 for (let i = 0; i < val.length; i++)
490 outVal.push(this._transformOutBind(val[i], options));
491 } else if (val instanceof impl.ResultSetImpl) {
492 outVal = new ResultSet();
493 outVal._setup(this, val);
494 } else if (val instanceof impl.LobImpl) {
495 outVal = new Lob();
496 outVal._setup(val, true);
497 } else if (val instanceof impl.DbObjectImpl) {
498 const cls = this._dbObjectClasses.get(val._objType);
499 outVal = Object.create(cls.prototype);
500 outVal._impl = val;
501 if (options.dbObjectAsPojo) {
502 outVal = outVal._toPojo();
503 } else if (outVal.isCollection) {
504 outVal = new Proxy(outVal, BaseDbObject._collectionProxyHandler);
505 }
506 }
507 return outVal;
508 }
509
510 //---------------------------------------------------------------------------
511 // _verifyExecOpts
512 //
513 // Verify that the value passed by the user for binds is acceptable. Perform
514 // any transformations necessary.
515 //---------------------------------------------------------------------------
516 _verifyExecOpts(options, inExecuteMany) {
517
518 // define normalized options (value returned to caller)
519 const outOptions = {};
520
521 // handle common options
522 errors.assertParamValue(nodbUtil.isObject(options), 3);
523
524 // autoCommit must be a boolean value
525 if (options.autoCommit !== undefined) {
526 errors.assertParamPropValue(typeof options.autoCommit === 'boolean', 3,
527 "autoCommit");
528 outOptions.autoCommit = options.autoCommit;
529 }
530
531 // dbObjectAsPojo must be a boolean value
532 if (options.dbObjectAsPojo !== undefined) {
533 errors.assertParamPropValue(typeof options.dbObjectAsPojo === 'boolean',
534 3, "dbObjectAsPojo");
535 outOptions.dbObjectAsPojo = options.dbObjectAsPojo;
536 }
537
538 // keepInStmtCache must be a boolean value
539 if (options.keepInStmtCache !== undefined) {
540 errors.assertParamPropValue(typeof options.keepInStmtCache === 'boolean',
541 3, "keepInStmtCache");
542 outOptions.keepInStmtCache = options.keepInStmtCache;
543 }
544
545 // handle options specific to executeMany()
546 if (inExecuteMany) {
547
548 // bindDefs must be an object or array
549 if (options.bindDefs !== undefined) {
550 errors.assertParamPropValue(nodbUtil.isObjectOrArray(options.bindDefs),
551 3, "bindDefs");
552 outOptions.bindDefs = options.bindDefs;
553 }
554
555 // batchErrors must be a boolean value
556 if (options.batchErrors !== undefined) {
557 errors.assertParamPropValue(typeof options.batchErrors === 'boolean',
558 3, "batchErrors");
559 outOptions.batchErrors = options.batchErrors;
560 }
561
562 // dmlRowCounts must be a boolean value
563 if (options.dmlRowCounts !== undefined) {
564 errors.assertParamPropValue(typeof options.dmlRowCounts === 'boolean',
565 3, "dmlRowCounts");
566 outOptions.dmlRowCounts = options.dmlRowCounts;
567 }
568
569 // handle options specific to execute()
570 } else {
571
572 // fetchArraySize must be a positive integer
573 if (options.fetchArraySize !== undefined) {
574 errors.assertParamPropValue(Number.isInteger(options.fetchArraySize) &&
575 options.fetchArraySize > 0, 3, "fetchArraySize");
576 outOptions.fetchArraySize = options.fetchArraySize;
577 }
578
579 // fetchInfo must be an object with keys containing an object with a
580 // "type" property; these are converted to an array of objects for ease
581 // of processing by the implementation
582 if (options.fetchInfo !== undefined) {
583 errors.assertParamPropValue(nodbUtil.isObject(options.fetchInfo), 3,
584 "fetchInfo");
585 const names = Object.getOwnPropertyNames(options.fetchInfo);
586 const map = new Map(settings.fetchTypeMap);
587 for (const name of names) {
588 const info = options.fetchInfo[name];
589 if (info.type === undefined)
590 errors.throwErr(errors.ERR_NO_TYPE_FOR_CONVERSION);
591 if (info.type !== constants.DEFAULT &&
592 info.type !== types.DB_TYPE_VARCHAR &&
593 info.type !== types.DB_TYPE_RAW) {
594 errors.throwErr(errors.ERR_INVALID_TYPE_FOR_CONVERSION);
595 }
596 map.set(name, info.type);
597 }
598 outOptions.fetchTypeMap = map;
599 }
600
601 // fetchTypeHandler must be a function which is called for each column to
602 // be fetched and accepts the metadata for a column
603 if (options.fetchTypeHandler !== undefined) {
604 const type = (typeof options.fetchTypeHandler);
605 errors.assertParamPropValue(type === 'function', 3, "fetchTypeHandler");
606 outOptions.fetchTypeHandler = options.fetchTypeHandler;
607 }
608
609 // maxRows must be a positive integer (or 0)
610 if (options.maxRows !== undefined) {
611 errors.assertParamPropValue(Number.isInteger(options.maxRows) &&
612 options.maxRows >= 0, 3, "maxRows");
613 outOptions.maxRows = options.maxRows;
614 }
615
616 // outFormat must be one of the two possible constants
617 if (options.outFormat !== undefined) {
618 errors.assertParamPropValue(
619 options.outFormat === constants.OUT_FORMAT_ARRAY ||
620 options.outFormat === constants.OUT_FORMAT_OBJECT, 3, "outFormat");
621 outOptions.outFormat = options.outFormat;
622 }
623
624 // prefetchRows must be a positive integer (or 0)
625 if (options.prefetchRows !== undefined) {
626 errors.assertParamPropValue(Number.isInteger(options.prefetchRows) &&
627 options.prefetchRows >= 0, 3, "prefetchRows");
628 outOptions.prefetchRows = options.prefetchRows;
629 }
630
631 // resultSet must be a boolean value
632 if (options.resultSet !== undefined) {
633 errors.assertParamPropValue(typeof options.resultSet === 'boolean', 3,
634 "resultSet");
635 outOptions.resultSet = options.resultSet;
636 }
637
638 }
639
640 return outOptions;
641 }
642
643 //---------------------------------------------------------------------------
644 // action
645 //
646 // Property for end-to-end tracing attribute.
647 //---------------------------------------------------------------------------
648 get action() {
649 return null;
650 }
651
652 set action(value) {
653 errors.assertPropValue(typeof value === 'string', "action");
654 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
655 this._impl.setAction(value);
656 }
657
658 //---------------------------------------------------------------------------
659 // breakExecution()
660 //
661 // Breaks execution of a running statement.
662 //---------------------------------------------------------------------------
663 async breakExecution() {
664 errors.assertArgCount(arguments, 0, 0);
665 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
666 await this._impl.breakExecution();
667 }
668
669 //---------------------------------------------------------------------------
670 // callTimeout
671 //
672 // Property for round-trip timeouts.
673 //---------------------------------------------------------------------------
674 get callTimeout() {
675 if (this._impl)
676 return this._impl.getCallTimeout();
677 return undefined;
678 }
679
680 set callTimeout(value) {
681 errors.assertPropValue(Number.isInteger(value) && value >= 0,
682 "callTimeout");
683 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
684 this._impl.setCallTimeout(value);
685 }
686
687 //---------------------------------------------------------------------------
688 // changePassword()
689 //
690 // Changes the password of the specified user.
691 //---------------------------------------------------------------------------
692 async changePassword(user, password, newPassword) {
693 errors.assertArgCount(arguments, 3, 3);
694 errors.assertParamValue(typeof user === 'string', 1);
695 errors.assertParamValue(typeof password === 'string', 2);
696 errors.assertParamValue(typeof newPassword === 'string', 3);
697 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
698 await this._impl.changePassword(user, password, newPassword);
699 }
700
701 //---------------------------------------------------------------------------
702 // clientId
703 //
704 // Property for end-to-end tracing attribute.
705 //---------------------------------------------------------------------------
706 get clientId() {
707 return null;
708 }
709
710 set clientId(value) {
711 errors.assertPropValue(typeof value === 'string', "clientId");
712 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
713 this._impl.setClientId(value);
714 }
715
716 //---------------------------------------------------------------------------
717 // clientInfo
718 //
719 // Property for end-to-end tracing attribute.
720 //---------------------------------------------------------------------------
721 get clientInfo() {
722 return null;
723 }
724
725 set clientInfo(value) {
726 errors.assertPropValue(typeof value === 'string', "clientInfo");
727 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
728 this._impl.setClientInfo(value);
729 }
730
731 //---------------------------------------------------------------------------
732 // close()
733 //
734 // Closes the connection and makes it unusable for further work.
735 //---------------------------------------------------------------------------
736 async close(a1) {
737 let options = {};
738
739 errors.assertArgCount(arguments, 0, 1);
740 if (arguments.length == 1) {
741 errors.assertParamValue(nodbUtil.isObject(a1), 1);
742 options = a1;
743 errors.assertParamPropBool(options, 1, "drop");
744 }
745 errors.assert(this._impl && !this._closing, errors.ERR_INVALID_CONNECTION);
746
747 this._closing = true;
748 try {
749 await this._impl.close(options);
750 } finally {
751 this._closing = false;
752 }
753
754 delete this._impl;
755 this._dbObjectClasses.clear();
756 this.emit('_afterConnClose');
757 }
758
759 //---------------------------------------------------------------------------
760 // commit()
761 //
762 // Commits the current transaction.
763 //---------------------------------------------------------------------------
764 async commit() {
765 errors.assertArgCount(arguments, 0, 0);
766 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
767 await this._impl.commit();
768 }
769
770 //---------------------------------------------------------------------------
771 // createLob()
772 //
773 // Creates a temporary LOB and returns it to the caller.
774 //---------------------------------------------------------------------------
775 async createLob(type) {
776 errors.assertArgCount(arguments, 1, 1);
777 errors.assertParamValue(type === types.DB_TYPE_CLOB ||
778 type === types.DB_TYPE_BLOB ||
779 type === types.DB_TYPE_NCLOB, 1);
780 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
781 const lob = new Lob();
782 lob._setup(await this._impl.createLob(type), false);
783 return lob;
784 }
785
786 //---------------------------------------------------------------------------
787 // currentSchema
788 //
789 // Property for identifying the current schema to use in the database.
790 //---------------------------------------------------------------------------
791 get currentSchema() {
792 if (this._impl)
793 return this._impl.getCurrentSchema();
794 return undefined;
795 }
796
797 set currentSchema(value) {
798 errors.assertPropValue(typeof value === 'string', "currentSchema");
799 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
800 this._impl.setCurrentSchema(value);
801 }
802
803 //---------------------------------------------------------------------------
804 // dbOp
805 //
806 // Property for end-to-end tracing attribute.
807 //---------------------------------------------------------------------------
808 get dbOp() {
809 return null;
810 }
811
812 set dbOp(value) {
813 errors.assertPropValue(typeof value === 'string', "dbOp");
814 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
815 this._impl.setDbOp(value);
816 }
817
818 //---------------------------------------------------------------------------
819 // thin()
820 //
821 // return true, if driver mode is thin while acquiring connection
822 // return false, if driver mode is thick while acquiring connection
823 //---------------------------------------------------------------------------
824 get thin() {
825 return settings.thin;
826 }
827
828 //---------------------------------------------------------------------------
829 // ecid
830 //
831 // Property for end-to-end tracing attribute.
832 //---------------------------------------------------------------------------
833 get ecid() {
834 return null;
835 }
836
837 set ecid(value) {
838 errors.assertPropValue(typeof value === 'string', "ecid");
839 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
840 this._impl.setECID(value);
841 }
842
843 //---------------------------------------------------------------------------
844 // decode()
845 //
846 // Decodes OSON Buffer to JS data type.
847 //---------------------------------------------------------------------------
848 decodeOSON(buf) {
849 errors.assertArgCount(arguments, 1, 1);
850 errors.assertParamValue(Buffer.isBuffer(buf), 1);
851 const decoder = new oson.OsonDecoder(buf);
852 return decoder.decode();
853 }
854
855
856 //---------------------------------------------------------------------------
857 // encode()
858 //
859 // Encodes the JS value into OSON bytes.
860 //---------------------------------------------------------------------------
861 encodeOSON(value) {
862 const encoder = new oson.OsonEncoder();
863 return encoder.encode(transformer.transformJsonValue(value));
864 }
865
866 //---------------------------------------------------------------------------
867 // execute()
868 //
869 // Executes a SQL statement and returns the results.
870 //---------------------------------------------------------------------------
871 async execute(sql, a2, a3) {
872 const numIters = 1;
873 let binds = [];
874 let options = {};
875
876 // process arguments
877 if (nodbUtil.isObject(sql) && typeof sql.statement === 'string') {
878 errors.assertArgCount(arguments, 1, 2);
879 if (sql.values) {
880 binds = await this._processExecuteBinds(sql.values);
881 }
882 sql = sql.statement;
883 if (arguments.length == 2) {
884 options = this._verifyExecOpts(a2, false);
885 }
886 } else {
887 errors.assertArgCount(arguments, 1, 3);
888 errors.assertParamValue(typeof sql === 'string', 1);
889 if (arguments.length >= 2) {
890 binds = await this._processExecuteBinds(a2);
891 }
892 if (arguments.length == 3) {
893 options = this._verifyExecOpts(a3, false);
894 }
895 }
896 this._addDefaultsToExecOpts(options);
897 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
898
899 // perform actual execute
900 let result;
901 try {
902 result = await this._impl.execute(sql, numIters, binds, options, false);
903 } catch (err) {
904 if (err.errorNum === 1406)
905 errors.throwErr(errors.ERR_INSUFFICIENT_BUFFER_FOR_BINDS);
906 throw err;
907 }
908
909 // convert ORA errors to NJS
910 if (result.warning) {
911 result.warning = errors.transformErr(result.warning);
912 }
913
914 // process queries; if a result set is not desired, fetch all of the rows
915 // from the result set and then destroy the result set
916 if (result.resultSet !== undefined) {
917 const resultSet = new ResultSet();
918 resultSet._setup(this, result.resultSet);
919 result.metaData = resultSet._impl.metaData;
920 if (options.resultSet) {
921 result.resultSet = resultSet;
922 } else {
923 result.rows = await resultSet._getAllRows();
924 delete result.resultSet;
925 }
926 }
927
928 // process output binds
929 if (result.outBinds !== undefined) {
930 for (const key in result.outBinds) {
931 const val = this._transformOutBind(result.outBinds[key], options);
932 result.outBinds[key] = val;
933 }
934 }
935
936 // process implicit results; ensure all implicit results have their fetch
937 // array size fixed, or, if a result set is not requested, that all rows
938 // are fetched
939 if (result.implicitResults) {
940 for (const key in result.implicitResults) {
941 const resultSetImpl = result.implicitResults[key];
942 const resultSet = new ResultSet();
943 resultSet._setup(this, resultSetImpl);
944 if (options.resultSet) {
945 result.implicitResults[key] = resultSet;
946 } else {
947 result.implicitResults[key] = await resultSet._getAllRows();
948 }
949 }
950 }
951
952 return (result);
953 }
954
955 //---------------------------------------------------------------------------
956 // executeMany()
957 //
958 // Executes a SQL statement multiple times and returns the results.
959 //---------------------------------------------------------------------------
960 async executeMany(sql, bindsOrNumIters, a3) {
961 let options = {};
962 let binds = [];
963 let numIters;
964
965 errors.assertArgCount(arguments, 2, 3);
966 errors.assertParamValue(typeof sql === 'string', 1);
967 if (arguments.length == 3) {
968 options = this._verifyExecOpts(a3, true);
969 }
970 this._addDefaultsToExecOpts(options);
971 if (typeof bindsOrNumIters === 'number') {
972 errors.assertParamValue(Number.isInteger(bindsOrNumIters) &&
973 bindsOrNumIters > 0, 2);
974 numIters = bindsOrNumIters;
975 if (options.bindDefs !== undefined) {
976 binds = await this._processExecuteManyBinds([], options.bindDefs);
977 }
978 } else {
979 errors.assertParamValue(Array.isArray(bindsOrNumIters) &&
980 bindsOrNumIters.length > 0, 2);
981 numIters = bindsOrNumIters.length;
982 binds = await this._processExecuteManyBinds(bindsOrNumIters,
983 options.bindDefs);
984 }
985 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
986
987 const result = await this._impl.execute(sql, numIters, binds, options,
988 true);
989
990 // convert ORA warnings to NJS
991 if (result.warning) {
992 result.warning = errors.transformErr(result.warning);
993 }
994
995 // process output binds
996 if (result.outBinds !== undefined) {
997 for (let i = 0; i < result.outBinds.length; i++) {
998 const outBind = result.outBinds[i];
999 for (const key in outBind) {
1000 outBind[key] = this._transformOutBind(outBind[key], options);
1001 }
1002 }
1003 }
1004
1005 return result;
1006 }
1007
1008 //---------------------------------------------------------------------------
1009 // externalName
1010 //
1011 // Property for identifying the external name to use in TPC logging.
1012 //---------------------------------------------------------------------------
1013 get externalName() {
1014 if (this._impl)
1015 return this._impl.getExternalName();
1016 return undefined;
1017 }
1018
1019 set externalName(value) {
1020 errors.assertPropValue(typeof value === 'string', "externalName");
1021 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1022 this._impl.setExternalName(value);
1023 }
1024
1025 //---------------------------------------------------------------------------
1026 // dbDomain (READONLY)
1027 //
1028 // Property for identifying the dbDomain of the Oracle Database.
1029 //---------------------------------------------------------------------------
1030 get dbDomain() {
1031 return this._impl && this._impl.getDbDomain();
1032 }
1033
1034 //---------------------------------------------------------------------------
1035 // dbName (READONLY)
1036 //
1037 // Property for identifying the dbName of the Oracle Database.
1038 //---------------------------------------------------------------------------
1039 get dbName() {
1040 return this._impl && this._impl.getDbName();
1041 }
1042
1043 //---------------------------------------------------------------------------
1044 // getDbObjectClass()
1045 //
1046 // Returns a database object class given its name. The cache is searched
1047 // first, but if not found, the database is queried and the result is cached
1048 // using the type information (as well as the name for easier lookup later).
1049 //---------------------------------------------------------------------------
1050 async getDbObjectClass(name) {
1051 errors.assertArgCount(arguments, 1, 1);
1052 errors.assertParamValue(typeof name === 'string', 1);
1053 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1054 return await this._getDbObjectClassForName(name);
1055 }
1056
1057 //---------------------------------------------------------------------------
1058 // getQueue()
1059 //
1060 // Returns a queue with the specified name.
1061 //---------------------------------------------------------------------------
1062 async getQueue(name, a2) {
1063 let options = {};
1064
1065 errors.assertArgCount(arguments, 1, 2);
1066 errors.assertParamValue(typeof name === 'string', 1);
1067 if (arguments.length == 2) {
1068 errors.assertParamValue(nodbUtil.isObject(a2), 2);
1069 options = {...a2};
1070 }
1071 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1072 const queue = new AqQueue();
1073 await queue.create(this, name, options);
1074 return queue;
1075 }
1076
1077 //---------------------------------------------------------------------------
1078 // getSodaDatabase()
1079 //
1080 // Returns a SodaDatabase object (high-level SODA object associated with
1081 // the current connection).
1082 //---------------------------------------------------------------------------
1083 getSodaDatabase() {
1084 errors.assertArgCount(arguments, 0, 0);
1085 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1086 const sodaDb = new SodaDatabase();
1087 sodaDb._impl = this._impl.getSodaDatabase();
1088 return sodaDb;
1089 }
1090
1091 //---------------------------------------------------------------------------
1092 // getStatementInfo()
1093 //
1094 // Returns information about the statement.
1095 //---------------------------------------------------------------------------
1096 async getStatementInfo(sql) {
1097 errors.assertArgCount(arguments, 1, 1);
1098 errors.assertParamValue(typeof sql === 'string', 1);
1099 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1100 const info = await this._impl.getStatementInfo(sql);
1101 if (info.metaData) {
1102 for (let i = 0; i < info.metaData.length; i++) {
1103 const m = info.metaData[i];
1104 nodbUtil.addTypeProperties(m, "dbType");
1105 m.fetchType = types.DB_TYPE_FETCH_TYPE_MAP.get(m.dbType);
1106 }
1107 }
1108 return info;
1109 }
1110
1111 //---------------------------------------------------------------------------
1112 // instanceName
1113 //
1114 // Returns the Oracle Database instance name associated with the connection.
1115 // This is the equivalent of the SQL expression:
1116 // sys_context('userenv', 'instance_name')
1117 //---------------------------------------------------------------------------
1118 get instanceName() {
1119 if (this._impl)
1120 return this._impl.getInstanceName();
1121 return undefined;
1122 }
1123
1124 //---------------------------------------------------------------------------
1125 // internalName
1126 //
1127 // Property for identifying the internal name to use in TPC logging.
1128 //---------------------------------------------------------------------------
1129 get internalName() {
1130 if (this._impl)
1131 return this._impl.getInternalName();
1132 return undefined;
1133 }
1134
1135 set internalName(value) {
1136 errors.assertPropValue(typeof value === 'string', "internalName");
1137 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1138 this._impl.setInternalName(value);
1139 }
1140
1141 //--------------------------------------------------------------------------
1142 // isHealthy()
1143 //
1144 // Returns the health status of the connection. If this function returns
1145 // false, the caller should close the connection.
1146 //---------------------------------------------------------------------------
1147 isHealthy() {
1148 return (this._impl !== undefined && !this._closing &&
1149 this._impl.isHealthy());
1150 }
1151
1152 //---------------------------------------------------------------------------
1153 // maxOpenCursors
1154 //
1155 // Returns maximum number of cursors that can be opened in one session.
1156 //---------------------------------------------------------------------------
1157 get maxOpenCursors() {
1158 return this._impl && this._impl.getMaxOpenCursors();
1159 }
1160
1161 //---------------------------------------------------------------------------
1162 // warning
1163 //
1164 // Returns warningInfo.
1165 //---------------------------------------------------------------------------
1166 get warning() {
1167 let warning = this._impl.getWarning();
1168 if (warning) {
1169 // Make sure that warning code attribute is populated and ORA error
1170 // is converted to NJS, if required
1171 warning = errors.transformErr(warning);
1172 }
1173 return this._impl && warning;
1174 }
1175
1176 //---------------------------------------------------------------------------
1177 // module
1178 //
1179 // Property for end-to-end tracing attribute.
1180 //---------------------------------------------------------------------------
1181 get module() {
1182 return null;
1183 }
1184
1185 set module(value) {
1186 errors.assertPropValue(typeof value === 'string', "module");
1187 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1188 this._impl.setModule(value);
1189 }
1190
1191 //---------------------------------------------------------------------------
1192 // oracleServerVersion
1193 //
1194 // Returns an integer identifying the Oracle Server version.
1195 //---------------------------------------------------------------------------
1196 get oracleServerVersion() {
1197 if (this._impl)
1198 return this._impl.getOracleServerVersion();
1199 return undefined;
1200 }
1201
1202 //---------------------------------------------------------------------------
1203 // oracleServerVersionString
1204 //
1205 // Returns a string identifying the Oracle Server version.
1206 //---------------------------------------------------------------------------
1207 get oracleServerVersionString() {
1208 if (this._impl)
1209 return this._impl.getOracleServerVersionString();
1210 return undefined;
1211 }
1212
1213 //---------------------------------------------------------------------------
1214 // serviceName
1215 //
1216 // Returns the Oracle Database service name associated with the connection.
1217 //---------------------------------------------------------------------------
1218 get serviceName() {
1219 return this._impl && this._impl.getServiceName();
1220 }
1221
1222 //---------------------------------------------------------------------------
1223 // transactionInProgress
1224 //
1225 // Returns a boolean value based on the presence of an active transaction
1226 // on the connection
1227 //---------------------------------------------------------------------------
1228 get transactionInProgress() {
1229 return this._impl && this._impl.getTransactionInProgress();
1230 }
1231
1232 //---------------------------------------------------------------------------
1233 // ping()
1234 //
1235 // Sends a "ping" to the database to see if it is "alive".
1236 //---------------------------------------------------------------------------
1237 async ping() {
1238 errors.assertArgCount(arguments, 0, 0);
1239 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1240 await this._impl.ping();
1241 }
1242
1243 //--------------------------------------------------------------------------
1244 // queryStream()
1245 //
1246 // Similar to execute() except that it immediately returns a QueryStream
1247 // object.
1248 // ---------------------------------------------------------------------------
1249 queryStream(sql, binds, options) {
1250 errors.assertArgCount(arguments, 1, 3);
1251 errors.assertParamValue(typeof sql === 'string', 1);
1252 if (arguments.length == 3) {
1253 errors.assertParamValue(nodbUtil.isObject(options), 3);
1254 options = {...options};
1255 } else {
1256 options = {};
1257 }
1258 options.resultSet = true;
1259
1260 const stream = new QueryStream();
1261
1262 // calling execute() via nextTick to ensure that handlers are registered
1263 // prior to the events being emitted
1264 process.nextTick(async () => {
1265 try {
1266 const result = await this.execute(sql, binds || [], options);
1267 if (!result.resultSet)
1268 errors.throwErr(errors.ERR_NOT_A_QUERY);
1269 stream._open(result.resultSet);
1270 } catch (err) {
1271 stream.destroy(err);
1272 return;
1273 }
1274 });
1275
1276 return (stream);
1277 }
1278
1279 //---------------------------------------------------------------------------
1280 // rollback()
1281 //
1282 // Rolls back the current transaction.
1283 //---------------------------------------------------------------------------
1284 async rollback() {
1285 errors.assertArgCount(arguments, 0, 0);
1286 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1287 await this._impl.rollback();
1288 }
1289
1290 //---------------------------------------------------------------------------
1291 // shutdown()
1292 // Shuts down the database instance.
1293 //---------------------------------------------------------------------------
1294 async shutdown(a1) {
1295 let mode = constants.SHUTDOWN_MODE_DEFAULT;
1296
1297 errors.assertArgCount(arguments, 0, 1);
1298 if (a1 !== undefined) {
1299 errors.assertParamValue(typeof mode === 'number', 1);
1300 mode = a1;
1301 }
1302 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1303
1304 await this._impl.shutdown(mode);
1305 }
1306
1307 //---------------------------------------------------------------------------
1308 // startup()
1309 // Starts up the database instance.
1310 //---------------------------------------------------------------------------
1311 async startup(a1) {
1312 let options = {};
1313
1314 errors.assertArgCount(arguments, 0, 1);
1315 if (arguments.length == 1) {
1316 errors.assertParamValue(typeof options === 'object', 1);
1317 options = a1;
1318 errors.assertParamPropBool(options, 1, "force");
1319 errors.assertParamPropBool(options, 1, "restrict");
1320 errors.assertParamPropString(options, 1, "pfile");
1321 }
1322 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1323
1324 await this._impl.startup(options);
1325 }
1326
1327 //---------------------------------------------------------------------------
1328 // stmtCacheSize
1329 //
1330 // Property for statement cache size.
1331 //---------------------------------------------------------------------------
1332 get stmtCacheSize() {
1333 if (this._impl)
1334 return this._impl.getStmtCacheSize();
1335 return undefined;
1336 }
1337
1338 set stmtCacheSize(value) {
1339 errors.assertPropValue(Number.isInteger(value) && value >= 0,
1340 "stmtCacheSize");
1341 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1342 this._impl.setStmtCacheSize(value);
1343 }
1344
1345 //---------------------------------------------------------------------------
1346 // subscribe()
1347 //
1348 // Creates a subscription which can be used to get notifications of database
1349 // changes or of AQ messages available to dequeue.
1350 //---------------------------------------------------------------------------
1351 async subscribe(name, options) {
1352 errors.assertArgCount(arguments, 2, 2);
1353 errors.assertParamValue(typeof name === 'string', 1);
1354 errors.assertParamValue(nodbUtil.isObject(options), 2);
1355 options = {name: name, ...options};
1356 errors.assertParamPropUnsignedInt(options, 2, "namespace");
1357 if (options.namespace === undefined)
1358 options.namespace = constants.SUBSCR_NAMESPACE_DBCHANGE;
1359 errors.assertParamPropString(options, 2, "ipAddress");
1360 errors.assertParamPropUnsignedInt(options, 2, "port");
1361 errors.assertParamPropUnsignedInt(options, 2, "timeout");
1362 errors.assertParamPropUnsignedInt(options, 2, "operations");
1363 errors.assertParamPropUnsignedInt(options, 2, "qos");
1364 errors.assertParamPropUnsignedInt(options, 2, "groupingClass");
1365 errors.assertParamPropUnsignedInt(options, 2, "groupingValue");
1366 errors.assertParamPropUnsignedInt(options, 2, "groupingType");
1367 errors.assertParamPropBool(options, 2, "clientInitiated");
1368 errors.assertParamPropFunction(options, 2, "callback");
1369 errors.assert(options.callback, errors.ERR_MISSING_SUBSCR_CALLBACK);
1370 if (options.namespace === constants.SUBSCR_NAMESPACE_DBCHANGE) {
1371 errors.assertParamPropString(options, 2, "sql");
1372 errors.assert(options.sql && options.sql.length > 0,
1373 errors.ERR_MISSING_SUBSCR_SQL);
1374 if (options.binds !== undefined) {
1375 options.binds = await this._processExecuteBinds(options.binds);
1376 }
1377 }
1378 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1379
1380 const inSubscr = _subscriptions.get(name);
1381 const outValue = await this._impl.subscribe(inSubscr, options);
1382 let subscription;
1383 if (options.namespace === constants.SUBSCR_NAMESPACE_DBCHANGE) {
1384 subscription = outValue.subscription;
1385 delete outValue.subscription;
1386 } else {
1387 subscription = outValue;
1388 }
1389 _subscriptions.set(name, subscription);
1390 return outValue;
1391 }
1392
1393 //---------------------------------------------------------------------------
1394 // tag
1395 //
1396 // Property for tag to associate with the connection.
1397 //---------------------------------------------------------------------------
1398 get tag() {
1399 if (this._impl)
1400 return this._impl.getTag();
1401 return undefined;
1402 }
1403
1404 set tag(value) {
1405 errors.assertPropValue(typeof value === 'string', "tag");
1406 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1407 this._impl.setTag(value);
1408 }
1409
1410 //---------------------------------------------------------------------------
1411 // tpcBegin()
1412 //
1413 // Starts a two-phase-commit transaction.
1414 //--------------------------------------------------------------------------
1415 async tpcBegin(xid, flag, timeout) {
1416 errors.assertArgCount(arguments, 1, 3);
1417 errors.assertParamValue(nodbUtil.isXid(xid), 1);
1418
1419 if (arguments.length < 3) {
1420 timeout = 60; // seconds
1421 } else {
1422 errors.assertParamValue(typeof timeout === 'number', 3);
1423 }
1424
1425 if (arguments.length < 2) {
1426 flag = constants.TPC_BEGIN_NEW;
1427 } else {
1428 errors.assertParamValue(typeof flag === 'number', 2);
1429 }
1430 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1431 await this._impl.tpcBegin(xid, flag, timeout);
1432 }
1433
1434 //---------------------------------------------------------------------------
1435 // tpcCommit()
1436 //
1437 // Commits a two-phase-commit transaction.
1438 //---------------------------------------------------------------------------
1439 async tpcCommit(xid, onePhase) {
1440 errors.assertArgCount(arguments, 0, 2);
1441
1442 if (arguments.length < 2) {
1443 onePhase = false;
1444 } else {
1445 errors.assertParamValue(typeof onePhase === 'boolean', 2);
1446 }
1447 if (arguments.length >= 1) {
1448 errors.assertParamValue(nodbUtil.isXid(xid), 1);
1449 }
1450 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1451 await this._impl.tpcCommit(xid, onePhase);
1452 }
1453
1454 //---------------------------------------------------------------------------
1455 // tpcEnd()
1456 //
1457 // Ends a two-phase-commit transaction.
1458 //---------------------------------------------------------------------------
1459 async tpcEnd(xid, flag) {
1460 errors.assertArgCount(arguments, 0, 2);
1461
1462 if (arguments.length < 2) {
1463 flag = constants.TPC_END_NORMAL;
1464 } else {
1465 errors.assertParamValue(typeof flag === 'number', 2);
1466 }
1467
1468 if (arguments.length >= 1) {
1469 errors.assertParamValue(nodbUtil.isXid(xid), 1);
1470 }
1471 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1472
1473 await this._impl.tpcEnd(xid, flag);
1474 }
1475
1476 //---------------------------------------------------------------------------
1477 // tpcForget()
1478 //
1479 // Causes the server to forget a heuristically completed two-phase-commit
1480 // transaction.
1481 // ---------------------------------------------------------------------------
1482 async tpcForget(xid) {
1483 errors.assertArgCount(arguments, 1, 1);
1484 errors.assertParamValue(nodbUtil.isXid(xid), 1);
1485 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1486
1487 await this._impl.tpcForget(xid);
1488 }
1489
1490 //---------------------------------------------------------------------------
1491 // tpcPrepare()
1492 //
1493 // Prepares a two-phase-commit transaction for commit.
1494 //---------------------------------------------------------------------------
1495 async tpcPrepare(xid) {
1496 errors.assertArgCount(arguments, 0, 1);
1497 if (arguments.length >= 1) {
1498 errors.assertParamValue(nodbUtil.isXid(xid), 1);
1499 }
1500 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1501
1502 return await this._impl.tpcPrepare(xid);
1503 }
1504
1505 //---------------------------------------------------------------------------
1506 // tpcRecover()
1507 //
1508 // Returns a list of pending two-phase-commit transactions.
1509 //---------------------------------------------------------------------------
1510 async tpcRecover(asString) {
1511 errors.assertArgCount(arguments, 0, 1);
1512
1513 if (arguments.length == 1) {
1514 errors.assertParamValue(typeof asString === 'boolean', 1);
1515 } else {
1516 asString = true;
1517 }
1518
1519 const sqlStr = `
1520 SELECT
1521 formatid as "formatId",
1522 UTL_RAW.CAST_TO_VARCHAR2(globalid) as "globalTransactionId",
1523 UTL_RAW.CAST_TO_VARCHAR2(branchid) as "branchQualifier"
1524 FROM DBA_PENDING_TRANSACTIONS`;
1525 const sqlBuf = `
1526 SELECT
1527 formatid as "formatId",
1528 globalid as "globalTransactionId",
1529 branchid as "branchQualifier"
1530 FROM DBA_PENDING_TRANSACTIONS`;
1531 const options = {
1532 outFormat: constants.OUT_FORMAT_OBJECT,
1533 resultSet: false
1534 };
1535
1536 const result = await this.execute(asString ? sqlStr : sqlBuf, {}, options);
1537 return result.rows;
1538 }
1539
1540 //---------------------------------------------------------------------------
1541 // tpcRollback()
1542 //
1543 // Rolls back the current changes in a two-phase-commit transaction.
1544 //---------------------------------------------------------------------------
1545 async tpcRollback(xid) {
1546 errors.assertArgCount(arguments, 0, 1);
1547 if (arguments.length == 1) {
1548 errors.assertParamValue(nodbUtil.isXid(xid), 1);
1549 }
1550 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1551
1552 await this._impl.tpcRollback(xid);
1553 }
1554
1555 //---------------------------------------------------------------------------
1556 // unsubscribe()
1557 //
1558 // Destroy a subscription which was earlier created using subscribe().
1559 //---------------------------------------------------------------------------
1560 async unsubscribe(name) {
1561 errors.assertArgCount(arguments, 1, 1);
1562 errors.assertParamValue(typeof name === 'string', 1);
1563 errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1564 errors.assert(_subscriptions.has(name), errors.ERR_INVALID_SUBSCR);
1565 await this._impl.unsubscribe(_subscriptions.get(name));
1566 _subscriptions.delete(name);
1567 }
1568
1569}
1570
1571// adjust functions to support the old callback style and to serialize calls
1572// that cannot take place concurrently
1573// NOTE: breakExecution() should not be serialized
1574Connection.prototype.break =
1575 nodbUtil.callbackify(Connection.prototype.breakExecution);
1576nodbUtil.wrapFns(Connection.prototype,
1577 "changePassword",
1578 "close",
1579 "commit",
1580 "createLob",
1581 "execute",
1582 "executeMany",
1583 "getDbObjectClass",
1584 "getQueue",
1585 "getStatementInfo",
1586 "ping",
1587 "rollback",
1588 "shutdown",
1589 "startup",
1590 "subscribe",
1591 "tpcBegin",
1592 "tpcCommit",
1593 "tpcEnd",
1594 "tpcForget",
1595 "tpcPrepare",
1596 "tpcRecover",
1597 "tpcRollback",
1598 "unsubscribe");
1599
1600// add alias for release()
1601Connection.prototype.release = Connection.prototype.close;
1602
1603// export just the Connection class
1604module.exports = Connection;