@natlibfi/oracledb-aleph
Version:
A Node.js module for Oracle Database access from JavaScript and TypeScript
1,373 lines (1,242 loc) • 66.8 kB
JavaScript
// 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);
}