UNPKG

oracledb

Version:

A Node.js module for Oracle Database access from JavaScript and TypeScript

1,373 lines (1,242 loc) 66.8 kB
// Copyright (c) 2016, 2025, Oracle and/or its affiliates. //----------------------------------------------------------------------------- // // This software is dual-licensed to you under the Universal Permissive License // (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License // 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose // either license. // // If you elect to accept the software under the Apache License, Version 2.0, // the following applies: // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //----------------------------------------------------------------------------- 'use strict'; const AqQueue = require('./aqQueue.js'); const BaseDbObject = require('./dbObject.js'); const { Buffer } = require('buffer'); const Lob = require('./lob.js'); const ResultSet = require('./resultset.js'); const SodaDatabase = require('./sodaDatabase.js'); const EventEmitter = require('events'); const QueryStream = require('./queryStream.js'); const errors = require('./errors.js'); const nodbUtil = require('./util.js'); const impl = require('./impl'); const process = require('process'); const util = require('util'); const constants = require('./constants.js'); const settings = require('./settings.js'); const transformer = require('./transformer.js'); const types = require('./types.js'); const oson = require('./impl/datahandlers/oson.js'); // global mapping of subscriptions; these cannot be tied to a particular // connection or pool since subscriptions can be created with one connection // and destroyed with another! const _subscriptions = new Map(); // default closure for NUMBER type. const defaultNumberConverter = (v) => (v === null) ? null : parseFloat(v); //--------------------------------------------------------------------------- // _determineDbObjTypeConverter() // // Determines the converter associated with each DB type and its metadata. // This function is called once during metadata construction and the // converters are invoked when retriving DBObject values. //--------------------------------------------------------------------------- function _determineDbObjTypeConverter(metadata, options) { // clear any previous converter functions that may have been // retained delete metadata.converter; // If a DBfetch type handler is specified, update converter, // if available. if (options.dbObjectTypeHandler) { const result = options.dbObjectTypeHandler(metadata); if (result !== undefined) { errors.assert(typeof result === 'object', errors.ERR_DB_FETCH_TYPE_HANDLER_RETURN_VALUE); if (result.converter !== undefined) { errors.assert(typeof result.converter === 'function', errors.ERR_DB_FETCH_TYPE_HANDLER_CONVERTER); } if ([types.DB_TYPE_CLOB, types.DB_TYPE_NCLOB, types.DB_TYPE_BLOB, types.DB_TYPE_BFILE].includes(metadata.type)) { // converters for LOB's are not supported. return errors.throwErr(errors.ERR_NOT_IMPLEMENTED, 'DbObjConverter for LOBs'); } metadata.converter = result.converter; } } if (metadata.type === types.DB_TYPE_NUMBER && !metadata.converter) { // set default converter for NUMBER type as they are returned as strings // from DB. metadata.converter = defaultNumberConverter; } } // define class class Connection extends EventEmitter { constructor() { super(); this._dbObjectClasses = new Map(); this._closing = false; } //--------------------------------------------------------------------------- // _addDefaultsToExecOpts() // // Add values to the execute options from the global settings, if needed. //--------------------------------------------------------------------------- _addDefaultsToExecOpts(options) { options.connection = this; if (options.keepInStmtCache === undefined) options.keepInStmtCache = true; if (options.suspendOnSuccess === undefined) options.suspendOnSuccess = false; settings.addToOptions(options, "autoCommit", "dbObjectAsPojo", "fetchArraySize", "fetchTypeHandler", "maxRows", "outFormat", "prefetchRows"); } //--------------------------------------------------------------------------- // _buildDbObjectClass() // // Builds and returns a database object class given the object type // information supplied by the implementation. //--------------------------------------------------------------------------- _buildDbObjectClass(objType) { const DbObject = function(initialValue) { this._impl = new impl.DbObjectImpl(objType); if (this.isCollection) { const proxy = new Proxy(this, BaseDbObject._collectionProxyHandler); if (initialValue !== undefined) { for (let i = 0; i < initialValue.length; i++) { this.append(initialValue[i]); } } return (proxy); } else if (initialValue !== undefined) { for (const attr of objType.attributes) { const value = initialValue[attr.name]; if (value !== undefined) { this._setAttrValue(attr, value); } } } }; DbObject.prototype = Object.create(BaseDbObject.prototype); DbObject.prototype.constructor = DbObject; DbObject.prototype._objType = objType; if (objType.elementTypeClass) { const cls = this._getDbObjectClass(objType.elementTypeClass); objType.elementTypeClass = cls; } const options = {dbObjectTypeHandler: settings.dbObjectTypeHandler}; if (objType.isCollection) { nodbUtil.addTypeProperties(objType, "elementType"); objType.elementTypeInfo.type = objType.elementType; _determineDbObjTypeConverter(objType.elementTypeInfo, options); } if (objType.attributes) { const props = {}; for (const attr of objType.attributes) { if (attr.typeClass) { attr.typeClass = this._getDbObjectClass(attr.typeClass); } nodbUtil.addTypeProperties(attr, "type"); const prop = { get() { return this._getAttrValue(attr); }, set(value) { this._setAttrValue(attr, value); } }; props[attr.name] = prop; // calculate for each attribute metadata as converters might change // based on precision, scale, maxSize,.. _determineDbObjTypeConverter(attr, options); } Object.defineProperties(DbObject.prototype, props); } DbObject.toString = function() { return ('DbObjectClass [' + objType.fqn + ']'); }; return (DbObject); } //--------------------------------------------------------------------------- // _getDbObjectClass() // // Returns the database object class given the object type information // supplied by the implementation. The cache is searched first to see if an // object class has already been built. //--------------------------------------------------------------------------- _getDbObjectClass(objType) { if (objType.prototype instanceof BaseDbObject) return objType; let cls = this._dbObjectClasses.get(objType); if (!cls) { cls = this._buildDbObjectClass(objType); cls._connection = this; cls._objType = objType; objType._connection = this._impl; this._dbObjectClasses.set(objType, cls); } return (cls); } //--------------------------------------------------------------------------- // _getDbObjectClassForName() // // Returns the database object class given the name of the database object // type. The cache is searched first to see if an object class has already // been built. //--------------------------------------------------------------------------- async _getDbObjectClassForName(name) { let cls = this._dbObjectClasses.get(name); if (!cls) { const objType = await this._impl.getDbObjectClass(name); cls = this._getDbObjectClass(objType); this._dbObjectClasses.set(name, cls); } return cls; } //--------------------------------------------------------------------------- // _isBindDir() // // Returns a boolean indicating if the supplied value is a valid bind // direction. //--------------------------------------------------------------------------- _isBindDir(value) { return ( value === constants.BIND_IN || value === constants.BIND_OUT || value === constants.BIND_INOUT ); } //--------------------------------------------------------------------------- // _isBindValue() // // Returns a boolean indicating if the supplied value is one that can be // bound. //--------------------------------------------------------------------------- _isBindValue(value) { return ( value === null || value === undefined || typeof value === 'number' || typeof value === 'string' || typeof value === 'boolean' || typeof value === 'bigint' || Array.isArray(value) || nodbUtil.isVectorValue(value) || Buffer.isBuffer(value) || util.types.isDate(value) || value instanceof Lob || value instanceof ResultSet || value instanceof BaseDbObject ); } //--------------------------------------------------------------------------- // _processBindUnit() // // Processes a bind unit (object) supplied by the user and returns the value // stored in it (if one is). //--------------------------------------------------------------------------- async _processBindUnit(bindInfo, bindUnit, inExecuteMany) { let okBindUnit = false; // get and validate bind direction; if not specified, IN is assumed if (bindUnit.dir === undefined) { bindInfo.dir = constants.BIND_IN; } else { errors.assert(this._isBindDir(bindUnit.dir), errors.ERR_INVALID_BIND_DIRECTION); bindInfo.dir = bindUnit.dir; okBindUnit = true; } // get and validate bind type; it must be one of the integer constants // identifying types, a string identifying an object type or a constructor // function identifying an object type if (bindUnit.type !== undefined) { if (typeof bindUnit.type === 'string') { bindInfo.type = types.DB_TYPE_OBJECT; bindInfo.typeClass = await this._getDbObjectClassForName(bindUnit.type); bindInfo.objType = bindInfo.typeClass._objType; } else if (bindUnit.type.prototype instanceof BaseDbObject) { bindInfo.type = types.DB_TYPE_OBJECT; bindInfo.typeClass = bindUnit.type; bindInfo.objType = bindInfo.typeClass._objType; } else { errors.assert(bindUnit.type instanceof types.DbType, errors.ERR_INVALID_BIND_DATA_TYPE, 2); bindInfo.type = bindUnit.type; } okBindUnit = true; // when calling executeMany(), bind type is mandatory } else if (inExecuteMany) { if (bindInfo.name) errors.throwErr(errors.ERR_MISSING_TYPE_BY_NAME, bindInfo.name); errors.throwErr(errors.ERR_MISSING_TYPE_BY_POS, bindInfo.pos); } // get and validate the maximum size for strings/buffers; this value is // used for IN/OUT and OUT binds in execute() and at all times for // executeMany() if (bindInfo.dir !== constants.BIND_IN || inExecuteMany) { if (bindUnit.maxSize !== undefined) { errors.assertParamPropValue(Number.isInteger(bindUnit.maxSize) && bindUnit.maxSize > 0, 2, "maxSize"); bindInfo.maxSize = bindUnit.maxSize; bindInfo.checkSize = true; okBindUnit = true; } else if (inExecuteMany) { if (bindInfo.type === types.DB_TYPE_VARCHAR || bindInfo.type === types.DB_TYPE_RAW) { if (bindInfo.name) errors.throwErr(errors.ERR_MISSING_MAX_SIZE_BY_NAME, bindInfo.name); errors.throwErr(errors.ERR_MISSING_MAX_SIZE_BY_POS, bindInfo.pos); } } else { bindInfo.maxSize = constants.DEFAULT_MAX_SIZE_FOR_OUT_BINDS; } } // get max array size (for array binds, not possible in executeMany()) bindInfo.isArray = false; if (!inExecuteMany) { if (bindUnit.maxArraySize !== undefined) { errors.assertParamPropValue(Number.isInteger(bindUnit.maxArraySize) && bindUnit.maxArraySize > 0, 2, "maxArraySize"); bindInfo.maxArraySize = bindUnit.maxArraySize; bindInfo.isArray = true; } } // get the value, if specified (not used in executeMany()) if (!inExecuteMany && bindUnit.val !== undefined) { return bindUnit.val; } if (!okBindUnit) errors.throwErr(errors.ERR_INVALID_BIND_UNIT); } //--------------------------------------------------------------------------- // _processBindValue() // // Processes the bind value supplied by the caller. This performs all checks // on the value and normalizes it for use by the implementation class. If no // bind info has been defined yet, the value defines that. //--------------------------------------------------------------------------- async _processBindValue(bindInfo, value, options) { const transformed = transformer.transformValueIn(bindInfo, value, options); if (bindInfo.isArray) { bindInfo.values = transformed.concat(bindInfo.values.slice(transformed.length)); } else { bindInfo.values[options.pos] = transformed; } if (bindInfo.type === types.DB_TYPE_OBJECT && bindInfo.typeClass === undefined) { bindInfo.typeClass = await this._getDbObjectClass(value._objType); bindInfo.objType = bindInfo.typeClass._objType; } } //--------------------------------------------------------------------------- // _processExecuteBind() // // Processes a single execute bind supplied by the caller. This performs all // checks on the bind and normalizes it for use by the implementation class. //--------------------------------------------------------------------------- async _processExecuteBind(bindInfo, bindData) { // setup defaults bindInfo.isArray = false; // if bind data is a value that can be bound directly, use it; otherwise, // scan the bind unit for bind information and its value let bindValue; if (this._isBindValue(bindData)) { bindInfo.dir = constants.BIND_IN; bindValue = bindData; } else { bindValue = await this._processBindUnit(bindInfo, bindData, false); } // for IN and IN/OUT binds, process the value if (bindInfo.dir !== constants.BIND_OUT) { const options = {pos: 0, allowArray: true}; await this._processBindValue(bindInfo, bindValue, options); } // if only null values were found (or an OUT bind was specified), type // information may not be set, so complete bind information as a string // and set the maxSize to 1 if it has not already been set if (bindInfo.type === undefined) { bindInfo.type = types.DB_TYPE_VARCHAR; if (bindInfo.maxSize === undefined) bindInfo.maxSize = 1; } // check valid bind type for array binds if (bindInfo.isArray && bindInfo.type !== types.DB_TYPE_VARCHAR && bindInfo.type !== types.DB_TYPE_NVARCHAR && bindInfo.type !== types.DB_TYPE_CHAR && bindInfo.type !== types.DB_TYPE_NCHAR && bindInfo.type !== types.DB_TYPE_NUMBER && bindInfo.type !== types.DB_TYPE_BINARY_FLOAT && bindInfo.type !== types.DB_TYPE_BINARY_DOUBLE && bindInfo.type !== types.DB_TYPE_DATE && bindInfo.type !== types.DB_TYPE_TIMESTAMP && bindInfo.type !== types.DB_TYPE_TIMESTAMP_LTZ && bindInfo.type !== types.DB_TYPE_TIMESTAMP_TZ && bindInfo.type !== types.DB_TYPE_RAW && bindInfo.type !== types.DB_TYPE_INTERVAL_YM && bindInfo.type !== types.DB_TYPE_INTERVAL_DS) { errors.throwErr(errors.ERR_INVALID_TYPE_FOR_ARRAY_BIND); } } //--------------------------------------------------------------------------- // _processExecuteBinds() // // Processes the binds supplied by the caller. This performs all checks on // the binds and normalizes them for use by the implementation class. //--------------------------------------------------------------------------- async _processExecuteBinds(binds) { const normBinds = []; if (Array.isArray(binds)) { for (let i = 0; i < binds.length; i++) { const bindInfo = normBinds[i] = {pos: i + 1, values: []}; await this._processExecuteBind(bindInfo, binds[i]); } } else { errors.assertParamValue(nodbUtil.isObject(binds), 2); const bindNames = Object.getOwnPropertyNames(binds); for (let i = 0; i < bindNames.length; i++) { const bindInfo = normBinds[i] = {name: bindNames[i], values: []}; await this._processExecuteBind(bindInfo, binds[bindNames[i]]); } } return normBinds; } //--------------------------------------------------------------------------- // _processExecuteManyBinds() // // Processes the binds supplied by the caller. This performs all checks on // the binds and normalizes them for use by the implementation class. //--------------------------------------------------------------------------- async _processExecuteManyBinds(binds, bindDefs) { const normBinds = []; let byPosition; // transform bindDefs into normalized binds, if available if (bindDefs !== undefined) { if (Array.isArray(bindDefs)) { byPosition = true; for (let i = 0; i < bindDefs.length; i++) { const bindInfo = normBinds[i] = {pos: i + 1, values: []}; await this._processBindUnit(bindInfo, bindDefs[i], true); } } else { byPosition = false; const bindNames = Object.getOwnPropertyNames(bindDefs); for (let i = 0; i < bindNames.length; i++) { const bindInfo = normBinds[i] = {name: bindNames[i], values: []}; await this._processBindUnit(bindInfo, bindDefs[bindNames[i]], true); } } // otherwise, use the first row to determine the binds to use } else { const row = binds[0]; errors.assertParamValue(nodbUtil.isObjectOrArray(row), 2); if (Array.isArray(row)) { byPosition = true; for (let i = 0; i < row.length; i++) { normBinds[i] = {pos: i + 1}; } } else { byPosition = false; const bindNames = Object.getOwnPropertyNames(row); for (let i = 0; i < bindNames.length; i++) { normBinds[i] = {name: bindNames[i]}; } } for (let i = 0; i < normBinds.length; i++) { normBinds[i].dir = constants.BIND_IN; normBinds[i].isArray = false; normBinds[i].values = []; } } // process each of the rows for (let i = 0; i < binds.length; i++) { const row = binds[i]; const options = {pos: i, allowArray: false}; errors.assert((byPosition && Array.isArray(row)) || (!byPosition && nodbUtil.isObject(row)), errors.ERR_MIXED_BIND); for (let j = 0; j < normBinds.length; j++) { const bindInfo = normBinds[j]; const value = (byPosition) ? row[j] : row[bindInfo.name]; await this._processBindValue(bindInfo, value, options); } } // set bind type and size to a string of size 1 if no bind type was // specified (and all values are null) for (let i = 0; i < normBinds.length; i++) { const bindInfo = normBinds[i]; if (bindInfo.type === undefined) { bindInfo.type = types.DB_TYPE_VARCHAR; bindInfo.maxSize = 1; } } return normBinds; } //--------------------------------------------------------------------------- // _transformOutBind() // // Transform an output bind value from an implementation value to a user // facing value (for result sets and LOBs). DML returning output variables // are always an array of values. //--------------------------------------------------------------------------- _transformOutBind(val, options) { let outVal = val; if (Array.isArray(val)) { outVal = []; for (let i = 0; i < val.length; i++) outVal.push(this._transformOutBind(val[i], options)); } else if (val instanceof impl.ResultSetImpl) { outVal = new ResultSet(); outVal._setup(this, val); } else if (val instanceof impl.LobImpl) { outVal = new Lob(); outVal._setup(val, true); } else if (val instanceof impl.DbObjectImpl) { const cls = this._dbObjectClasses.get(val._objType); outVal = Object.create(cls.prototype); outVal._impl = val; if (options.dbObjectAsPojo) { outVal = outVal._toPojo(); } else if (outVal.isCollection) { outVal = new Proxy(outVal, BaseDbObject._collectionProxyHandler); } } return outVal; } //--------------------------------------------------------------------------- // _verifyExecOpts // // Verify that the value passed by the user for binds is acceptable. Perform // any transformations necessary. //--------------------------------------------------------------------------- _verifyExecOpts(options, inExecuteMany) { // define normalized options (value returned to caller) const outOptions = {}; // handle common options errors.assertParamValue(nodbUtil.isObject(options), 3); // autoCommit must be a boolean value if (options.autoCommit !== undefined) { errors.assertParamPropValue(typeof options.autoCommit === 'boolean', 3, "autoCommit"); outOptions.autoCommit = options.autoCommit; } // dbObjectAsPojo must be a boolean value if (options.dbObjectAsPojo !== undefined) { errors.assertParamPropValue(typeof options.dbObjectAsPojo === 'boolean', 3, "dbObjectAsPojo"); outOptions.dbObjectAsPojo = options.dbObjectAsPojo; } // keepInStmtCache must be a boolean value if (options.keepInStmtCache !== undefined) { errors.assertParamPropValue(typeof options.keepInStmtCache === 'boolean', 3, "keepInStmtCache"); outOptions.keepInStmtCache = options.keepInStmtCache; } if (options.suspendOnSuccess !== undefined) { errors.assertParamPropBool(options, 2, "suspendOnSucess"); outOptions.suspendOnSuccess = options.suspendOnSuccess; } // handle options specific to executeMany() if (inExecuteMany) { // bindDefs must be an object or array if (options.bindDefs !== undefined) { errors.assertParamPropValue(nodbUtil.isObjectOrArray(options.bindDefs), 3, "bindDefs"); outOptions.bindDefs = options.bindDefs; } // batchErrors must be a boolean value if (options.batchErrors !== undefined) { errors.assertParamPropValue(typeof options.batchErrors === 'boolean', 3, "batchErrors"); outOptions.batchErrors = options.batchErrors; } // dmlRowCounts must be a boolean value if (options.dmlRowCounts !== undefined) { errors.assertParamPropValue(typeof options.dmlRowCounts === 'boolean', 3, "dmlRowCounts"); outOptions.dmlRowCounts = options.dmlRowCounts; } // handle options specific to execute() } else { // fetchArraySize must be a positive integer errors.assertParamPropUnsignedIntNonZero(options, 3, "fetchArraySize"); outOptions.fetchArraySize = options.fetchArraySize; // fetchInfo must be an object with keys containing an object with a // "type" property; these are converted to an array of objects for ease // of processing by the implementation if (options.fetchInfo !== undefined) { errors.assertParamPropValue(nodbUtil.isObject(options.fetchInfo), 3, "fetchInfo"); const names = Object.getOwnPropertyNames(options.fetchInfo); const map = new Map(settings.fetchTypeMap); for (const name of names) { const info = options.fetchInfo[name]; if (info.type === undefined) errors.throwErr(errors.ERR_NO_TYPE_FOR_CONVERSION); if (info.type !== constants.DEFAULT && info.type !== types.DB_TYPE_VARCHAR && info.type !== types.DB_TYPE_RAW) { errors.throwErr(errors.ERR_INVALID_TYPE_FOR_CONVERSION); } map.set(name, info.type); } outOptions.fetchTypeMap = map; } // fetchTypeHandler must be a function which is called for each column to // be fetched and accepts the metadata for a column if (options.fetchTypeHandler !== undefined) { const type = (typeof options.fetchTypeHandler); errors.assertParamPropValue(type === 'function', 3, "fetchTypeHandler"); outOptions.fetchTypeHandler = options.fetchTypeHandler; } // maxRows must be a positive integer (or 0) if (options.maxRows !== undefined) { errors.assertParamPropValue(Number.isInteger(options.maxRows) && options.maxRows >= 0, 3, "maxRows"); outOptions.maxRows = options.maxRows; } // outFormat must be one of the two possible constants if (options.outFormat !== undefined) { errors.assertParamPropValue( options.outFormat === constants.OUT_FORMAT_ARRAY || options.outFormat === constants.OUT_FORMAT_OBJECT, 3, "outFormat"); outOptions.outFormat = options.outFormat; } // prefetchRows must be a positive integer (or 0) if (options.prefetchRows !== undefined) { errors.assertParamPropValue(Number.isInteger(options.prefetchRows) && options.prefetchRows >= 0, 3, "prefetchRows"); outOptions.prefetchRows = options.prefetchRows; } // resultSet must be a boolean value if (options.resultSet !== undefined) { errors.assertParamPropValue(typeof options.resultSet === 'boolean', 3, "resultSet"); outOptions.resultSet = options.resultSet; } } return outOptions; } //--------------------------------------------------------------------------- // action // // Property for end-to-end tracing attribute. //--------------------------------------------------------------------------- get action() { return null; } set action(value) { errors.assertPropValue(typeof value === 'string', "action"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setAction(value); } //--------------------------------------------------------------------------- // beginSessionlessTransaction() // // Begin a new sessionless transaction with provided transactionId. // If transactionId wasn't provided a random-generated transactionId will be // used and returned. //--------------------------------------------------------------------------- async beginSessionlessTransaction(options = {}) { errors.assertArgCount(arguments, 0, 1); errors.assertParamValue(nodbUtil.isObject(options), 1); if (options.transactionId !== undefined) errors.assertParamPropValue( nodbUtil.isTransactionId(options.transactionId), 1, 'transactionId'); errors.assertParamPropUnsignedIntNonZero(options, 1, 'timeout'); errors.assertParamPropBool(options, 1, 'deferRoundTrip'); const normalizedTransactionId = nodbUtil.normalizeTransactionId(options.transactionId); const {timeout = 60, deferRoundTrip = false} = options; await this._impl.startSessionlessTransaction(normalizedTransactionId, timeout, constants.TPC_BEGIN_NEW, deferRoundTrip); return normalizedTransactionId; } //--------------------------------------------------------------------------- // breakExecution() // // Breaks execution of a running statement. //--------------------------------------------------------------------------- async breakExecution() { errors.assertArgCount(arguments, 0, 0); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); await this._impl.breakExecution(); } //--------------------------------------------------------------------------- // callTimeout // // Property for round-trip timeouts. //--------------------------------------------------------------------------- get callTimeout() { if (this._impl) return this._impl.getCallTimeout(); return undefined; } set callTimeout(value) { errors.assertPropValue(Number.isInteger(value) && value >= 0, "callTimeout"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setCallTimeout(value); } //--------------------------------------------------------------------------- // changePassword() // // Changes the password of the specified user. //--------------------------------------------------------------------------- async changePassword(user, password, newPassword) { errors.assertArgCount(arguments, 3, 3); errors.assertParamValue(typeof user === 'string', 1); errors.assertParamValue(typeof password === 'string', 2); errors.assertParamValue(typeof newPassword === 'string', 3); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); await this._impl.changePassword(user, password, newPassword); } //--------------------------------------------------------------------------- // clientId // // Property for end-to-end tracing attribute. //--------------------------------------------------------------------------- get clientId() { return null; } set clientId(value) { errors.assertPropValue(typeof value === 'string', "clientId"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setClientId(value); } //--------------------------------------------------------------------------- // clientInfo // // Property for end-to-end tracing attribute. //--------------------------------------------------------------------------- get clientInfo() { return null; } set clientInfo(value) { errors.assertPropValue(typeof value === 'string', "clientInfo"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setClientInfo(value); } //--------------------------------------------------------------------------- // close() // // Closes the connection and makes it unusable for further work. //--------------------------------------------------------------------------- async close(a1) { let options = {}; errors.assertArgCount(arguments, 0, 1); if (arguments.length == 1) { errors.assertParamValue(nodbUtil.isObject(a1), 1); options = a1; errors.assertParamPropBool(options, 1, "drop"); } errors.assert(this._impl && !this._closing, errors.ERR_INVALID_CONNECTION); this._closing = true; try { // If connection is part of a pool, notify the pool of its availability if (this._pool) { await this._pool._release(this._impl, options); } else { await this._impl.close(options); } } finally { this._closing = false; } delete this._impl; if (!this.thin) { for (const cls of this._dbObjectClasses.values()) { cls._objType.refCleanup(); } } this._dbObjectClasses.clear(); } //--------------------------------------------------------------------------- // commit() // // Commits the current transaction. //--------------------------------------------------------------------------- async commit() { errors.assertArgCount(arguments, 0, 0); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); await this._impl.commit(); } //--------------------------------------------------------------------------- // createLob() // // Creates a temporary LOB and returns it to the caller. //--------------------------------------------------------------------------- async createLob(type) { errors.assertArgCount(arguments, 1, 1); errors.assertParamValue(type === types.DB_TYPE_CLOB || type === types.DB_TYPE_BLOB || type === types.DB_TYPE_NCLOB, 1); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); const lob = new Lob(); lob._setup(await this._impl.createLob(type), false); return lob; } //--------------------------------------------------------------------------- // currentSchema // // Property for identifying the current schema to use in the database. //--------------------------------------------------------------------------- get currentSchema() { if (this._impl) return this._impl.getCurrentSchema(); return undefined; } set currentSchema(value) { errors.assertPropValue(typeof value === 'string', "currentSchema"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setCurrentSchema(value); } //--------------------------------------------------------------------------- // dbOp // // Property for end-to-end tracing attribute. //--------------------------------------------------------------------------- get dbOp() { return null; } set dbOp(value) { errors.assertPropValue(typeof value === 'string', "dbOp"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setDbOp(value); } //--------------------------------------------------------------------------- // thin() // // return true, if driver mode is thin while acquiring connection // return false, if driver mode is thick while acquiring connection //--------------------------------------------------------------------------- get thin() { return settings.thin; } //--------------------------------------------------------------------------- // ecid // // Property for end-to-end tracing attribute. //--------------------------------------------------------------------------- get ecid() { return null; } set ecid(value) { errors.assertPropValue(typeof value === 'string', "ecid"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setECID(value); } //--------------------------------------------------------------------------- // decode() // // Decodes OSON Buffer to JS data type. //--------------------------------------------------------------------------- decodeOSON(buf) { errors.assertArgCount(arguments, 1, 1); errors.assertParamValue(Buffer.isBuffer(buf), 1); const decoder = new oson.OsonDecoder(buf); return decoder.decode(); } //--------------------------------------------------------------------------- // encode() // // Encodes the JS value into OSON bytes. //--------------------------------------------------------------------------- encodeOSON(value) { const encoder = new oson.OsonEncoder(); return encoder.encode(transformer.transformJsonValue(value), this._impl._osonMaxFieldNameSize); } //--------------------------------------------------------------------------- // execute() // // Executes a SQL statement and returns the results. //--------------------------------------------------------------------------- async execute(sql, a2, a3) { const numIters = 1; let binds = []; let options = {}; errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); // process arguments if (nodbUtil.isObject(sql) && typeof sql.statement === 'string') { errors.assertArgCount(arguments, 1, 2); if (sql.values) { if (this._impl._callLevelTraceData) { this._impl._callLevelTraceData.values = sql.values; } binds = await this._processExecuteBinds(sql.values); } sql = sql.statement; if (arguments.length == 2) { options = this._verifyExecOpts(a2, false); } } else { errors.assertArgCount(arguments, 1, 3); errors.assertParamValue(typeof sql === 'string', 1); if (arguments.length >= 2) { if (this._impl._callLevelTraceData) { this._impl._callLevelTraceData.values = a2; } binds = await this._processExecuteBinds(a2); } if (arguments.length == 3) { options = this._verifyExecOpts(a3, false); } } this._addDefaultsToExecOpts(options); // perform actual execute let result; try { if (this._impl._callLevelTraceData) { this._impl._callLevelTraceData.statement = sql; } result = await this._impl.execute(sql, numIters, binds, options, false); } catch (err) { if (err.errorNum === 1406) errors.throwErr(errors.ERR_INSUFFICIENT_BUFFER_FOR_BINDS); throw err; } // convert ORA errors to NJS if (result.warning) { result.warning = errors.transformErr(result.warning); } // process queries; if a result set is not desired, fetch all of the rows // from the result set and then destroy the result set if (result.resultSet !== undefined) { const resultSet = new ResultSet(); resultSet._setup(this, result.resultSet); result.metaData = resultSet._impl.metaData; if (options.resultSet) { result.resultSet = resultSet; } else { result.rows = await resultSet._getAllRows(); delete result.resultSet; } } // process output binds if (result.outBinds !== undefined) { for (const [key, value] of Object.entries(result.outBinds)) { const val = this._transformOutBind(value, options); result.outBinds[key] = val; } } // process implicit results; ensure all implicit results have their fetch // array size fixed, or, if a result set is not requested, that all rows // are fetched if (result.implicitResults) { for (const [key, impl] of Object.entries(result.implicitResults)) { const resultSet = new ResultSet(); resultSet._setup(this, impl); if (options.resultSet) { result.implicitResults[key] = resultSet; } else { result.implicitResults[key] = await resultSet._getAllRows(); } } } return (result); } //--------------------------------------------------------------------------- // executeMany() // // Executes a SQL statement multiple times and returns the results. //--------------------------------------------------------------------------- async executeMany(sql, bindsOrNumIters, a3) { let options = {}; let binds = []; let numIters; errors.assertArgCount(arguments, 2, 3); errors.assertParamValue(typeof sql === 'string', 1); if (arguments.length == 3) { options = this._verifyExecOpts(a3, true); } this._addDefaultsToExecOpts(options); if (typeof bindsOrNumIters === 'number') { errors.assertParamValue(Number.isInteger(bindsOrNumIters) && bindsOrNumIters > 0, 2); numIters = bindsOrNumIters; if (options.bindDefs !== undefined) { binds = await this._processExecuteManyBinds([], options.bindDefs); } } else { errors.assertParamValue(Array.isArray(bindsOrNumIters) && bindsOrNumIters.length > 0, 2); numIters = bindsOrNumIters.length; binds = await this._processExecuteManyBinds(bindsOrNumIters, options.bindDefs); } errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); const result = await this._impl.execute(sql, numIters, binds, options, true); // convert ORA warnings to NJS if (result.warning) { result.warning = errors.transformErr(result.warning); } // process output binds if (result.outBinds !== undefined) { for (let i = 0; i < result.outBinds.length; i++) { const outBind = result.outBinds[i]; for (const [key, value] of Object.entries(outBind)) { outBind[key] = this._transformOutBind(value, options); } } } return result; } //--------------------------------------------------------------------------- // externalName // // Property for identifying the external name to use in TPC logging. //--------------------------------------------------------------------------- get externalName() { if (this._impl) return this._impl.getExternalName(); return undefined; } set externalName(value) { errors.assertPropValue(typeof value === 'string', "externalName"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setExternalName(value); } //--------------------------------------------------------------------------- // dbDomain (READONLY) // // Property for identifying the dbDomain of the Oracle Database. //--------------------------------------------------------------------------- get dbDomain() { return this._impl && this._impl.getDbDomain(); } //--------------------------------------------------------------------------- // dbName (READONLY) // // Property for identifying the dbName of the Oracle Database. //--------------------------------------------------------------------------- get dbName() { return this._impl && this._impl.getDbName(); } //--------------------------------------------------------------------------- // hostName (READONLY) // // Property for identifying the hostName of the Oracle Database. //--------------------------------------------------------------------------- get hostName() { return this._impl && this._impl.getHostName(); } //--------------------------------------------------------------------------- // port (READONLY) // // Property for identifying the port to which client is connected. //--------------------------------------------------------------------------- get port() { return this._impl && this._impl.getPort(); } //--------------------------------------------------------------------------- // protocol (READONLY) // // Property for identifying the protocol used to connect to Oracle Database. //--------------------------------------------------------------------------- get protocol() { return this._impl && this._impl.getProtocol(); } //--------------------------------------------------------------------------- // connectString (READONLY) // // User provided connectString to connect to Oracle Database. //--------------------------------------------------------------------------- get connectString() { return this._impl && this._impl._connectString; } //--------------------------------------------------------------------------- // user // // User property provided to connect to Oracle Database. //--------------------------------------------------------------------------- get user() { if (this.currentSchema.length) { return this.currentSchema; } return this._impl && this._impl._user; } //--------------------------------------------------------------------------- // connectTraceConfig // // Property for getting the connection related config. //--------------------------------------------------------------------------- get connectTraceConfig() { return this._impl && this._impl._getConnectTraceConfig(); } //--------------------------------------------------------------------------- // getDbObjectClass() // // Returns a database object class given its name. The cache is searched // first, but if not found, the database is queried and the result is cached // using the type information (as well as the name for easier lookup later). //--------------------------------------------------------------------------- async getDbObjectClass(name) { errors.assertArgCount(arguments, 1, 1); errors.assertParamValue(typeof name === 'string', 1); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); return await this._getDbObjectClassForName(name); } //--------------------------------------------------------------------------- // getQueue() // // Returns a queue with the specified name. //--------------------------------------------------------------------------- async getQueue(name, a2) { let options = {}; errors.assertArgCount(arguments, 1, 2); errors.assertParamValue(typeof name === 'string', 1); if (arguments.length == 2) { errors.assertParamValue(nodbUtil.isObject(a2), 2); options = {...a2}; } errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); const queue = new AqQueue(); await queue.create(this, name, options); return queue; } //--------------------------------------------------------------------------- // getSodaDatabase() // // Returns a SodaDatabase object (high-level SODA object associated with // the current connection). //--------------------------------------------------------------------------- getSodaDatabase() { errors.assertArgCount(arguments, 0, 0); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); const sodaDb = new SodaDatabase(); sodaDb._impl = this._impl.getSodaDatabase(); return sodaDb; } //--------------------------------------------------------------------------- // getStatementInfo() // // Returns information about the statement. //--------------------------------------------------------------------------- async getStatementInfo(sql) { errors.assertArgCount(arguments, 1, 1); errors.assertParamValue(typeof sql === 'string', 1); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); const info = await this._impl.getStatementInfo(sql); if (info.metaData) { for (let i = 0; i < info.metaData.length; i++) { const m = info.metaData[i]; nodbUtil.addTypeProperties(m, "dbType"); m.fetchType = types.DB_TYPE_FETCH_TYPE_MAP.get(m.dbType); } } return info; } //--------------------------------------------------------------------------- // instanceName // // Returns the Oracle Database instance name associated with the connection. // This is the equivalent of the SQL expression: // sys_context('userenv', 'instance_name') //--------------------------------------------------------------------------- get instanceName() { if (this._impl) return this._impl.getInstanceName(); return undefined; } //--------------------------------------------------------------------------- // internalName // // Property for identifying the internal name to use in TPC logging. //--------------------------------------------------------------------------- get internalName() { if (this._impl) return this._impl.getInternalName(); return undefined; } set internalName(value) { errors.assertPropValue(typeof value === 'string', "internalName"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setInternalName(value); } //--------------------------------------------------------------------------- // ltxid // // Property for identifying the logical transaction ID to avoid duplicate // transactions. Used with Oracle Transaction Guard. //--------------------------------------------------------------------------- get ltxid() { if (this._impl) return this._impl.getLTXID(); return undefined; } //-------------------------------------------------------------------------- // isHealthy() // // Returns the health status of the connection. If this function returns // false, the caller should close the connection. //--------------------------------------------------------------------------- isHealthy() { return (this._impl !== undefined && !this._closing && this._impl.isHealthy()); } isCompressionEnabled() { return (this._impl && this._impl.isCompressionEnabled()); } //--------------------------------------------------------------------------- // maxIdentifierLength // // Returns the maximum length of identifiers supported by the database to // which this connection has been established. //--------------------------------------------------------------------------- get maxIdentifierLength() { return this._impl && this._impl.getMaxIdentifierLength(); } //--------------------------------------------------------------------------- // maxOpenCursors // // Returns maximum number of cursors that can be opened in one session. //--------------------------------------------------------------------------- get maxOpenCursors() { return this._impl && this._impl.getMaxOpenCursors(); } //--------------------------------------------------------------------------- // warning // // Returns warningInfo. //--------------------------------------------------------------------------- get warning() { let warning = this._impl.getWarning(); if (warning) { // Make sure that warning code attribute is populated and ORA error // is converted to NJS, if required warning = errors.transformErr(warning); } return this._impl && warning; } //--------------------------------------------------------------------------- // module // // Property for end-to-end tracing attribute. //--------------------------------------------------------------------------- get module() { return null; } set module(value) { errors.assertPropValue(typeof value === 'string', "module"); errors.assert(this._impl, errors.ERR_INVALID_CONNECTION); this._impl.setModule(value); }