1348 lines
45 KiB
JavaScript
1348 lines
45 KiB
JavaScript
// Copyright (c) 2022, 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 { Buffer } = require('buffer');
|
|
const ConnectionImpl = require('../impl/connection.js');
|
|
const ThinResultSetImpl = require('./resultSet.js');
|
|
const ThinLobImpl = require("./lob.js");
|
|
const Protocol = require("./protocol/protocol.js");
|
|
const { BaseBuffer } = require('../impl/datahandlers/buffer.js');
|
|
const {NetworkSession: nsi} = require("./sqlnet/networkSession.js");
|
|
const { Statement } = require("./statement");
|
|
const thinUtil = require('./util');
|
|
const sqlNetConstants = require('./sqlnet/constants.js');
|
|
const constants = require('./protocol/constants.js');
|
|
const process = require('process');
|
|
const types = require('../types.js');
|
|
const errors = require("../errors.js");
|
|
const messages = require('./protocol/messages');
|
|
const StatementCache = require('./statementCache.js');
|
|
|
|
const finalizationRegistry = new global.FinalizationRegistry((heldValue) => {
|
|
heldValue.disconnect();
|
|
});
|
|
|
|
class TDSBuffer extends BaseBuffer {
|
|
}
|
|
|
|
class ThinConnectionImpl extends ConnectionImpl {
|
|
|
|
/**
|
|
* Terminates the connection
|
|
*
|
|
* @return {Promise}
|
|
*/
|
|
async close() {
|
|
try {
|
|
if (this._protocol.txnInProgress) {
|
|
if (this.tpcContext) {
|
|
const message = this.createTpcRollbackMessage();
|
|
await this._protocol._processMessage(message);
|
|
} else {
|
|
await this.rollback();
|
|
}
|
|
this.tpcContext = null;
|
|
}
|
|
if (this._drcpEnabled) {
|
|
await this._sessRelease();
|
|
this._drcpEstablishSession = true;
|
|
}
|
|
if (this._pool && !this._dropSess) {
|
|
await this._pool.release(this);
|
|
} else {
|
|
if (!this._drcpEnabled) {
|
|
const message = new messages.LogOffMessage(this);
|
|
await this._protocol._processMessage(message);
|
|
}
|
|
this.nscon.disconnect();
|
|
}
|
|
} catch (err) {
|
|
// immediate close of open socket on failure
|
|
// exception won't be thrown to user
|
|
this.nscon.disconnect(sqlNetConstants.NSFIMM);
|
|
}
|
|
}
|
|
|
|
async _sessRelease() {
|
|
const message = new messages.SessionReleaseMessage(this);
|
|
if (!this.isPooled()) {
|
|
message.sessReleaseMode = constants.DRCP_DEAUTHENTICATE;
|
|
}
|
|
await this._protocol._processMessage(message);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// _getElementTypeObj()
|
|
//
|
|
// Get the element type's object type. This is needed when processing
|
|
// collections with an object as the element type since this information is
|
|
// not available in the TDS.
|
|
//---------------------------------------------------------------------------
|
|
async _getElementTypeObj(info) {
|
|
const binds = [
|
|
{
|
|
name: "owner",
|
|
type: types.DB_TYPE_VARCHAR,
|
|
dir: constants.BIND_IN,
|
|
maxSize: 128,
|
|
values: [info.schema]
|
|
},
|
|
{
|
|
name: "name",
|
|
type: types.DB_TYPE_VARCHAR,
|
|
dir: constants.BIND_IN,
|
|
maxSize: 128,
|
|
values: [info.name]
|
|
},
|
|
{
|
|
name: "package_name",
|
|
type: types.DB_TYPE_VARCHAR,
|
|
dir: constants.BIND_IN,
|
|
maxSize: 128,
|
|
values: [info.packageName]
|
|
}
|
|
];
|
|
let sql;
|
|
if (info.packageName) {
|
|
sql = `
|
|
select
|
|
elem_type_owner,
|
|
elem_type_name,
|
|
elem_type_package
|
|
from all_plsql_coll_types
|
|
where owner = :owner
|
|
and type_name = :name
|
|
and package_name = :package_name`;
|
|
} else {
|
|
binds.pop();
|
|
sql = `
|
|
select
|
|
elem_type_owner,
|
|
elem_type_name
|
|
from all_coll_types
|
|
where owner = :owner
|
|
and type_name = :name`;
|
|
}
|
|
const options = {
|
|
connection: { _impl: this },
|
|
prefetchRows: 2
|
|
};
|
|
const result = await this.execute(sql, 1, binds, options, false);
|
|
const rows = await result.resultSet.getRows(1, options);
|
|
await result.resultSet.close();
|
|
const row = rows[0];
|
|
info.elementTypeClass = this._getDbObjectType(row[0], row[1], row[2]);
|
|
if (info.elementTypeClass.partial) {
|
|
this._partialDbObjectTypes.push(info.elementTypeClass);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// _execute()
|
|
//
|
|
// Calls the RPC that executes a SQL statement and returns the results.
|
|
//---------------------------------------------------------------------------
|
|
async _execute(statement, numIters, binds, options, executeManyFlag) {
|
|
|
|
// Throw error if executeMany is not called on a DML statement or PL/SQL
|
|
if (executeManyFlag && statement.isQuery) {
|
|
errors.throwErr(errors.ERR_EXECMANY_NOT_ALLOWED_ON_QUERIES);
|
|
}
|
|
// perform binds
|
|
const numStmtBinds = statement.bindInfoList.length;
|
|
const numUserBinds = binds.length;
|
|
if (numStmtBinds !== numUserBinds) {
|
|
if (!binds[0]?.name) {
|
|
// positional bind mismatch or zero binds
|
|
errors.throwErr(errors.ERR_WRONG_NUMBER_OF_BINDS, numStmtBinds, numUserBinds);
|
|
}
|
|
|
|
// named bind mismatch
|
|
// Find the number of distinct named bind placeholders and see
|
|
// if they match the bind values
|
|
const numDistinctStmtBinds = statement.bindInfoDict.size;
|
|
if (numDistinctStmtBinds !== numUserBinds)
|
|
errors.throwErr(errors.ERR_WRONG_NUMBER_OF_BINDS, numStmtBinds, numUserBinds);
|
|
}
|
|
|
|
for (let i = 0; i < binds.length; i++) {
|
|
await this._bind(statement, binds[i], i + 1);
|
|
}
|
|
if (statement.isPlSql && (options.batchErrors || options.dmlRowCounts)) {
|
|
errors.throwErr(errors.ERR_EXEC_MODE_ONLY_FOR_DML);
|
|
}
|
|
|
|
// send database request
|
|
const message = new messages.ExecuteMessage(this, statement, options);
|
|
message.numExecs = numIters;
|
|
message.arrayDmlRowCounts = options.dmlRowCounts;
|
|
message.batchErrors = options.batchErrors;
|
|
|
|
// if a PL/SQL statement requires a full execute, perform only a single
|
|
// iteration in order to allow the determination of input/output binds
|
|
// to be completed; after that, an execution of the remaining iterations
|
|
// can be performed.
|
|
if (statement.isPlSql && (statement.cursorId === 0 ||
|
|
statement.requiresFullExecute)) {
|
|
message.numExecs = 1;
|
|
message.noImplicitRelease = true;
|
|
await this._protocol._processMessage(message);
|
|
statement.requiresFullExecute = false;
|
|
message.numExecs = numIters - 1;
|
|
message.offset = 1;
|
|
message.noImplicitRelease = false;
|
|
}
|
|
if (message.numExecs > 0) {
|
|
await this._protocol._processMessage(message);
|
|
statement.requiresFullExecute = false;
|
|
}
|
|
|
|
// if a define is required, send an additional request to the database
|
|
if (statement.requiresDefine && statement.sql) {
|
|
statement.requiresFullExecute = true;
|
|
await this._protocol._processMessage(message);
|
|
statement.requiresFullExecute = false;
|
|
statement.requiresDefine = false;
|
|
}
|
|
|
|
// process results
|
|
const result = {};
|
|
if (message.warning) {
|
|
result.warning = message.warning;
|
|
}
|
|
if (statement.numQueryVars > 0) {
|
|
result.resultSet = message.resultSet;
|
|
} else {
|
|
statement.bufferRowIndex = 0;
|
|
const outBinds = thinUtil.getOutBinds(statement, numIters,
|
|
executeManyFlag);
|
|
if (outBinds) {
|
|
result.outBinds = outBinds;
|
|
}
|
|
if (executeManyFlag) {
|
|
if (!statement.isPlSql) {
|
|
result.rowsAffected = statement.rowCount;
|
|
delete statement.rowCount;
|
|
}
|
|
if (options.dmlRowCounts) {
|
|
result.dmlRowCounts = options.dmlRowCounts;
|
|
}
|
|
if (options.batchErrors) {
|
|
result.batchErrors = options.batchErrors;
|
|
}
|
|
} else {
|
|
if (statement.isPlSql && options.implicitResultSet) {
|
|
result.implicitResults = options.implicitResultSet;
|
|
}
|
|
if (statement.lastRowid) {
|
|
result.lastRowid = statement.lastRowid;
|
|
delete statement.lastRowid;
|
|
}
|
|
if (statement.isPlSql) {
|
|
if (statement.rowCount) {
|
|
result.rowsAffected = statement.rowCount;
|
|
}
|
|
} else {
|
|
result.rowsAffected = statement.rowCount || 0;
|
|
}
|
|
if (statement.rowCount) {
|
|
delete statement.rowCount;
|
|
}
|
|
}
|
|
this._returnStatement(statement);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// _parseTDSAttr()
|
|
//
|
|
// Returns the DB type and fills metadata from the TDS buffer.
|
|
//---------------------------------------------------------------------------
|
|
_parseTDSAttr(buf, metaData) {
|
|
let oraTypeNum, csfrm, attrType;
|
|
|
|
// skip until a type code that is of interest
|
|
for (;;) {
|
|
attrType = buf.readUInt8();
|
|
if (attrType === constants.TNS_OBJ_TDS_TYPE_EMBED_ADT_INFO) {
|
|
buf.skipBytes(1); // flags
|
|
} else if (attrType !== constants.TNS_OBJ_TDS_TYPE_SUBTYPE_MARKER) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// process the type code.
|
|
switch (attrType) {
|
|
case constants.TNS_OBJ_TDS_TYPE_NUMBER:
|
|
metaData.precision = buf.readInt8();
|
|
metaData.scale = buf.readInt8();
|
|
return types.DB_TYPE_NUMBER;
|
|
case constants.TNS_OBJ_TDS_TYPE_FLOAT:
|
|
metaData.precision = buf.readInt8();
|
|
return types.DB_TYPE_NUMBER;
|
|
case constants.TNS_OBJ_TDS_TYPE_VARCHAR:
|
|
case constants.TNS_OBJ_TDS_TYPE_CHAR:
|
|
metaData.maxSize = buf.readUInt16BE(); // maximum length
|
|
oraTypeNum = (attrType === constants.TNS_OBJ_TDS_TYPE_VARCHAR) ?
|
|
constants.TNS_DATA_TYPE_VARCHAR : constants.TNS_DATA_TYPE_CHAR;
|
|
csfrm = buf.readUInt8();
|
|
csfrm = csfrm & 0x7f;
|
|
buf.skipBytes(2); // character set
|
|
return types.getTypeByOraTypeNum(oraTypeNum, csfrm);
|
|
case constants.TNS_OBJ_TDS_TYPE_RAW:
|
|
metaData.maxSize = buf.readUInt16BE(); // maximum length
|
|
return types.DB_TYPE_RAW;
|
|
case constants.TNS_OBJ_TDS_TYPE_BINARY_FLOAT:
|
|
return types.DB_TYPE_BINARY_FLOAT;
|
|
case constants.TNS_OBJ_TDS_TYPE_BINARY_DOUBLE:
|
|
return types.DB_TYPE_BINARY_DOUBLE;
|
|
case constants.TNS_OBJ_TDS_TYPE_DATE:
|
|
return types.DB_TYPE_DATE;
|
|
case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP:
|
|
buf.skipBytes(1); // precision
|
|
return types.DB_TYPE_TIMESTAMP;
|
|
case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_LTZ:
|
|
buf.skipBytes(1); // precision
|
|
return types.DB_TYPE_TIMESTAMP_LTZ;
|
|
case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_TZ:
|
|
buf.skipBytes(1); // precision
|
|
return types.DB_TYPE_TIMESTAMP_TZ;
|
|
case constants.TNS_OBJ_TDS_TYPE_BOOLEAN:
|
|
return types.DB_TYPE_BOOLEAN;
|
|
case constants.TNS_OBJ_TDS_TYPE_CLOB:
|
|
return types.DB_TYPE_CLOB;
|
|
case constants.TNS_OBJ_TDS_TYPE_BLOB:
|
|
return types.DB_TYPE_BLOB;
|
|
case constants.TNS_OBJ_TDS_TYPE_OBJ:
|
|
buf.skipBytes(5);
|
|
return types.DB_TYPE_OBJECT;
|
|
case constants.TNS_OBJ_TDS_TYPE_START_EMBED_ADT:
|
|
// loop until end type, TNS_OBJ_TDS_TYPE_END_EMBED_ADT
|
|
// is received.
|
|
while (this._parseTDSAttr(buf, {}) !== 0) {
|
|
continue;
|
|
}
|
|
return types.DB_TYPE_OBJECT;
|
|
case constants.TNS_OBJ_TDS_TYPE_END_EMBED_ADT:
|
|
return 0;
|
|
default:
|
|
errors.throwErr(errors.ERR_TDS_TYPE_NOT_SUPPORTED, attrType);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// _parseTDS()
|
|
//
|
|
// Parses the TDS (type descriptor segment) for the type.
|
|
//---------------------------------------------------------------------------
|
|
async _parseTDS(tds, info) {
|
|
// parse initial TDS bytes
|
|
const buf = new TDSBuffer(tds);
|
|
buf.skipBytes(4); // end offset
|
|
buf.skipBytes(2); // version op code and version
|
|
buf.skipBytes(2); // unknown
|
|
|
|
// if the number of attributes exceeds 1, the type cannot refer to a
|
|
// collection, so nothing further needs to be done
|
|
const numAttrs = buf.readUInt16BE();
|
|
|
|
// continue parsing TDS bytes to discover if type refers to a collection
|
|
buf.skipBytes(1); // TDS attributes?
|
|
buf.skipBytes(1); // start ADT op code
|
|
buf.skipBytes(2); // ADT number (always zero)
|
|
buf.skipBytes(4); // offset to index table
|
|
|
|
// check to see if type refers to a collection (only one attribute is
|
|
// present in that case).
|
|
info.isCollection = false;
|
|
if (numAttrs === 1) {
|
|
const pos = buf.pos;
|
|
const attrType = buf.readUInt8();
|
|
if (attrType === constants.TNS_OBJ_TDS_TYPE_COLL) {
|
|
info.isCollection = true;
|
|
} else {
|
|
buf.pos = pos;
|
|
}
|
|
}
|
|
|
|
// Handle collections
|
|
if (info.isCollection) {
|
|
// continue parsing TDS to determine element type
|
|
const elementPos = buf.readUInt32BE();
|
|
info.maxNumElements = buf.readUInt32BE();
|
|
info.collectionType = buf.readUInt8();
|
|
if (info.collectionType === constants.TNS_OBJ_PLSQL_INDEX_TABLE) {
|
|
info.collectionFlags = constants.TNS_OBJ_HAS_INDEXES;
|
|
}
|
|
buf.pos = elementPos;
|
|
info.elementTypeInfo = {};
|
|
info.elementType = this._parseTDSAttr(buf, info.elementTypeInfo);
|
|
if (info.elementType === types.DB_TYPE_OBJECT) {
|
|
await this._getElementTypeObj(info);
|
|
if (info.elementTypeClass.isXmlType) {
|
|
info.elementType = types.DB_TYPE_XMLTYPE;
|
|
}
|
|
}
|
|
} else {
|
|
if (info.attributes) { // skip for XML type as it has no attributes.
|
|
for (const attr of info.attributes) {
|
|
this._parseTDSAttr(buf, attr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// _populateRowTypeInfo()
|
|
//
|
|
// Get column and datatype information in case of %ROWTYPE handling.
|
|
//---------------------------------------------------------------------------
|
|
async _populateRowTypeInfo(info, options, result) {
|
|
const getColumnsSQL = `
|
|
SELECT
|
|
column_name,
|
|
data_type,
|
|
data_type_owner,
|
|
case
|
|
when data_type in
|
|
('CHAR', 'NCHAR', 'VARCHAR2', 'NVARCHAR2', 'RAW')
|
|
then data_length
|
|
else 0
|
|
end,
|
|
nvl(data_precision, 0),
|
|
nvl(data_scale, 0)
|
|
from all_tab_cols
|
|
where owner = :owner
|
|
and table_name = :name
|
|
and hidden_column != 'YES'
|
|
order by column_id`;
|
|
|
|
const bindVal = [
|
|
{
|
|
name: "owner",
|
|
type: types.DB_TYPE_VARCHAR,
|
|
maxSize: 128,
|
|
dir: constants.BIND_IN,
|
|
values: [result.outBinds.schema],
|
|
},
|
|
{
|
|
name: "name",
|
|
type: types.DB_TYPE_VARCHAR,
|
|
maxSize: 128,
|
|
dir: constants.BIND_IN,
|
|
values: [info.name.substring(0, info.name.length - 8)]
|
|
}
|
|
];
|
|
const val = await this.execute(
|
|
getColumnsSQL, 1, bindVal, options, false
|
|
);
|
|
try {
|
|
const attrRows = await val.resultSet._getAllRows();
|
|
info.attributes = [];
|
|
for (const row of attrRows) {
|
|
const metaData = {
|
|
name: row[0],
|
|
dataType: row[1],
|
|
dataTypeOwner: row[2],
|
|
maxSize: row[3],
|
|
dataPrecision: row[4],
|
|
dataScale: row[5],
|
|
};
|
|
if (!metaData.dataTypeOwner) {
|
|
const startPos = row[1].indexOf('(');
|
|
const endPos = row[1].indexOf(')');
|
|
if (endPos > startPos) {
|
|
metaData.dataType = metaData.dataType.substring(0, startPos) +
|
|
metaData.dataType.substring(
|
|
endPos + 1, metaData.dataType.length
|
|
);
|
|
}
|
|
}
|
|
this._addAttr(info.attributes, metaData);
|
|
}
|
|
} finally {
|
|
val.resultSet.close();
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// _populateDbObjectTypeInfo()
|
|
//
|
|
// Poplates type information given the name of the type.
|
|
//---------------------------------------------------------------------------
|
|
async _populateDbObjectTypeInfo(name) {
|
|
|
|
// get type information from the database
|
|
const sql = `
|
|
declare
|
|
t_Instantiable varchar2(3);
|
|
t_SuperTypeOwner varchar2(128);
|
|
t_SuperTypeName varchar2(128);
|
|
t_SubTypeRefCursor sys_refcursor;
|
|
t_Pos pls_integer;
|
|
begin
|
|
:ret_val := dbms_pickler.get_type_shape(:full_name, :oid,
|
|
:version, :tds, t_Instantiable, t_SuperTypeOwner,
|
|
t_SuperTypeName, :attrs_rc, t_SubTypeRefCursor);
|
|
:package_name := null;
|
|
if substr(:full_name, length(:full_name) - 7) = '%ROWTYPE' then
|
|
t_Pos := instr(:full_name, '.');
|
|
:schema := substr(:full_name, 1, t_Pos - 1);
|
|
:name := substr(:full_name, t_Pos + 1);
|
|
else
|
|
begin
|
|
select owner, type_name
|
|
into :schema, :name
|
|
from all_types
|
|
where type_oid = :oid;
|
|
exception
|
|
when no_data_found then
|
|
begin
|
|
select owner, package_name, type_name
|
|
into :schema, :package_name, :name
|
|
from all_plsql_types
|
|
where type_oid = :oid;
|
|
exception
|
|
when no_data_found then
|
|
null;
|
|
end;
|
|
end;
|
|
end if;
|
|
end;`;
|
|
|
|
const binds = [
|
|
{
|
|
name: "full_name",
|
|
type: types.DB_TYPE_VARCHAR,
|
|
dir: constants.BIND_INOUT,
|
|
maxSize: 500,
|
|
values: [name]
|
|
},
|
|
{
|
|
name: "ret_val",
|
|
type: types.DB_TYPE_BINARY_INTEGER,
|
|
dir: constants.BIND_OUT,
|
|
values: []
|
|
},
|
|
{
|
|
name: "oid",
|
|
type: types.DB_TYPE_RAW,
|
|
maxSize: 16,
|
|
dir: constants.BIND_OUT,
|
|
values: []
|
|
},
|
|
{
|
|
name: "version",
|
|
type: types.DB_TYPE_BINARY_INTEGER,
|
|
dir: constants.BIND_OUT,
|
|
values: []
|
|
},
|
|
{
|
|
name: "tds",
|
|
type: types.DB_TYPE_RAW,
|
|
maxSize: 2000,
|
|
dir: constants.BIND_OUT,
|
|
values: []
|
|
},
|
|
{
|
|
name: "attrs_rc",
|
|
type: types.DB_TYPE_CURSOR,
|
|
dir: constants.BIND_OUT,
|
|
values: []
|
|
},
|
|
{
|
|
name: "package_name",
|
|
type: types.DB_TYPE_VARCHAR,
|
|
maxSize: 128,
|
|
dir: constants.BIND_OUT,
|
|
values: []
|
|
},
|
|
{
|
|
name: "schema",
|
|
type: types.DB_TYPE_VARCHAR,
|
|
maxSize: 128,
|
|
dir: constants.BIND_OUT,
|
|
values: []
|
|
},
|
|
{
|
|
name: "name",
|
|
type: types.DB_TYPE_VARCHAR,
|
|
maxSize: 128,
|
|
dir: constants.BIND_OUT,
|
|
values: []
|
|
}
|
|
];
|
|
const options = {
|
|
connection: { _impl: this },
|
|
nullifyInvalidCursor: true
|
|
};
|
|
const result = await this.execute(sql, 1, binds, options, false);
|
|
if (result.outBinds.ret_val !== 0) {
|
|
errors.throwErr(errors.ERR_INVALID_OBJECT_TYPE_NAME, name);
|
|
}
|
|
|
|
try {
|
|
// check cache; if already present, nothing more to do!
|
|
const info = this._getDbObjectType(result.outBinds.schema,
|
|
result.outBinds.name, result.outBinds.package_name, result.outBinds.oid);
|
|
if (!info.partial) {
|
|
return info;
|
|
}
|
|
|
|
// process TDS and attributes cursor
|
|
if (info.name.endsWith('%ROWTYPE')) {
|
|
await this._populateRowTypeInfo(info, options, result);
|
|
} else {
|
|
info.version = result.outBinds.version;
|
|
const attrRows = await result.outBinds.attrs_rc._getAllRows();
|
|
if (attrRows.length > 0) {
|
|
// Its an object not a collection.
|
|
info.attributes = [];
|
|
for (const row of attrRows) {
|
|
const metaData = {
|
|
name: row[1],
|
|
dataType: row[3],
|
|
dataTypeOwner: row[4],
|
|
packageName: row[5],
|
|
oid: row[6]
|
|
};
|
|
this._addAttr(info.attributes, metaData);
|
|
}
|
|
|
|
}
|
|
await this._parseTDS(result.outBinds.tds, info);
|
|
}
|
|
info.partial = false;
|
|
return info;
|
|
} finally {
|
|
result.outBinds.attrs_rc.close();
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// _addAttr()
|
|
//
|
|
// Populates "attributes" object present in "attrList".
|
|
//---------------------------------------------------------------------------
|
|
_addAttr(attributes, attrInfo) {
|
|
const attr = { name: attrInfo.name };
|
|
if (attrInfo.dataTypeOwner) {
|
|
attr.type = types.DB_TYPE_OBJECT;
|
|
attr.typeClass = this._getDbObjectType(
|
|
attrInfo.dataTypeOwner,
|
|
attrInfo.dataType,
|
|
attrInfo.packageName,
|
|
attrInfo.oid
|
|
);
|
|
|
|
if (attr.typeClass.isXmlType) {
|
|
attr.type = types.DB_TYPE_XMLTYPE;
|
|
}
|
|
if (attr.typeClass.partial) {
|
|
this._partialDbObjectTypes.push(attr.typeClass);
|
|
}
|
|
} else {
|
|
attr.type = types.getTypeByColumnTypeName(attrInfo.dataType);
|
|
attr.maxSize = attrInfo.maxSize;
|
|
attr.precision = attrInfo.dataPrecision;
|
|
attr.scale = attrInfo.dataScale;
|
|
}
|
|
attributes.push(attr);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// _populatePartialDbObjectTypes()
|
|
//
|
|
// Populates partial types that were discovered earlier. Since populating an
|
|
// object type might result in additional object types being discovered,
|
|
// object types are popped from the partial types list until the list is
|
|
// empty.
|
|
//---------------------------------------------------------------------------
|
|
async _populatePartialDbObjectTypes() {
|
|
while (this._partialDbObjectTypes.length > 0) {
|
|
const info = this._partialDbObjectTypes.pop();
|
|
let suffix = "%ROWTYPE";
|
|
let name = info.name;
|
|
if (name.endsWith(suffix)) {
|
|
name = name.substring(0, name.length - suffix.length);
|
|
} else {
|
|
suffix = "";
|
|
}
|
|
let fullName;
|
|
if (info.packageName) {
|
|
fullName = `"${info.schema}"."${info.packageName}"."${name}"${suffix}`;
|
|
} else {
|
|
fullName = `"${info.schema}"."${name}"${suffix}`;
|
|
}
|
|
await this._populateDbObjectTypeInfo(fullName);
|
|
}
|
|
}
|
|
|
|
async commit() {
|
|
const message = new messages.CommitMessage(this);
|
|
await this._protocol._processMessage(message);
|
|
}
|
|
|
|
async breakExecution() {
|
|
await this._protocol.breakMessage();
|
|
}
|
|
|
|
isCompressionEnabled() {
|
|
return this.nscon.compressionEnabled;
|
|
}
|
|
isHealthy() {
|
|
try {
|
|
if (this.nscon.recvInbandNotif() === 0)
|
|
return true;
|
|
return false;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
isPooled() {
|
|
return (this._pool) ? true : false;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {object} params Configuration of the connection
|
|
*
|
|
* @return {Promise}
|
|
*/
|
|
async connect(params) {
|
|
if (!params.connectString) {
|
|
errors.throwErr(errors.ERR_EMPTY_CONNECT_STRING);
|
|
}
|
|
thinUtil.checkCredentials(params);
|
|
|
|
this.sessionID = 0;
|
|
this.serialNum = 0;
|
|
this.autoCommit = false;
|
|
this.serverVersion = "";
|
|
this.statementCache = null;
|
|
this.currentSchema = "";
|
|
this.invokeSessionCallback = true;
|
|
this.statementCacheSize = params.stmtCacheSize;
|
|
this._currentSchemaModified = false;
|
|
this._tempLobsToClose = [];
|
|
this._tempLobsTotalSize = 0;
|
|
this._drcpEstablishSession = false;
|
|
this._cclass = null;
|
|
this._clientIdentifier = "";
|
|
this._clientIdentifierModified = false;
|
|
this._action = "";
|
|
this._actionModified = false;
|
|
this._dbOp = "";
|
|
this._dbOpModified = false;
|
|
this._clientInfo = "";
|
|
this._clientInfoModified = false;
|
|
this._module = "";
|
|
this._moduleModified = false;
|
|
this._drcpEnabled = false;
|
|
this.serviceName = '';
|
|
this.remoteAddress = '';
|
|
this.comboKey = null; // used in changePassword API
|
|
this.tpcContext = null;
|
|
|
|
this.nscon = new nsi();
|
|
finalizationRegistry.register(this, this.nscon);
|
|
await this.nscon.connect(params);
|
|
|
|
let serverType;
|
|
if (this.isPooled()) {
|
|
serverType = params._connInfo[0];
|
|
this.serviceName = params._connInfo[2];
|
|
this.purity = params._connInfo[3] | constants.PURITY_DEFAULT;
|
|
this.sid = params._connInfo[4];
|
|
} else {
|
|
serverType = this.nscon.getOption(sqlNetConstants.SERVERTYPE);
|
|
this.serviceName = this.nscon.getOption(sqlNetConstants.SVCNAME);
|
|
this.sid = this.nscon.getOption(sqlNetConstants.SID);
|
|
this.purity = this.nscon.getOption(sqlNetConstants.PURITY) | constants.PURITY_DEFAULT;
|
|
}
|
|
if (serverType) {
|
|
this._drcpEnabled = serverType.toLowerCase() === 'pooled';
|
|
}
|
|
this.remoteAddress = this.nscon.getOption(sqlNetConstants.REMOTEADDR);
|
|
this.connectionClass = params.connectionClass;
|
|
|
|
/*
|
|
* if drcp is used, use purity = NEW as the default purity for
|
|
* standalone connections and purity = SELF for connections that belong
|
|
* to a pool
|
|
*/
|
|
if (this.purity === constants.PURITY_DEFAULT && this._drcpEnabled) {
|
|
if (this.isPooled()) {
|
|
this.purity = constants.PURITY_SELF;
|
|
} else {
|
|
this.purity = constants.PURITY_NEW;
|
|
}
|
|
}
|
|
|
|
this._protocol = new Protocol(this);
|
|
|
|
// check if the protocol version supported by the database is high
|
|
// enough; if not, reject the connection immediately
|
|
if (this._protocol.caps.protocolVersion < constants.TNS_VERSION_MIN_ACCEPTED) {
|
|
errors.throwErr(errors.ERR_SERVER_VERSION_NOT_SUPPORTED);
|
|
}
|
|
|
|
try {
|
|
const protocolMessage = new messages.ProtocolMessage(this);
|
|
const dataTypeMessage = new messages.DataTypeMessage(this);
|
|
const authMessage = new messages.AuthMessage(this, params);
|
|
if (this.nscon.supportsFastAuth) {
|
|
const fastAuthMessage = new messages.FastAuthMessage(this);
|
|
fastAuthMessage.protocolMessage = protocolMessage;
|
|
fastAuthMessage.dataTypeMessage = dataTypeMessage;
|
|
fastAuthMessage.authMessage = authMessage;
|
|
await this._protocol._processMessage(fastAuthMessage);
|
|
if (fastAuthMessage.reNegotiate) {
|
|
// Fast Authentication failed.
|
|
await this._protocol._processMessage(dataTypeMessage);
|
|
await this._protocol._processMessage(authMessage);
|
|
}
|
|
} else {
|
|
// When Fast Auth is explicitly disabled on servers > 23ai,
|
|
// we dont rely on message type TNS_MSG_TYPE_END_OF_REQUEST too
|
|
// for protocolMessage and dataTypeMessage.
|
|
const endOfRequestSupport = this.nscon.endOfRequestSupport;
|
|
this.nscon.endOfRequestSupport = false;
|
|
await this._protocol._processMessage(protocolMessage);
|
|
await this._protocol._processMessage(dataTypeMessage);
|
|
this.nscon.endOfRequestSupport = endOfRequestSupport;
|
|
await this._protocol._processMessage(authMessage);
|
|
}
|
|
if (!params.externalAuth) { // non-token Authentication
|
|
await this._protocol._processMessage(authMessage); // OAUTH
|
|
}
|
|
} catch (err) {
|
|
this.nscon.disconnect();
|
|
throw err;
|
|
}
|
|
|
|
this.statementCache = new StatementCache(this.statementCacheSize);
|
|
// maintain a list of partially populated database object types
|
|
this._partialDbObjectTypes = [];
|
|
|
|
if (params.debugJDWP) {
|
|
this.jdwpData = Buffer.from(params.debugJDWP);
|
|
} else if (process.env.ORA_DEBUG_JDWP) {
|
|
this.jdwpData = Buffer.from(process.env.ORA_DEBUG_JDWP);
|
|
}
|
|
this._protocol.connInProgress = false;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Return the statement to the statement cache, if applicable
|
|
//---------------------------------------------------------------------------
|
|
_returnStatement(statement) {
|
|
this.statementCache.returnStatement(statement);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Parses the sql statement and puts it into cache if keepInStmtCache
|
|
// option is true
|
|
//---------------------------------------------------------------------------
|
|
_prepare(sql, options) {
|
|
const statement = this._getStatement(sql, options.keepInStmtCache);
|
|
statement.bufferRowIndex = 0;
|
|
statement.bufferRowCount = 0;
|
|
statement.lastRowIndex = 0;
|
|
statement.moreRowsToFetch = true;
|
|
return statement;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Binds the values by user to the statement object
|
|
//---------------------------------------------------------------------------
|
|
async _bind(stmt, variable, pos = 0) {
|
|
const bindInfoDict = stmt.bindInfoDict;
|
|
const bindInfoList = stmt.bindInfoList;
|
|
|
|
/*
|
|
* For PL/SQL blocks, if the size of a string or bytes object exceeds
|
|
* 32,767 bytes it is converted to a BLOB/CLOB; and conversion
|
|
* needs to be established as well to return the string in the way that
|
|
* the user expects to get it
|
|
*/
|
|
if (stmt.isPlSql && variable.maxSize > 32767) {
|
|
if (variable.type === types.DB_TYPE_RAW ||
|
|
variable.type === types.DB_TYPE_LONG_RAW) {
|
|
variable.type = types.DB_TYPE_BLOB;
|
|
} else if (variable.type._csfrm === constants.CSFRM_NCHAR) {
|
|
variable.type = types.DB_TYPE_NCLOB;
|
|
} else {
|
|
variable.type = types.DB_TYPE_CLOB;
|
|
}
|
|
const maxSize = variable.maxSize;
|
|
delete variable.maxSize;
|
|
variable.outConverter = async function(val) {
|
|
if (val === null) {
|
|
return null;
|
|
}
|
|
const data = await val.getData();
|
|
const len = val._length;
|
|
if (data && len > maxSize) {
|
|
errors.throwErr(errors.ERR_INSUFFICIENT_BUFFER_FOR_BINDS);
|
|
}
|
|
return data;
|
|
};
|
|
}
|
|
|
|
if (variable.type === types.DB_TYPE_CLOB ||
|
|
variable.type === types.DB_TYPE_NCLOB ||
|
|
variable.type === types.DB_TYPE_BLOB) {
|
|
for (const [index, val] of variable.values.entries()) {
|
|
if (!(val instanceof ThinLobImpl)) {
|
|
if (val && val.length > 0) {
|
|
const lobImpl = new ThinLobImpl();
|
|
await lobImpl.create(this, variable.type);
|
|
await lobImpl.write(1, val);
|
|
variable.values[index] = lobImpl;
|
|
} else {
|
|
variable.values[index] = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (variable.name) {
|
|
let normalizedName;
|
|
if (variable.name.startsWith('"') && variable.name.endsWith('"')) {
|
|
normalizedName = variable.name.substring(1, variable.name.length - 1);
|
|
} else {
|
|
normalizedName = variable.name.toUpperCase();
|
|
}
|
|
if (normalizedName.startsWith(':')) {
|
|
normalizedName = variable.name.substring(1);
|
|
}
|
|
if (!bindInfoDict.has(normalizedName)) {
|
|
errors.throwErr(errors.ERR_INVALID_BIND_NAME, normalizedName);
|
|
}
|
|
bindInfoDict.get(normalizedName).forEach((bindInfo) => {
|
|
stmt._setVariable(bindInfo, variable);
|
|
});
|
|
} else {
|
|
const bindInfo = bindInfoList[pos - 1];
|
|
stmt._setVariable(bindInfo, variable);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// _createResultSet()
|
|
//
|
|
// Creates a result set and performs any necessary initialization.
|
|
//---------------------------------------------------------------------------
|
|
_createResultSet(options, statement) {
|
|
const resultSet = new ThinResultSetImpl();
|
|
if (!statement) {
|
|
statement = new Statement();
|
|
}
|
|
resultSet._resultSetNew(this, statement, options);
|
|
if (statement.queryVars.length > 0) {
|
|
const metadata = thinUtil.getMetadataMany(statement.queryVars);
|
|
resultSet._setup(options, metadata);
|
|
}
|
|
return resultSet;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// getDbObjectClass()
|
|
//
|
|
// Returns a database object class given its name.
|
|
//---------------------------------------------------------------------------
|
|
async getDbObjectClass(name) {
|
|
const info = await this._populateDbObjectTypeInfo(name);
|
|
await this._populatePartialDbObjectTypes();
|
|
return info;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// getStatementInfo()
|
|
//
|
|
// Parses the SQL statement and returns information about the statement.
|
|
//---------------------------------------------------------------------------
|
|
async getStatementInfo(sql) {
|
|
const options = {};
|
|
const result = {};
|
|
const statement = this._prepare(sql, options);
|
|
options.connection = this;
|
|
try {
|
|
if (!statement.isDdl) {
|
|
const message = new messages.ExecuteMessage(this, statement, options);
|
|
message.parseOnly = true;
|
|
await this._protocol._processMessage(message);
|
|
}
|
|
if (statement.numQueryVars > 0) {
|
|
result.metaData = thinUtil.getMetadataMany(statement.queryVars);
|
|
}
|
|
result.bindNames = Array.from(statement.bindInfoDict.keys());
|
|
result.statementType = statement.statementType;
|
|
return result;
|
|
} finally {
|
|
this._returnStatement(statement);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// execute()
|
|
//
|
|
// Calls the RPC that executes a SQL statement and returns the results.
|
|
//---------------------------------------------------------------------------
|
|
async execute(sql, numIters, binds, options, executeManyFlag) {
|
|
const statement = this._prepare(sql, options);
|
|
try {
|
|
return await this._execute(statement, numIters, binds, options,
|
|
executeManyFlag);
|
|
} catch (err) {
|
|
this._returnStatement(statement);
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Get the statement object from the statement cache for the SQL if it exists
|
|
// else prepare a new statement object for the SQL. If a statement is already
|
|
// in use a copy will be made and returned (and will not be returned to the
|
|
// cache). If a statement is being executed for the first time after releasing
|
|
// a DRCP session, a copy will also be made (and will not be returned to the
|
|
// cache) since it is unknown at this point whether the original session or a
|
|
// new session is going to be used.
|
|
//---------------------------------------------------------------------------
|
|
_getStatement(sql, cacheStatement = false) {
|
|
return this.statementCache.getStatement(sql, cacheStatement,
|
|
this._drcpEstablishSession);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Calls the ping RPC for Oracle Database
|
|
//---------------------------------------------------------------------------
|
|
async ping() {
|
|
const message = new messages.PingMessage(this);
|
|
await this._protocol._processMessage(message);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Calls the Rollback RPC for Oracle Database
|
|
//---------------------------------------------------------------------------
|
|
async rollback() {
|
|
const message = new messages.RollbackMessage(this);
|
|
await this._protocol._processMessage(message);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the Oracle Server version
|
|
//---------------------------------------------------------------------------
|
|
getOracleServerVersion() {
|
|
return this.serverVersion;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the Oracle Server version string
|
|
//---------------------------------------------------------------------------
|
|
getOracleServerVersionString() {
|
|
return this.serverVersionString;
|
|
}
|
|
|
|
setCurrentSchema(schema) {
|
|
this._currentSchemaModified = true;
|
|
this.currentSchema = schema;
|
|
}
|
|
|
|
getCurrentSchema() {
|
|
return this.currentSchema;
|
|
}
|
|
|
|
setClientId(clientId) {
|
|
this._clientIdentifierModified = true;
|
|
this._clientIdentifier = clientId;
|
|
}
|
|
|
|
setDbOp(dbOp) {
|
|
this._dbOpModified = true;
|
|
this._dbOp = dbOp;
|
|
}
|
|
|
|
setExternalName(value) {
|
|
this.externalName = value;
|
|
}
|
|
|
|
setInternalName(value) {
|
|
this.internalName = value;
|
|
}
|
|
|
|
setClientInfo(clientInfo) {
|
|
this._clientInfoModified = true;
|
|
this._clientInfo = clientInfo;
|
|
}
|
|
|
|
setModule(module) {
|
|
this._moduleModified = true;
|
|
this._module = module;
|
|
|
|
/*
|
|
* setting the module by itself results in an error so always force
|
|
* action to be set as well (which eliminates this error)
|
|
*/
|
|
this._actionModified = true;
|
|
}
|
|
|
|
setAction(action) {
|
|
this._actionModified = true;
|
|
this._action = action;
|
|
}
|
|
|
|
async changePassword(user, password, newPassword) {
|
|
const config = {
|
|
user: user,
|
|
newPassword: newPassword,
|
|
password: password,
|
|
changePassword: true
|
|
};
|
|
const message = new messages.AuthMessage(this, config);
|
|
await this._protocol._processMessage(message); // OAUTH
|
|
}
|
|
|
|
async createLob(dbType) {
|
|
const lobImpl = new ThinLobImpl();
|
|
await lobImpl.create(this, dbType);
|
|
return lobImpl;
|
|
}
|
|
|
|
// Check the state returned by the tpcCommit() call.
|
|
checkTpcCommitState(state, onePhase) {
|
|
if ((onePhase && state !== constants.TNS_TPC_TXN_STATE_READ_ONLY
|
|
&& state !== constants.TNS_TPC_TXN_STATE_COMMITTED) ||
|
|
(!onePhase && state !== constants.TNS_TPC_TXN_STATE_FORGOTTEN)) {
|
|
errors.throwErr(errors.ERR_UNKNOWN_TRANSACTION_STATE, state);
|
|
}
|
|
}
|
|
|
|
// Creates a two-phase commit message suitable for committing a transaction.
|
|
createTpcCommitMessage(xid, onePhase) {
|
|
const message = new messages.TransactionChangeStateMessage(this);
|
|
message.operation = constants.TNS_TPC_TXN_COMMIT;
|
|
message.state = (onePhase == 0) ? constants.TNS_TPC_TXN_STATE_COMMITTED :
|
|
constants.TNS_TPC_TXN_STATE_READ_ONLY;
|
|
message.xid = xid;
|
|
message.context = this.tpcContext;
|
|
return message;
|
|
}
|
|
|
|
// Creates a two-phase commit rollback message suitable for use in both
|
|
// the close() method and explicitly by the user.
|
|
createTpcRollbackMessage(xid = null) {
|
|
const message = new messages.TransactionChangeStateMessage(this);
|
|
message.operation = constants.TNS_TPC_TXN_ABORT;
|
|
message.state = constants.TNS_TPC_TXN_STATE_ABORTED;
|
|
message.xid = xid;
|
|
message.context = this.tpcContext;
|
|
return message;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// tpcBegin()
|
|
//---------------------------------------------------------------------------
|
|
async tpcBegin(xid, flags, timeout) {
|
|
const message = new messages.TransactionSwitchMessage(this);
|
|
message.operation = constants.TNS_TPC_TXN_START;
|
|
message.xid = xid;
|
|
message.flags = flags;
|
|
message.timeout = timeout;
|
|
await this._protocol._processMessage(message);
|
|
this.tpcContext = message.context;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// tpcCommit()
|
|
//---------------------------------------------------------------------------
|
|
async tpcCommit(xid, onePhase) {
|
|
const message = this.createTpcCommitMessage(xid, onePhase);
|
|
await this._protocol._processMessage(message);
|
|
this.checkTpcCommitState(message.state, onePhase);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// tpcEnd()
|
|
//---------------------------------------------------------------------------
|
|
async tpcEnd(xid, flags) {
|
|
const message = new messages.TransactionSwitchMessage(this);
|
|
message.operation = constants.TNS_TPC_TXN_DETACH;
|
|
message.xid = xid;
|
|
message.context = this.tpcContext;
|
|
message.flags = flags;
|
|
await this._protocol._processMessage(message);
|
|
this.tpcContext = null;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// tpcPrepare()
|
|
//---------------------------------------------------------------------------
|
|
async tpcPrepare(xid) {
|
|
const message = new messages.TransactionChangeStateMessage(this);
|
|
message.operation = constants.TNS_TPC_TXN_PREPARE;
|
|
message.xid = xid;
|
|
message.context = this.tpcContext;
|
|
await this._protocol._processMessage(message);
|
|
if (message.state === constants.TNS_TPC_TXN_STATE_REQUIRES_COMMIT) {
|
|
return true;
|
|
} else if (message.state === constants.TNS_TPC_TXN_STATE_READ_ONLY) {
|
|
return false;
|
|
}
|
|
|
|
errors.throwErr(errors.ERR_UNKNOWN_TRANSACTION_STATE, message.state);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// tpcRollback()
|
|
//---------------------------------------------------------------------------
|
|
async tpcRollback(xid) {
|
|
const message = this.createTpcRollbackMessage(xid);
|
|
await this._protocol._processMessage(message);
|
|
if (message.state !== constants.TNS_TPC_TXN_STATE_ABORTED) {
|
|
errors.throwErr(errors.ERR_UNKNOWN_TRANSACTION_STATE, message.state);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the statement cache size for the statement cache maintained by
|
|
// the connection object
|
|
//---------------------------------------------------------------------------
|
|
getStmtCacheSize() {
|
|
return this.statementCache._maxSize;
|
|
}
|
|
|
|
setCallTimeout(timeout) {
|
|
this._protocol.callTimeout = timeout;
|
|
}
|
|
|
|
getCallTimeout() {
|
|
return this._protocol.callTimeout;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns getTag. Actual tag returned by db must be a string.
|
|
//---------------------------------------------------------------------------
|
|
getTag() {
|
|
return '';
|
|
}
|
|
|
|
getExternalName() {
|
|
return this.externalName;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the Oracle Database instance name associated with the connection.
|
|
//---------------------------------------------------------------------------
|
|
getInstanceName() {
|
|
return this.instanceName;
|
|
}
|
|
|
|
getInternalName() {
|
|
return this.internalName;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the Oracle Database domain name associated with the connection.
|
|
//---------------------------------------------------------------------------
|
|
getDbDomain() {
|
|
return this.dbDomain;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the Oracle Database host name associated with the connection.
|
|
//---------------------------------------------------------------------------
|
|
getHostName() {
|
|
return this.nscon.ntAdapter.hostName;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the Oracle Database port number associated with the connection.
|
|
//---------------------------------------------------------------------------
|
|
getPort() {
|
|
return this.nscon.ntAdapter.port;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the protocol associated with the connection.
|
|
//---------------------------------------------------------------------------
|
|
getProtocol() {
|
|
return (this.nscon.ntAdapter.secure) ? 'TCPS' : 'TCP';
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the Oracle Database name associated with the connection.
|
|
//---------------------------------------------------------------------------
|
|
getDbName() {
|
|
return this.dbName;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns maximum number of cursors that can be opened in one session.
|
|
//---------------------------------------------------------------------------
|
|
getMaxOpenCursors() {
|
|
return this.maxOpenCursors;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the maximum length of identifiers supported by the database to
|
|
// which this connection has been established.
|
|
//---------------------------------------------------------------------------
|
|
getMaxIdentifierLength() {
|
|
return this.maxIdentifierLength;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the Oracle Database service name associated with the connection.
|
|
//---------------------------------------------------------------------------
|
|
getServiceName() {
|
|
return this.serviceName;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns boolean based on this._protocol.txnInProgress value.
|
|
//---------------------------------------------------------------------------
|
|
getTransactionInProgress() {
|
|
return this._protocol.txnInProgress;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns the warning object.
|
|
//---------------------------------------------------------------------------
|
|
getWarning() {
|
|
return this.warning;
|
|
}
|
|
}
|
|
module.exports = ThinConnectionImpl;
|