Add DB Support objects and Pipelined Table tests
This commit is contained in:
parent
4c2336e54d
commit
3a9c0d9e82
|
@ -37,22 +37,26 @@ Error.stackTraceLimit = 50;
|
|||
const oracledb = require('oracledb');
|
||||
const dbConfig = require('./dbconfig.js');
|
||||
|
||||
// This example requires node-oracledb Thick mode.
|
||||
// This example runs in both node-oracledb Thin and Thick modes.
|
||||
//
|
||||
// Thick mode requires Oracle Client or Oracle Instant Client libraries. On
|
||||
// Windows and macOS Intel you can specify the directory containing the
|
||||
// libraries at runtime or before Node.js starts. On other platforms (where
|
||||
// Oracle libraries are available) the system library search path must always
|
||||
// include the Oracle library path before Node.js starts. If the search path
|
||||
// is not correct, you will get a DPI-1047 error. See the node-oracledb
|
||||
// installation documentation.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
// Optionally run in node-oracledb Thick mode
|
||||
if (process.env.NODE_ORACLEDB_DRIVER_MODE === 'thick') {
|
||||
|
||||
// Thick mode requires Oracle Client or Oracle Instant Client libraries.
|
||||
// On Windows and macOS Intel you can specify the directory containing the
|
||||
// libraries at runtime or before Node.js starts. On other platforms (where
|
||||
// Oracle libraries are available) the system library search path must always
|
||||
// include the Oracle library path before Node.js starts. If the search path
|
||||
// is not correct, you will get a DPI-1047 error. See the node-oracledb
|
||||
// installation documentation.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
|
||||
|
||||
async function run() {
|
||||
let connection, binds, options, result, obj;
|
||||
|
|
|
@ -37,22 +37,26 @@ Error.stackTraceLimit = 50;
|
|||
const oracledb = require('oracledb');
|
||||
const dbConfig = require('./dbconfig.js');
|
||||
|
||||
// This example requires node-oracledb Thick mode.
|
||||
// This example runs in both node-oracledb Thin and Thick modes.
|
||||
//
|
||||
// Thick mode requires Oracle Client or Oracle Instant Client libraries. On
|
||||
// Windows and macOS Intel you can specify the directory containing the
|
||||
// libraries at runtime or before Node.js starts. On other platforms (where
|
||||
// Oracle libraries are available) the system library search path must always
|
||||
// include the Oracle library path before Node.js starts. If the search path
|
||||
// is not correct, you will get a DPI-1047 error. See the node-oracledb
|
||||
// installation documentation.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
// Optionally run in node-oracledb Thick mode
|
||||
if (process.env.NODE_ORACLEDB_DRIVER_MODE === 'thick') {
|
||||
|
||||
// Thick mode requires Oracle Client or Oracle Instant Client libraries.
|
||||
// On Windows and macOS Intel you can specify the directory containing the
|
||||
// libraries at runtime or before Node.js starts. On other platforms (where
|
||||
// Oracle libraries are available) the system library search path must always
|
||||
// include the Oracle library path before Node.js starts. If the search path
|
||||
// is not correct, you will get a DPI-1047 error. See the node-oracledb
|
||||
// installation documentation.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
|
||||
|
||||
async function run() {
|
||||
let connection;
|
||||
|
|
|
@ -37,20 +37,26 @@ Error.stackTraceLimit = 50;
|
|||
const oracledb = require('oracledb');
|
||||
const dbConfig = require('./dbconfig.js');
|
||||
|
||||
// This example requires node-oracledb Thick mode.
|
||||
// This example runs in both node-oracledb Thin and Thick modes.
|
||||
//
|
||||
// On Windows and macOS Intel, you can specify the directory containing the
|
||||
// Oracle Client Libraries at runtime, or before Node.js starts. On other
|
||||
// platforms the system library search path must always be set before Node.js
|
||||
// is started. See the node-oracledb installation documentation. If the
|
||||
// search path is not correct, you will get a DPI-1047 error.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
// Optionally run in node-oracledb Thick mode
|
||||
if (process.env.NODE_ORACLEDB_DRIVER_MODE === 'thick') {
|
||||
|
||||
// Thick mode requires Oracle Client or Oracle Instant Client libraries.
|
||||
// On Windows and macOS Intel you can specify the directory containing the
|
||||
// libraries at runtime or before Node.js starts. On other platforms (where
|
||||
// Oracle libraries are available) the system library search path must always
|
||||
// include the Oracle library path before Node.js starts. If the search path
|
||||
// is not correct, you will get a DPI-1047 error. See the node-oracledb
|
||||
// installation documentation.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts);
|
||||
|
||||
// If each object's attributes are accessed multiple times, it may be more
|
||||
// efficient to fetch as simple JavaScriptobjects.
|
||||
|
|
|
@ -38,22 +38,26 @@ Error.stackTraceLimit = 50;
|
|||
const oracledb = require('oracledb');
|
||||
const dbConfig = require('./dbconfig.js');
|
||||
|
||||
// This example requires node-oracledb Thick mode.
|
||||
// This example runs in both node-oracledb Thin and Thick modes.
|
||||
//
|
||||
// Thick mode requires Oracle Client or Oracle Instant Client libraries. On
|
||||
// Windows and macOS Intel you can specify the directory containing the
|
||||
// libraries at runtime or before Node.js starts. On other platforms (where
|
||||
// Oracle libraries are available) the system library search path must always
|
||||
// include the Oracle library path before Node.js starts. If the search path
|
||||
// is not correct, you will get a DPI-1047 error. See the node-oracledb
|
||||
// installation documentation.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
// Optionally run in node-oracledb Thick mode
|
||||
if (process.env.NODE_ORACLEDB_DRIVER_MODE === 'thick') {
|
||||
|
||||
// Thick mode requires Oracle Client or Oracle Instant Client libraries.
|
||||
// On Windows and macOS Intel you can specify the directory containing the
|
||||
// libraries at runtime or before Node.js starts. On other platforms (where
|
||||
// Oracle libraries are available) the system library search path must always
|
||||
// include the Oracle library path before Node.js starts. If the search path
|
||||
// is not correct, you will get a DPI-1047 error. See the node-oracledb
|
||||
// installation documentation.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
|
||||
|
||||
// If each object's attributes are accessed multiple times, it may be more
|
||||
// efficient to fetch as simple JavaScriptobjects.
|
||||
|
|
|
@ -37,22 +37,26 @@ Error.stackTraceLimit = 50;
|
|||
const oracledb = require('oracledb');
|
||||
const dbConfig = require('./dbconfig.js');
|
||||
|
||||
// This example requires node-oracledb Thick mode.
|
||||
// This example runs in both node-oracledb Thin and Thick modes.
|
||||
//
|
||||
// Thick mode requires Oracle Client or Oracle Instant Client libraries. On
|
||||
// Windows and macOS Intel you can specify the directory containing the
|
||||
// libraries at runtime or before Node.js starts. On other platforms (where
|
||||
// Oracle libraries are available) the system library search path must always
|
||||
// include the Oracle library path before Node.js starts. If the search path
|
||||
// is not correct, you will get a DPI-1047 error. See the node-oracledb
|
||||
// installation documentation.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
// Optionally run in node-oracledb Thick mode
|
||||
if (process.env.NODE_ORACLEDB_DRIVER_MODE === 'thick') {
|
||||
|
||||
// Thick mode requires Oracle Client or Oracle Instant Client libraries.
|
||||
// On Windows and macOS Intel you can specify the directory containing the
|
||||
// libraries at runtime or before Node.js starts. On other platforms (where
|
||||
// Oracle libraries are available) the system library search path must always
|
||||
// include the Oracle library path before Node.js starts. If the search path
|
||||
// is not correct, you will get a DPI-1047 error. See the node-oracledb
|
||||
// installation documentation.
|
||||
let clientOpts = {};
|
||||
if (process.platform === 'win32') { // Windows
|
||||
clientOpts = { libDir: 'C:\\oracle\\instantclient_19_17' };
|
||||
} else if (process.platform === 'darwin' && process.arch === 'x64') { // macOS Intel
|
||||
clientOpts = { libDir: process.env.HOME + '/Downloads/instantclient_19_8' };
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
|
||||
}
|
||||
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
|
||||
|
||||
async function run() {
|
||||
let connection;
|
||||
|
|
|
@ -82,8 +82,7 @@ class Connection extends EventEmitter {
|
|||
//---------------------------------------------------------------------------
|
||||
_buildDbObjectClass(objType) {
|
||||
const DbObject = function(initialValue) {
|
||||
this._impl = new impl.DbObjectImpl();
|
||||
this._impl._objType = objType;
|
||||
this._impl = new impl.DbObjectImpl(objType);
|
||||
if (this.isCollection) {
|
||||
const proxy = new Proxy(this, BaseDbObject._collectionProxyHandler);
|
||||
if (initialValue !== undefined) {
|
||||
|
@ -169,6 +168,8 @@ class Connection extends EventEmitter {
|
|||
// 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);
|
||||
|
|
|
@ -140,7 +140,7 @@ class BaseDbObject {
|
|||
//---------------------------------------------------------------------------
|
||||
get attributes() {
|
||||
if (!this._attributes) {
|
||||
const implAttrs = this._objType.attributes;
|
||||
const implAttrs = this._objType.attributes || [];
|
||||
const attrs = {};
|
||||
for (let i = 0; i < implAttrs.length; i++) {
|
||||
const implAttr = implAttrs[i];
|
||||
|
|
|
@ -126,6 +126,12 @@ const ERR_CALL_TIMEOUT_EXCEEDED = 123;
|
|||
const ERR_EMPTY_CONNECTION_STRING = 125;
|
||||
const ERR_OSON_VERSION_NOT_SUPPORTED = 126;
|
||||
const ERR_UNKOWN_SERVER_SIDE_PIGGYBACK = 127;
|
||||
const ERR_UNKNOWN_COLUMN_TYPE_NAME = 128;
|
||||
const ERR_INVALID_OBJECT_TYPE_NAME = 129;
|
||||
const ERR_TDS_TYPE_NOT_SUPPORTED = 130;
|
||||
const ERR_INVALID_COLL_INDEX_SET = 131;
|
||||
const ERR_INVALID_COLL_INDEX_GET = 132;
|
||||
const ERR_DELETE_ELEMENTS_OF_VARRAY = 133;
|
||||
|
||||
// Oracle Net layer errors start from 500
|
||||
const ERR_CONNECTION_CLOSED = 500;
|
||||
|
@ -153,12 +159,16 @@ const ERR_CONNECTION_EOF = 521;
|
|||
// define mapping for ODPI-C errors that need to be wrapped with NJS errors
|
||||
const adjustErrorXref = new Map();
|
||||
adjustErrorXref.set("DPI-1010", ERR_CONNECTION_CLOSED);
|
||||
adjustErrorXref.set("DPI-1024", [ERR_INVALID_COLL_INDEX_GET, 'at index ([0-9]+) does']);
|
||||
adjustErrorXref.set("DPI-1040", ERR_LOB_CLOSED);
|
||||
adjustErrorXref.set("DPI-1044", ERR_ORACLE_NUMBER_NO_REPR);
|
||||
adjustErrorXref.set("DPI-1055", ERR_NAN_VALUE);
|
||||
adjustErrorXref.set("DPI-1063", ERR_EXEC_MODE_ONLY_FOR_DML);
|
||||
adjustErrorXref.set("DPI-1067", [ERR_CALL_TIMEOUT_EXCEEDED, "call timeout of ([0-9]+) ms"]);
|
||||
adjustErrorXref.set("DPI-1080", ERR_CONNECTION_CLOSED);
|
||||
adjustErrorXref.set("OCI-22303", [ERR_INVALID_OBJECT_TYPE_NAME, 'type "([^"]*"."[^"]*)"']);
|
||||
adjustErrorXref.set("OCI-22164", ERR_DELETE_ELEMENTS_OF_VARRAY);
|
||||
adjustErrorXref.set("OCI-22165", [ERR_INVALID_COLL_INDEX_SET, /index \[([0-9]+)\] must be in the range of \[([0-9]+)\] to \[([0-9]+)\]/]);
|
||||
adjustErrorXref.set("ORA-00028", ERR_CONNECTION_CLOSED);
|
||||
adjustErrorXref.set("ORA-00600", ERR_CONNECTION_CLOSED);
|
||||
adjustErrorXref.set("ORA-24338", ERR_INVALID_REF_CURSOR);
|
||||
|
@ -359,6 +369,18 @@ messages.set(ERR_OSON_VERSION_NOT_SUPPORTED, // NJS-126
|
|||
'OSON version %s is not supported');
|
||||
messages.set(ERR_UNKOWN_SERVER_SIDE_PIGGYBACK, // NJS-127
|
||||
'internal error: unknown server side piggyback opcode %s');
|
||||
messages.set(ERR_UNKNOWN_COLUMN_TYPE_NAME, // NJS-128
|
||||
'internal error: unknown column type name "%s"');
|
||||
messages.set(ERR_INVALID_OBJECT_TYPE_NAME, // NJS-129
|
||||
'invalid object type name: "%s"');
|
||||
messages.set(ERR_TDS_TYPE_NOT_SUPPORTED, // NJS-130
|
||||
'Oracle TDS data type %d is not supported');
|
||||
messages.set(ERR_INVALID_COLL_INDEX_SET, // NJS-131
|
||||
'given index %d must be in the range of %d to %d');
|
||||
messages.set(ERR_INVALID_COLL_INDEX_GET, // NJS-132
|
||||
'element at index %d does not exist');
|
||||
messages.set(ERR_DELETE_ELEMENTS_OF_VARRAY, // NJS-133
|
||||
'cannot delete elements of a VARRAY');
|
||||
|
||||
// Oracle Net layer errors
|
||||
|
||||
|
@ -719,6 +741,12 @@ module.exports = {
|
|||
ERR_CALL_TIMEOUT_EXCEEDED,
|
||||
ERR_EMPTY_CONNECTION_STRING,
|
||||
ERR_UNKOWN_SERVER_SIDE_PIGGYBACK,
|
||||
ERR_UNKNOWN_COLUMN_TYPE_NAME,
|
||||
ERR_INVALID_OBJECT_TYPE_NAME,
|
||||
ERR_TDS_TYPE_NOT_SUPPORTED,
|
||||
ERR_INVALID_COLL_INDEX_SET,
|
||||
ERR_INVALID_COLL_INDEX_GET,
|
||||
ERR_DELETE_ELEMENTS_OF_VARRAY,
|
||||
ERR_CONNECTION_CLOSED_CODE: `${ERR_PREFIX}-${ERR_CONNECTION_CLOSED}`,
|
||||
assert,
|
||||
assertArgCount,
|
||||
|
|
|
@ -72,15 +72,34 @@ class ConnectionImpl {
|
|||
// _getDbObjectType()
|
||||
//
|
||||
// Return the object identifying the object type. These are cached by fully
|
||||
// qualified name.
|
||||
// ---------------------------------------------------------------------------
|
||||
_getDbObjectType(schema, name) {
|
||||
const fqn = `${schema}.${name}`;
|
||||
let dbObjectType = this._dbObjectTypes.get(fqn);
|
||||
// qualified name and by OID (thin mode only).
|
||||
//---------------------------------------------------------------------------
|
||||
_getDbObjectType(schema, name, packageName, oid) {
|
||||
let dbObjectType;
|
||||
if (oid) {
|
||||
dbObjectType = this._dbObjectTypes.get(oid);
|
||||
if (dbObjectType)
|
||||
return dbObjectType;
|
||||
}
|
||||
const fqn = (packageName) ? `${schema}.${packageName}.${name}` :
|
||||
`${schema}.${name}`;
|
||||
dbObjectType = this._dbObjectTypes.get(fqn);
|
||||
if (!dbObjectType) {
|
||||
dbObjectType = {schema: schema, name: name, fqn: fqn};
|
||||
dbObjectType = {
|
||||
oid: oid,
|
||||
fqn: fqn,
|
||||
schema: schema,
|
||||
name: name,
|
||||
packageName: packageName,
|
||||
partial: true,
|
||||
isXmlType: (schema === 'SYS' && name === 'XMLTYPE')
|
||||
};
|
||||
this._dbObjectTypes.set(fqn, dbObjectType);
|
||||
}
|
||||
if (oid && !dbObjectType.oid) {
|
||||
dbObjectType.oid = oid;
|
||||
this._dbObjectTypes.set(oid, dbObjectType);
|
||||
}
|
||||
return dbObjectType;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@ const errors = require('../errors.js');
|
|||
// instantiated; a cache of these classes are maintained on each connection
|
||||
class DbObjectImpl {
|
||||
|
||||
constructor(objType) {
|
||||
this._objType = objType;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// append()
|
||||
//
|
||||
|
|
|
@ -100,6 +100,35 @@ class ResultSetImpl {
|
|||
metadata.fetchType = actualFetchType;
|
||||
}
|
||||
|
||||
// in thin mode, Oracle NUMBER values are internally fetched as string in
|
||||
// order to preserve precision so must be converted to JavaScript Number
|
||||
// when needed; other numeric and date types are fetched natively as
|
||||
// JavaScript Number and Date values and are converted to string using
|
||||
// toString() when desired
|
||||
if (settings.thin) {
|
||||
let converter;
|
||||
const userConverter = metadata.converter;
|
||||
if (metadata.dbType === types.DB_TYPE_NUMBER &&
|
||||
metadata.fetchType === types.DB_TYPE_NUMBER) {
|
||||
converter = (v) => (v === null) ? null : parseFloat(v);
|
||||
} else if (metadata.fetchType === types.DB_TYPE_VARCHAR &&
|
||||
(metadata.dbType === types.DB_TYPE_BINARY_DOUBLE ||
|
||||
metadata.dbType === types.DB_TYPE_BINARY_FLOAT ||
|
||||
metadata.dbType === types.DB_TYPE_DATE ||
|
||||
metadata.dbType === types.DB_TYPE_TIMESTAMP ||
|
||||
metadata.dbType === types.DB_TYPE_TIMESTAMP_LTZ ||
|
||||
metadata.dbType === types.DB_TYPE_TIMESTAMP_TZ)) {
|
||||
converter = (v) => (v === null) ? null : v.toString();
|
||||
}
|
||||
if (userConverter && converter) {
|
||||
const internalConverter = converter;
|
||||
converter = (v) => userConverter(internalConverter(v));
|
||||
}
|
||||
if (converter) {
|
||||
metadata.converter = converter;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
|
|
@ -30,12 +30,12 @@ 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('./protocol/buffer.js');
|
||||
const {NetworkSession:nsi, getConnectionInfo} = require("./sqlnet/networkSession.js");
|
||||
const { Statement } = require("./statement");
|
||||
const thinUtil = require('./util');
|
||||
const sqlNetConstants = require('./sqlnet/constants.js');
|
||||
const constants = require('../constants.js');
|
||||
const protocolConstants = require('./protocol/constants.js');
|
||||
const constants = require('./protocol/constants.js');
|
||||
const types = require('../types.js');
|
||||
const errors = require("../errors.js");
|
||||
const messages = require('./protocol/messages');
|
||||
|
@ -44,6 +44,9 @@ const finalizationRegistry = new global.FinalizationRegistry((heldValue) => {
|
|||
heldValue.disconnect();
|
||||
});
|
||||
|
||||
class TDSBuffer extends BaseBuffer {
|
||||
}
|
||||
|
||||
class ThinConnectionImpl extends ConnectionImpl {
|
||||
|
||||
/**
|
||||
|
@ -80,6 +83,355 @@ class ThinConnectionImpl extends ConnectionImpl {
|
|||
await this._protocol._processMessage(message);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _determineElementObjType()
|
||||
//
|
||||
// Determine 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 _determineElementObjType(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);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _parseElementType()
|
||||
//
|
||||
// Parses the element type from the TDS buffer.
|
||||
//---------------------------------------------------------------------------
|
||||
async _parseElementType(buf, info) {
|
||||
let oraTypeNum, csfrm;
|
||||
const attrType = buf.readUInt8();
|
||||
switch (attrType) {
|
||||
case constants.TNS_OBJ_TDS_TYPE_NUMBER:
|
||||
case constants.TNS_OBJ_TDS_TYPE_FLOAT:
|
||||
info.elementType = types.DB_TYPE_NUMBER;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_VARCHAR:
|
||||
case constants.TNS_OBJ_TDS_TYPE_CHAR:
|
||||
info.maxSize = buf.readUInt16BE();
|
||||
oraTypeNum = (attrType === constants.TNS_OBJ_TDS_TYPE_VARCHAR) ?
|
||||
constants.TNS_DATA_TYPE_VARCHAR : constants.TNS_DATA_TYPE_CHAR;
|
||||
csfrm = buf.readUInt8();
|
||||
info.elementType = types.getTypeByOraTypeNum(oraTypeNum, csfrm);
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_RAW:
|
||||
info.elementType = types.DB_TYPE_RAW;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_BINARY_FLOAT:
|
||||
info.elementType = types.DB_TYPE_BINARY_FLOAT;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_BINARY_DOUBLE:
|
||||
info.elementType = types.DB_TYPE_BINARY_DOUBLE;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_DATE:
|
||||
info.elementType = types.DB_TYPE_DATE;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP:
|
||||
info.elementType = types.DB_TYPE_TIMESTAMP;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_LTZ:
|
||||
info.elementType = types.DB_TYPE_TIMESTAMP_LTZ;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_TZ:
|
||||
info.elementType = types.DB_TYPE_TIMESTAMP_TZ;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_BOOLEAN:
|
||||
info.elementType = types.DB_TYPE_BOOLEAN;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_CLOB:
|
||||
this._determineElementTypeCharsetForm(info);
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_BLOB:
|
||||
info.elementType = types.DB_TYPE_BLOB;
|
||||
break;
|
||||
case constants.TNS_OBJ_TDS_TYPE_OBJ:
|
||||
info.elementType = types.DB_TYPE_OBJECT;
|
||||
await this._determineElementObjType(info);
|
||||
break;
|
||||
default:
|
||||
errors.throwErr(errors.ERR_TDS_TYPE_NOT_SUPPORTED, attrType);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _parseTDS()
|
||||
//
|
||||
// Parses the TDS for the type. This is only needed for collection types, so
|
||||
// if the TDS is determined to be for an object type, the remaining
|
||||
// information is ignored.
|
||||
//---------------------------------------------------------------------------
|
||||
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();
|
||||
if (numAttrs > 1) {
|
||||
info.isCollection = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// if type of first attribute is not a collection, nothing further needs
|
||||
// to be done
|
||||
const attrType = buf.readUInt8();
|
||||
info.isCollection = (attrType === constants.TNS_OBJ_TDS_TYPE_COLL);
|
||||
if (!info.isCollection)
|
||||
return;
|
||||
|
||||
// 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;
|
||||
|
||||
await this._parseElementType(buf, info);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _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);
|
||||
}
|
||||
|
||||
// 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
|
||||
info.version = result.outBinds.version;
|
||||
await this._parseTDS(result.outBinds.tds, info);
|
||||
const attrRows = await result.outBinds.attrs_rc.getRows(1000, {});
|
||||
if (attrRows.length > 0) {
|
||||
info.attributes = [];
|
||||
for (const row of attrRows) {
|
||||
const attr = { name: row[1] };
|
||||
if (row[4]) {
|
||||
attr.type = types.DB_TYPE_OBJECT;
|
||||
attr.typeClass = this._getDbObjectType(row[4], row[3], row[5], row[6]);
|
||||
if (attr.typeClass.partial) {
|
||||
this._partialDbObjectTypes.push(attr.typeClass);
|
||||
}
|
||||
} else {
|
||||
attr.type = types.getTypeByColumnTypeName(row[3]);
|
||||
}
|
||||
info.attributes.push(attr);
|
||||
}
|
||||
}
|
||||
info.partial = false;
|
||||
|
||||
return info;
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _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);
|
||||
|
@ -190,6 +542,8 @@ class ThinConnectionImpl extends ConnectionImpl {
|
|||
throw err;
|
||||
}
|
||||
|
||||
// maintain a list of partially populated database object types
|
||||
this._partialDbObjectTypes = [];
|
||||
|
||||
if (params.debugJDWP) {
|
||||
this.jdwpData = Buffer.from(params.debugJDWP);
|
||||
|
@ -283,7 +637,7 @@ class ThinConnectionImpl extends ConnectionImpl {
|
|||
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 === protocolConstants.TNS_CS_NCHAR) {
|
||||
} else if (variable.type._csfrm === constants.CSFRM_NCHAR) {
|
||||
variable.type = types.DB_TYPE_NCLOB;
|
||||
} else {
|
||||
variable.type = types.DB_TYPE_CLOB;
|
||||
|
@ -388,6 +742,17 @@ class ThinConnectionImpl extends ConnectionImpl {
|
|||
this._cursorsToClose.clear();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getDbObjectClass()
|
||||
//
|
||||
// Returns a database object class given its name.
|
||||
//---------------------------------------------------------------------------
|
||||
async getDbObjectClass(name) {
|
||||
const info = await this._populateDbObjectTypeInfo(name);
|
||||
await this._populatePartialDbObjectTypes();
|
||||
return info;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Parses the sql given by User
|
||||
// calls the OAL8 RPC that parses the SQL statement and returns the metadata
|
||||
|
@ -489,7 +854,7 @@ class ThinConnectionImpl extends ConnectionImpl {
|
|||
message.numExecs = numIters;
|
||||
message.arrayDmlRowCounts = options.dmlRowCounts;
|
||||
message.batchErrors = options.batchErrors;
|
||||
if (statement.isPlSql && statement.cursorId === 0) {
|
||||
if (statement.isPlSql && statement.requiresFullExecute) {
|
||||
message.numExecs = 1;
|
||||
await this._protocol._processMessage(message);
|
||||
if (statement.plsqlMultipleExecs) {
|
||||
|
|
|
@ -0,0 +1,637 @@
|
|||
// Copyright (c) 2023, 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 constants = require('./protocol/constants.js');
|
||||
const errors = require('../errors.js');
|
||||
const types = require('../types.js');
|
||||
const DbObjectImpl = require('../impl/dbObject.js');
|
||||
const { GrowableBuffer } = require('./protocol/buffer.js');
|
||||
|
||||
class DbObjectPickleBuffer extends GrowableBuffer {
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getIsAtomicNull()
|
||||
//
|
||||
// Reads the next byte and checks to see if the value is atomically null. If
|
||||
// not, the byte is returned to the buffer for further processing.
|
||||
//---------------------------------------------------------------------------
|
||||
getIsAtomicNull() {
|
||||
const value = this.readUInt8();
|
||||
if (value === constants.TNS_OBJ_ATOMIC_NULL ||
|
||||
value === constants.TNS_NULL_LENGTH_INDICATOR) {
|
||||
return true;
|
||||
}
|
||||
this.pos -= 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// readHeader()
|
||||
//
|
||||
// Reads the header of the pickled data.
|
||||
//---------------------------------------------------------------------------
|
||||
readHeader(obj) {
|
||||
obj.imageFlags = this.readUInt8();
|
||||
obj.imageVersion = this.readUInt8();
|
||||
this.readLength();
|
||||
if ((obj.imageFlags & constants.TNS_OBJ_NO_PREFIX_SEG) === 0) {
|
||||
const prefixSegLength = this.readLength();
|
||||
this.skipBytes(prefixSegLength);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// readLength()
|
||||
//
|
||||
// Read the length from the buffer. This will be a single byte, unless the
|
||||
// value meets or exeeds TNS_LONG_LENGTH_INDICATOR. In that case, the value
|
||||
// is stored as a 4-byte integer.
|
||||
//---------------------------------------------------------------------------
|
||||
readLength() {
|
||||
const shortLength = this.readUInt8();
|
||||
if (shortLength !== constants.TNS_LONG_LENGTH_INDICATOR) {
|
||||
return shortLength;
|
||||
}
|
||||
return this.readUInt32BE();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// writeHeader()
|
||||
//
|
||||
// Writes the header of the pickled data. Since the size is unknown at this
|
||||
// point, zero is written initially and the actual size is written later.
|
||||
//---------------------------------------------------------------------------
|
||||
writeHeader(obj) {
|
||||
this.writeUInt8(obj.imageFlags);
|
||||
this.writeUInt8(obj.imageVersion);
|
||||
this.writeUInt8(constants.TNS_LONG_LENGTH_INDICATOR);
|
||||
this.writeUInt32BE(0);
|
||||
if (obj._objType.isCollection) {
|
||||
this.writeUInt8(1); // length of prefix segment
|
||||
this.writeUInt8(1); // prefix segment contents
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// writeLength()
|
||||
//
|
||||
// Writes the length to the buffer.
|
||||
//---------------------------------------------------------------------------
|
||||
writeLength(length) {
|
||||
if (length <= constants.TNS_OBJ_MAX_SHORT_LENGTH) {
|
||||
this.writeUInt8(length);
|
||||
} else {
|
||||
this.writeUInt8(constants.TNS_LONG_LENGTH_INDICATOR);
|
||||
this.writeUInt32BE(length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ThinDbObjectImpl extends DbObjectImpl {
|
||||
|
||||
constructor(objType, packedData) {
|
||||
if (typeof objType === 'function') {
|
||||
objType = objType.prototype._objType;
|
||||
}
|
||||
super(objType);
|
||||
this.packedData = packedData;
|
||||
this.unpackedAttrs = new Map();
|
||||
if (packedData) {
|
||||
this.unpackedAssocArray = new Map();
|
||||
this.unpackedAssocKeys = undefined;
|
||||
} else {
|
||||
const prefix = Buffer.from([0, 0x22, constants.TNS_OBJ_NON_NULL_OID,
|
||||
constants.TNS_OBJ_HAS_EXTENT_OID]);
|
||||
this.toid = Buffer.concat([prefix, objType.oid, constants.TNS_EXTENT_OID]);
|
||||
this.flags = constants.TNS_OBJ_TOP_LEVEL;
|
||||
this.imageFlags = constants.TNS_OBJ_IS_VERSION_81;
|
||||
this.imageVersion = constants.TNS_OBJ_IMAGE_VERSION;
|
||||
if (objType.isCollection) {
|
||||
this.imageFlags |= constants.TNS_OBJ_IS_COLLECTION;
|
||||
if (objType.collectionType === constants.TNS_OBJ_PLSQL_INDEX_TABLE) {
|
||||
this.unpackedAssocArray = new Map();
|
||||
} else {
|
||||
this.unpackedArray = [];
|
||||
}
|
||||
} else {
|
||||
this.imageFlags |= constants.TNS_OBJ_NO_PREFIX_SEG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _ensureAssocKeys()
|
||||
//
|
||||
// Ensure that the keys for the associative array have been calculated.
|
||||
// PL/SQL associative arrays keep their keys in sorted order so this must be
|
||||
// calculated when indices are required.
|
||||
//---------------------------------------------------------------------------
|
||||
_ensureAssocKeys() {
|
||||
if (!this.unpackedAssocKeys) {
|
||||
this.unpackedAssocKeys = [...this.unpackedAssocArray.keys()].sort();
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _ensureUnpacked()
|
||||
//
|
||||
// Ensure that the data has been unpacked.
|
||||
//---------------------------------------------------------------------------
|
||||
_ensureUnpacked() {
|
||||
if (this.packedData) {
|
||||
this._unpackData();
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _getPackedData()
|
||||
//
|
||||
// Returns the packed data for the object. This will either be the value
|
||||
// retrieved from the database or generated packed data (for new objects and
|
||||
// those that have had their data unpacked already).
|
||||
//---------------------------------------------------------------------------
|
||||
_getPackedData() {
|
||||
if (this.packedData)
|
||||
return this.packedData;
|
||||
const buf = new DbObjectPickleBuffer();
|
||||
buf.writeHeader(this);
|
||||
this._packData(buf);
|
||||
const size = buf.pos;
|
||||
buf.pos = 3;
|
||||
buf.writeUInt32BE(size);
|
||||
return buf.buf.subarray(0, size);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _packData()
|
||||
//
|
||||
// Packs the data from the object into the buffer.
|
||||
//---------------------------------------------------------------------------
|
||||
_packData(buf) {
|
||||
const objType = this._objType;
|
||||
if (objType.isCollection) {
|
||||
buf.writeUInt8(objType.collectionFlags);
|
||||
if (objType.collectionType === constants.TNS_OBJ_PLSQL_INDEX_TABLE) {
|
||||
this._ensureAssocKeys();
|
||||
buf.writeLength(this.unpackedAssocKeys.length);
|
||||
for (const index of this.unpackedAssocKeys) {
|
||||
buf.writeInt32BE(index);
|
||||
this._packValue(buf, objType.elementType, objType.elementTypeClass,
|
||||
this.unpackedAssocArray.get(index));
|
||||
}
|
||||
} else {
|
||||
buf.writeLength(this.unpackedArray.length);
|
||||
for (const value of this.unpackedArray) {
|
||||
this._packValue(buf, objType.elementType, objType.elementTypeClass,
|
||||
value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const attr of objType.attributes) {
|
||||
this._packValue(buf, attr.type, attr.typeClass,
|
||||
this.unpackedAttrs.get(attr.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _packValue()
|
||||
//
|
||||
// Packs a value into the buffer. At this point it is assumed that the value
|
||||
// matches the correct type.
|
||||
//---------------------------------------------------------------------------
|
||||
_packValue(buf, type, typeClass, value) {
|
||||
if (value === null || value === undefined) {
|
||||
if (typeClass && !typeClass.prototype.isCollection) {
|
||||
buf.writeUInt8(constants.TNS_OBJ_ATOMIC_NULL);
|
||||
} else {
|
||||
buf.writeUInt8(constants.TNS_NULL_LENGTH_INDICATOR);
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case types.DB_TYPE_CHAR:
|
||||
case types.DB_TYPE_VARCHAR:
|
||||
buf.writeBytesWithLength(Buffer.from(value));
|
||||
break;
|
||||
case types.DB_TYPE_NCHAR:
|
||||
case types.DB_TYPE_NVARCHAR:
|
||||
buf.writeBytesWithLength(Buffer.From(value, 'utf16-le').swap16());
|
||||
break;
|
||||
case types.DB_TYPE_NUMBER:
|
||||
buf.writeOracleNumber(value.toString());
|
||||
break;
|
||||
case types.DB_TYPE_BINARY_INTEGER:
|
||||
case types.DB_TYPE_BOOLEAN:
|
||||
buf.writeUInt8(4);
|
||||
buf.writeUInt32BE(value);
|
||||
break;
|
||||
case types.DB_TYPE_RAW:
|
||||
buf.writeBytesWithLength(value);
|
||||
break;
|
||||
case types.DB_TYPE_BINARY_DOUBLE:
|
||||
buf.writeBinaryDouble(value);
|
||||
break;
|
||||
case types.DB_TYPE_BINARY_FLOAT:
|
||||
buf.writeBinaryFloat(value);
|
||||
break;
|
||||
case types.DB_TYPE_DATE:
|
||||
case types.DB_TYPE_TIMESTAMP:
|
||||
case types.DB_TYPE_TIMESTAMP_LTZ:
|
||||
case types.DB_TYPE_TIMESTAMP_TZ:
|
||||
buf.writeOracleDate(value, type);
|
||||
break;
|
||||
case types.DB_TYPE_BLOB:
|
||||
case types.DB_TYPE_CLOB:
|
||||
case types.DB_TYPE_NCLOB:
|
||||
buf.writeLob(value);
|
||||
break;
|
||||
case types.DB_TYPE_OBJECT:
|
||||
if (this._objType.isCollection || value._objType.isCollection) {
|
||||
buf.writeBytesWithLength(value._getPackedData());
|
||||
} else {
|
||||
value._packData(buf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errors.throwErr(errors.ERR_NOT_IMPLEMENTED, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _unpackData()
|
||||
//
|
||||
// Unpacks the packed data into a map of JavaScript values.
|
||||
//---------------------------------------------------------------------------
|
||||
_unpackData() {
|
||||
const buf = new DbObjectPickleBuffer(this.packedData);
|
||||
buf.readHeader(this);
|
||||
this._unpackDataFromBuf(buf);
|
||||
this.packedData = undefined;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _unpackDataFromBuf()
|
||||
//
|
||||
// Unpacks the data in the buffer into a map of JavaScript values.
|
||||
//---------------------------------------------------------------------------
|
||||
_unpackDataFromBuf(buf) {
|
||||
let unpackedArray, unpackedAssocArray, assocIndex, unpackedAttrs;
|
||||
const objType = this._objType;
|
||||
if (objType.isCollection) {
|
||||
if (objType.collectionType === constants.TNS_OBJ_PLSQL_INDEX_TABLE) {
|
||||
unpackedAssocArray = new Map();
|
||||
} else {
|
||||
unpackedArray = [];
|
||||
}
|
||||
this.collectionFlags = buf.readUInt8();
|
||||
const numElements = buf.readLength();
|
||||
for (let i = 0; i < numElements; i++) {
|
||||
if (objType.collectionType === constants.TNS_OBJ_PLSQL_INDEX_TABLE) {
|
||||
assocIndex = buf.readUInt32BE();
|
||||
}
|
||||
const value = this._unpackValue(buf, objType.elementType,
|
||||
objType.elementTypeClass);
|
||||
if (objType.collectionType === constants.TNS_OBJ_PLSQL_INDEX_TABLE) {
|
||||
unpackedAssocArray.set(assocIndex, value);
|
||||
} else {
|
||||
unpackedArray.push(value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unpackedAttrs = new Map();
|
||||
for (const attr of objType.attributes) {
|
||||
const value = this._unpackValue(buf, attr.type, attr.typeClass);
|
||||
unpackedAttrs.set(attr.name, value);
|
||||
}
|
||||
}
|
||||
this.unpackedAttrs = unpackedAttrs;
|
||||
this.unpackedArray = unpackedArray;
|
||||
this.unpackedAssocArray = unpackedAssocArray;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// _unpackValue()
|
||||
//
|
||||
// Unpacks a single value and returns it.
|
||||
//---------------------------------------------------------------------------
|
||||
_unpackValue(buf, type, typeClass) {
|
||||
let isNull, obj, value;
|
||||
switch (type) {
|
||||
case types.DB_TYPE_NUMBER:
|
||||
value = buf.readOracleNumber();
|
||||
if (value !== null)
|
||||
value = parseFloat(value);
|
||||
return value;
|
||||
case types.DB_TYPE_BINARY_INTEGER:
|
||||
return buf.readBinaryInteger();
|
||||
case types.DB_TYPE_VARCHAR:
|
||||
case types.DB_TYPE_CHAR:
|
||||
return buf.readStr(constants.TNS_CS_IMPLICIT);
|
||||
case types.DB_TYPE_NVARCHAR:
|
||||
case types.DB_TYPE_NCHAR:
|
||||
return buf.readStr(constants.TNS_CS_NCHAR);
|
||||
case types.DB_TYPE_RAW:
|
||||
return buf.readBytes();
|
||||
case types.DB_TYPE_BINARY_DOUBLE:
|
||||
return buf.readBinaryDouble();
|
||||
case types.DB_TYPE_BINARY_FLOAT:
|
||||
return buf.readBinaryFloat();
|
||||
case types.DB_TYPE_DATE:
|
||||
case types.DB_TYPE_TIMESTAMP:
|
||||
return buf.readOracleDate(true);
|
||||
case types.DB_TYPE_TIMESTAMP_LTZ:
|
||||
case types.DB_TYPE_TIMESTAMP_TZ:
|
||||
return buf.readOracleDate(false);
|
||||
case types.DB_TYPE_BLOB:
|
||||
case types.DB_TYPE_CLOB:
|
||||
case types.DB_TYPE_NCLOB:
|
||||
return buf.readLob(this.type.typeClass._connection, type);
|
||||
case types.DB_TYPE_BOOLEAN:
|
||||
return buf.readBool();
|
||||
case types.DB_TYPE_OBJECT:
|
||||
isNull = buf.getIsAtomicNull();
|
||||
if (isNull)
|
||||
return null;
|
||||
obj = new ThinDbObjectImpl(typeClass);
|
||||
if (obj._objType.isCollection || this._objType.isCollection) {
|
||||
obj.packedData = buf.readBytesWithLength();
|
||||
} else {
|
||||
obj._unpackDataFromBuf(buf);
|
||||
}
|
||||
return obj;
|
||||
default:
|
||||
errors.throwErr(errors.ERR_NOT_IMPLEMENTED, type);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// append()
|
||||
//
|
||||
// Appends an element to the collection.
|
||||
//---------------------------------------------------------------------------
|
||||
append(value) {
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
const objType = this._objType;
|
||||
if (objType.maxNumElements > 0 &&
|
||||
this.unpackedArray.length >= objType.maxNumElements) {
|
||||
errors.throwErr(errors.ERR_INVALID_COLL_INDEX_SET,
|
||||
this.unpackedArray.length, 0, objType.maxNumElements);
|
||||
}
|
||||
this.unpackedArray.push(value);
|
||||
} else {
|
||||
this._ensureAssocKeys();
|
||||
let newIndex;
|
||||
if (this.unpackedAssocKeys.length === 0) {
|
||||
newIndex = 0;
|
||||
} else {
|
||||
const keyIndex = this.unpackedAssocKeys.length - 1;
|
||||
newIndex = this.unpackedAssocKeys[keyIndex] + 1;
|
||||
}
|
||||
this.unpackedAssocArray.set(newIndex, value);
|
||||
this.unpackedAssocKeys.push(newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// deleteElement()
|
||||
//
|
||||
// Deletes an element from a collection.
|
||||
//---------------------------------------------------------------------------
|
||||
deleteElement(index) {
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
if (this._objType.collectionType == constants.TNS_OBJ_VARRAY) {
|
||||
errors.throwErr(errors.ERR_DELETE_ELEMENTS_OF_VARRAY);
|
||||
}
|
||||
this.unpackedArray.splice(index, 1);
|
||||
} else {
|
||||
this._unpackedAssocKeys = undefined;
|
||||
this.unpackedAssocArray.delete(index);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getAttrValue()
|
||||
//
|
||||
// Returns the value of the given attribute on the object.
|
||||
//---------------------------------------------------------------------------
|
||||
getAttrValue(attr) {
|
||||
this._ensureUnpacked();
|
||||
const value = this.unpackedAttrs.get(attr.name);
|
||||
if (value === undefined)
|
||||
return null;
|
||||
return value;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getElement()
|
||||
//
|
||||
// Returns an element from the collection.
|
||||
//---------------------------------------------------------------------------
|
||||
getElement(index) {
|
||||
let value;
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
value = this.unpackedArray[index];
|
||||
} else {
|
||||
value = this.unpackedAssocArray.get(index);
|
||||
}
|
||||
if (value === undefined) {
|
||||
errors.throwErr(errors.ERR_INVALID_COLL_INDEX_GET, index);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getFirstIndex()
|
||||
//
|
||||
// Returns the first index in a collection.
|
||||
//---------------------------------------------------------------------------
|
||||
getFirstIndex() {
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
return 0;
|
||||
} else if (this.unpackedAssocArray) {
|
||||
this._ensureAssocKeys();
|
||||
return this.unpackedAssocKeys[0];
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getKeys()
|
||||
//
|
||||
// Returns the keys of the collection in a JavaScript array.
|
||||
//---------------------------------------------------------------------------
|
||||
getKeys() {
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
return Array.from(this.unpackedArray.keys());
|
||||
} else if (this.unpackedAssocArray) {
|
||||
this._ensureAssocKeys();
|
||||
return Array.from(this.unpackedAssocKeys);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getLastIndex()
|
||||
//
|
||||
// Returns the last index in a collection.
|
||||
//---------------------------------------------------------------------------
|
||||
getLastIndex() {
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
if (this.unpackedArray.length > 0)
|
||||
return this.unpackedArray.length - 1;
|
||||
} else if (this.unpackedAssocArray) {
|
||||
this._ensureAssocKeys();
|
||||
return this.unpackedAssocKeys[this.unpackedAssocKeys.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getNextIndex()
|
||||
//
|
||||
// Returns the next index in a collection.
|
||||
//---------------------------------------------------------------------------
|
||||
getNextIndex(index) {
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
if (index + 1 < this.unpackedArray.length) {
|
||||
return index + 1;
|
||||
}
|
||||
} else if (this.unpackedAssocArray) {
|
||||
this._ensureAssocKeys();
|
||||
for (const key of this.unpackedAssocKeys) {
|
||||
if (key > index)
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getPrevIndex()
|
||||
//
|
||||
// Returns the previous index in a collection.
|
||||
//---------------------------------------------------------------------------
|
||||
getPrevIndex(index) {
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
if (index > 0) {
|
||||
return index - 1;
|
||||
}
|
||||
} else if (this.unpackedAssocArray) {
|
||||
this._ensureAssocKeys();
|
||||
for (const key of this.unpackedAssocKeys.reverse()) {
|
||||
if (key < index)
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// getValues()
|
||||
//
|
||||
// Returns the values of the collection in a JavaScript array.
|
||||
//---------------------------------------------------------------------------
|
||||
getValues() {
|
||||
const result = [];
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
return Array.from(this.unpackedArray);
|
||||
} else if (this.unpackedAssocArray) {
|
||||
this._ensureAssocKeys();
|
||||
for (const key of this.unpackedAssocKeys) {
|
||||
result.push(this.unpackedAssocArray.get(key));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// hasElement()
|
||||
//
|
||||
// Returns whether an element exists at the given index.
|
||||
//---------------------------------------------------------------------------
|
||||
hasElement(index) {
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
return (index >= 0 && index < this.unpackedArray.length);
|
||||
}
|
||||
return this.unpackedAssocArray.has(index);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// setAttrValue()
|
||||
//
|
||||
// Sets the value of the attribute on the object to the given value.
|
||||
//---------------------------------------------------------------------------
|
||||
setAttrValue(attr, value) {
|
||||
this._ensureUnpacked();
|
||||
this.unpackedAttrs.set(attr.name, value);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// setElement()
|
||||
//
|
||||
// Sets an entry in a collection that is indexed by integers.
|
||||
//---------------------------------------------------------------------------
|
||||
setElement(index, value) {
|
||||
this._ensureUnpacked();
|
||||
if (this.unpackedArray) {
|
||||
const maxIndex = Math.max(this.unpackedArray.length - 1, 0);
|
||||
if (index > maxIndex) {
|
||||
errors.throwErr(errors.ERR_INVALID_COLL_INDEX_SET, index, 0, maxIndex);
|
||||
}
|
||||
this.unpackedArray[index] = value;
|
||||
} else {
|
||||
if (!this.unpackedAssocArray.has(index))
|
||||
this.unpackedAssocKeys = undefined;
|
||||
this.unpackedAssocArray.set(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// trim()
|
||||
//
|
||||
// Trim the specified number of elements from the end of the collection.
|
||||
//---------------------------------------------------------------------------
|
||||
trim(numToTrim) {
|
||||
this._ensureUnpacked();
|
||||
if (numToTrim > 0) {
|
||||
this.unpackedArray = this.unpackedArray.slice(0,
|
||||
this.unpackedArray.length - numToTrim);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ThinDbObjectImpl;
|
|
@ -30,9 +30,11 @@ const ThinConnectionImpl = require('./connection.js');
|
|||
const ThinResultSetImpl = require('./resultSet.js');
|
||||
const ThinPoolImpl = require('./pool.js');
|
||||
const ThinLobImpl = require('./lob.js');
|
||||
const ThinDbObjectImpl = require('./dbObject.js');
|
||||
|
||||
const impl = require('../impl');
|
||||
impl.ConnectionImpl = ThinConnectionImpl;
|
||||
impl.ResultSetImpl = ThinResultSetImpl;
|
||||
impl.PoolImpl = ThinPoolImpl;
|
||||
impl.LobImpl = ThinLobImpl;
|
||||
impl.DbObjectImpl = ThinDbObjectImpl;
|
||||
|
|
|
@ -194,7 +194,7 @@ class BaseBuffer {
|
|||
// is assumed at this point that the buffer only contains the encoded numeric
|
||||
// data.
|
||||
//---------------------------------------------------------------------------
|
||||
parseOracleNumber(buf, desiredType) {
|
||||
parseOracleNumber(buf) {
|
||||
|
||||
// the first byte is the exponent; positive numbers have the highest
|
||||
// order bit set, whereas negative numbers have the highest order bit
|
||||
|
@ -211,9 +211,9 @@ class BaseBuffer {
|
|||
// of -1e126 (if negative)
|
||||
if (buf.length === 1) {
|
||||
if (isPositive) {
|
||||
return 0;
|
||||
return "0";
|
||||
}
|
||||
return -1e126;
|
||||
return "-1e126";
|
||||
}
|
||||
|
||||
// check for the trailing 102 byte for negative numbers and, if present,
|
||||
|
@ -289,11 +289,7 @@ class BaseBuffer {
|
|||
}
|
||||
|
||||
// convert result to a Number
|
||||
const text = chars.join("");
|
||||
if (desiredType === types.DB_TYPE_VARCHAR) {
|
||||
return text;
|
||||
}
|
||||
return parseFloat(text);
|
||||
return chars.join("");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -302,16 +298,12 @@ class BaseBuffer {
|
|||
// Reads a binary double value from the buffer and returns a Number or a
|
||||
// String, depending on the desired type.
|
||||
//---------------------------------------------------------------------------
|
||||
readBinaryDouble(desiredType) {
|
||||
readBinaryDouble() {
|
||||
const buf = this.readBytesWithLength();
|
||||
if (!buf) {
|
||||
return null;
|
||||
}
|
||||
const val = this.parseBinaryDouble(buf);
|
||||
if (desiredType === types.DB_TYPE_VARCHAR) {
|
||||
return val.toString();
|
||||
}
|
||||
return val;
|
||||
return this.parseBinaryDouble(buf);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -320,16 +312,12 @@ class BaseBuffer {
|
|||
// Reads a binary float value from the buffer and returns a Number or a
|
||||
// String, depending on the desired type.
|
||||
//---------------------------------------------------------------------------
|
||||
readBinaryFloat(desiredType) {
|
||||
readBinaryFloat() {
|
||||
const buf = this.readBytesWithLength();
|
||||
if (!buf) {
|
||||
return null;
|
||||
}
|
||||
const val = this.parseBinaryFloat(buf);
|
||||
if (desiredType === types.DB_TYPE_VARCHAR) {
|
||||
return val.toString();
|
||||
}
|
||||
return val;
|
||||
return this.parseBinaryFloat(buf);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -376,6 +364,31 @@ class BaseBuffer {
|
|||
return this._readBytesWithLength(numBytes);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// readDbObject()
|
||||
//
|
||||
// Reads a database object from the buffer and returns the implementation
|
||||
// object (or null, if the object is atomically null).
|
||||
//---------------------------------------------------------------------------
|
||||
readDbObject() {
|
||||
const obj = {};
|
||||
let numBytes = this.readUB4();
|
||||
if (numBytes > 0)
|
||||
obj.toid = this.readBytesWithLength();
|
||||
numBytes = this.readUB4();
|
||||
if (numBytes > 0)
|
||||
obj.oid = this.readBytesWithLength();
|
||||
numBytes = this.readUB4();
|
||||
if (numBytes > 0)
|
||||
obj.snapshot = this.readBytesWithLength();
|
||||
this.skipUB2(); // version
|
||||
numBytes = this.readUB4();
|
||||
this.skipUB2(); // flags
|
||||
if (numBytes > 0)
|
||||
obj.packedData = this.readBytesWithLength();
|
||||
return obj;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// readInt8()
|
||||
//
|
||||
|
@ -392,16 +405,12 @@ class BaseBuffer {
|
|||
// Reads an Oracle date from the buffer and returns a Date or a String,
|
||||
// depending on the desired type.
|
||||
//---------------------------------------------------------------------------
|
||||
readOracleDate(desiredType, useLocalTime) {
|
||||
readOracleDate(useLocalTime) {
|
||||
const buf = this.readBytesWithLength();
|
||||
if (!buf) {
|
||||
return null;
|
||||
}
|
||||
const val = this.parseOracleDate(buf, useLocalTime);
|
||||
if (desiredType === types.DB_TYPE_VARCHAR) {
|
||||
return val.toString();
|
||||
}
|
||||
return val;
|
||||
return this.parseOracleDate(buf, useLocalTime);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -410,12 +419,12 @@ class BaseBuffer {
|
|||
// Reads an Oracle number from the buffer and returns a Number or a String,
|
||||
// depending on the desired type.
|
||||
//---------------------------------------------------------------------------
|
||||
readOracleNumber(desiredType) {
|
||||
readOracleNumber() {
|
||||
const buf = this.readBytesWithLength();
|
||||
if (!buf) {
|
||||
return null;
|
||||
}
|
||||
return this.parseOracleNumber(buf, desiredType);
|
||||
return this.parseOracleNumber(buf);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -692,6 +701,28 @@ class BaseBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// writeDbObject()
|
||||
//
|
||||
// Writes a database object to the buffer.
|
||||
//---------------------------------------------------------------------------
|
||||
writeDbObject(obj) {
|
||||
this.writeUB4(obj.toid.length);
|
||||
this.writeBytesWithLength(obj.toid);
|
||||
if (obj.oid) {
|
||||
this.writeUB4(obj.oid.length);
|
||||
this.writeBytesWithLength(obj.oid);
|
||||
} else {
|
||||
this.writeUB4(0);
|
||||
}
|
||||
this.writeUB4(0); // snapshot
|
||||
this.writeUB4(0); // version
|
||||
const packedData = obj._getPackedData();
|
||||
this.writeUB4(packedData.length);
|
||||
this.writeUB4(obj.flags);
|
||||
this.writeBytesWithLength(packedData);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// writeOracleDate()
|
||||
//
|
||||
|
@ -858,6 +889,16 @@ class BaseBuffer {
|
|||
this.writeBytes(Buffer.from(s));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// writeInt32BE()
|
||||
//
|
||||
// Writes a signed 32-bit integer to the buffer in big endian order.
|
||||
//---------------------------------------------------------------------------
|
||||
writeInt32BE(n) {
|
||||
const buf = this.reserveBytes(4);
|
||||
buf.writeInt32BE(n);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// writeUB4()
|
||||
//
|
||||
|
@ -965,9 +1006,13 @@ class GrowableBuffer extends BaseBuffer {
|
|||
//
|
||||
// Initializes the buffer with an initial fixed chunk size.
|
||||
//---------------------------------------------------------------------------
|
||||
constructor() {
|
||||
super(constants.BUFFER_CHUNK_SIZE);
|
||||
this.size = this.maxSize;
|
||||
constructor(initializer) {
|
||||
if (initializer) {
|
||||
super(initializer);
|
||||
} else {
|
||||
super(constants.BUFFER_CHUNK_SIZE);
|
||||
this.size = this.maxSize;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
|
|
@ -33,6 +33,8 @@ module.exports = {
|
|||
// constants from upper level exposed here in order to avoid having multiple
|
||||
// files containing constants
|
||||
BIND_IN: constants.BIND_IN,
|
||||
BIND_INOUT: constants.BIND_INOUT,
|
||||
BIND_OUT: constants.BIND_OUT,
|
||||
CLIENT_VERSION:
|
||||
constants.VERSION_MAJOR << 24 |
|
||||
constants.VERSION_MINOR << 20 |
|
||||
|
@ -696,15 +698,52 @@ module.exports = {
|
|||
TZ_HOUR_OFFSET: 20,
|
||||
TZ_MINUTE_OFFSET: 60,
|
||||
|
||||
// Preferred Num Types
|
||||
NUM_TYPE_FLOAT: 0,
|
||||
NUM_TYPE_INT: 1,
|
||||
NUM_TYPE_DECIMAL: 2,
|
||||
NUM_TYPE_STR: 3,
|
||||
|
||||
// drcp release mode
|
||||
DRCP_DEAUTHENTICATE: 0x00000002,
|
||||
|
||||
// database object image flags
|
||||
TNS_OBJ_IS_VERSION_81: 0x80,
|
||||
TNS_OBJ_IS_DEGENERATE: 0x10,
|
||||
TNS_OBJ_IS_COLLECTION: 0x08,
|
||||
TNS_OBJ_NO_PREFIX_SEG: 0x04,
|
||||
TNS_OBJ_IMAGE_VERSION: 1,
|
||||
|
||||
// database object flags
|
||||
TNS_OBJ_MAX_SHORT_LENGTH: 245,
|
||||
TNS_OBJ_ATOMIC_NULL: 253,
|
||||
TNS_OBJ_NON_NULL_OID: 0x02,
|
||||
TNS_OBJ_HAS_EXTENT_OID: 0x08,
|
||||
TNS_OBJ_TOP_LEVEL: 0x01,
|
||||
TNS_OBJ_HAS_INDEXES: 0x10,
|
||||
|
||||
// database object collection types
|
||||
TNS_OBJ_PLSQL_INDEX_TABLE: 1,
|
||||
TNS_OBJ_NESTED_TABLE: 2,
|
||||
TNS_OBJ_VARRAY: 3,
|
||||
|
||||
// database object TDS type codes
|
||||
TNS_OBJ_TDS_TYPE_CHAR: 1,
|
||||
TNS_OBJ_TDS_TYPE_DATE: 2,
|
||||
TNS_OBJ_TDS_TYPE_FLOAT: 5,
|
||||
TNS_OBJ_TDS_TYPE_NUMBER: 6,
|
||||
TNS_OBJ_TDS_TYPE_VARCHAR: 7,
|
||||
TNS_OBJ_TDS_TYPE_BOOLEAN: 8,
|
||||
TNS_OBJ_TDS_TYPE_RAW: 19,
|
||||
TNS_OBJ_TDS_TYPE_TIMESTAMP: 21,
|
||||
TNS_OBJ_TDS_TYPE_TIMESTAMP_TZ: 23,
|
||||
TNS_OBJ_TDS_TYPE_OBJ: 27,
|
||||
TNS_OBJ_TDS_TYPE_COLL: 28,
|
||||
TNS_OBJ_TDS_TYPE_CLOB: 29,
|
||||
TNS_OBJ_TDS_TYPE_BLOB: 30,
|
||||
TNS_OBJ_TDS_TYPE_TIMESTAMP_LTZ: 33,
|
||||
TNS_OBJ_TDS_TYPE_BINARY_FLOAT: 37,
|
||||
TNS_OBJ_TDS_TYPE_BINARY_DOUBLE: 45,
|
||||
|
||||
// xml type constants
|
||||
TNS_XML_TYPE_LOB: 0x0001,
|
||||
TNS_XML_TYPE_STRING: 0x0004,
|
||||
TNS_XML_TYPE_FLAG_SKIP_NEXT_4: 0x100000,
|
||||
|
||||
// errors
|
||||
TNS_ERR_VAR_NOT_IN_SELECT_LIST: 1007,
|
||||
TNS_ERR_INBAND_MESSAGE: 12573,
|
||||
|
@ -720,5 +759,6 @@ module.exports = {
|
|||
BUFFER_CHUNK_SIZE: 65536,
|
||||
CHUNKED_BYTES_CHUNK_SIZE: 65536,
|
||||
|
||||
TNS_BASE64_ALPHABET_ARRAY: Buffer.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
|
||||
TNS_BASE64_ALPHABET_ARRAY: Buffer.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'),
|
||||
TNS_EXTENT_OID: Buffer.from('00000000000000000000000000010001', 'hex')
|
||||
};
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
const utils = require("../utils");
|
||||
const constants = require("../constants.js");
|
||||
const Message = require("./base.js");
|
||||
const ThinDbObjectImpl = require("../../dbObject.js");
|
||||
const ThinLobImpl = require("../../lob.js");
|
||||
const errors = require('../../../errors');
|
||||
const types = require('../../../types.js');
|
||||
|
@ -54,6 +55,7 @@ class MessageWithData extends Message {
|
|||
this.outVariables = [];
|
||||
this.inFetch = false;
|
||||
this.parseOnly = false;
|
||||
this.resultSetsToSetup = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,11 +130,11 @@ class MessageWithData extends Message {
|
|||
if (statement.numQueryVars > 0) {
|
||||
buf.skipUB1();
|
||||
}
|
||||
const metadata = [];
|
||||
resultSet.metadata = [];
|
||||
for (let i = 0; i < statement.numQueryVars; i++) {
|
||||
const variable = this.processColumnInfo(buf, i + 1);
|
||||
statement.queryVars.push(variable);
|
||||
metadata.push(variable.fetchInfo);
|
||||
resultSet.metadata.push(variable.fetchInfo);
|
||||
}
|
||||
|
||||
let numBytes = buf.readUB4();
|
||||
|
@ -148,25 +150,7 @@ class MessageWithData extends Message {
|
|||
buf.skipBytes(numBytes + 1);
|
||||
}
|
||||
|
||||
resultSet._setup(this.options, metadata);
|
||||
for (let i = 0; i < metadata.length; i++) {
|
||||
const variable = statement.queryVars[i];
|
||||
|
||||
// LOBs always require define and they change the type that is actually
|
||||
// returned by the server
|
||||
if (variable.type === types.DB_TYPE_CLOB ||
|
||||
variable.type === types.DB_TYPE_NCLOB ||
|
||||
variable.type === types.DB_TYPE_BLOB ||
|
||||
variable.type === types.DB_TYPE_JSON) {
|
||||
if (variable.type !== variable.fetchInfo.fetchType) {
|
||||
variable.type = variable.fetchInfo.fetchType;
|
||||
variable.maxSize = constants.TNS_MAX_LONG_LENGTH;
|
||||
}
|
||||
statement.requiresDefine = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.resultSetsToSetup.push(resultSet);
|
||||
}
|
||||
|
||||
processColumnInfo(buf, columnNum) {
|
||||
|
@ -186,13 +170,14 @@ class MessageWithData extends Message {
|
|||
const maxSize = buf.readUB4();
|
||||
buf.skipUB4(); // max number of array elements
|
||||
buf.skipUB4(); // cont flags
|
||||
let oid;
|
||||
let numBytes = buf.readUB4(); // OID
|
||||
if (numBytes > 0) {
|
||||
buf.skipBytes(numBytes + 1);
|
||||
oid = buf.readBytesWithLength();
|
||||
}
|
||||
buf.skipUB2(); // version
|
||||
buf.skipUB2(); // character set id
|
||||
const csfrm = buf.readUInt8(); // character set form
|
||||
const csfrm = buf.readUInt8(); // character set form
|
||||
let size = buf.readUB4();
|
||||
if (dataType === constants.TNS_DATA_TYPE_RAW) {
|
||||
size = maxSize;
|
||||
|
@ -207,15 +192,15 @@ class MessageWithData extends Message {
|
|||
if (numBytes > 0) {
|
||||
name = buf.readStr(constants.TNS_CS_IMPLICIT);
|
||||
}
|
||||
let schema;
|
||||
numBytes = buf.readUB4();
|
||||
if (numBytes > 0) {
|
||||
buf.skipUB1(); // skip repeated length
|
||||
buf.skipBytes(numBytes); // schema name
|
||||
schema = buf.readStr(constants.TNS_CS_IMPLICIT);
|
||||
}
|
||||
numBytes = buf.readUB4();
|
||||
let typeName;
|
||||
if (numBytes > 0) {
|
||||
buf.skipUB1(); // skip repeated length
|
||||
buf.skipBytes(numBytes); // type name
|
||||
typeName = buf.readStr(constants.TNS_CS_IMPLICIT);
|
||||
}
|
||||
buf.skipUB2(); // column position
|
||||
buf.skipUB4(); // uds flag
|
||||
|
@ -242,6 +227,13 @@ class MessageWithData extends Message {
|
|||
case types.DB_TYPE_TIMESTAMP_LTZ:
|
||||
fetchInfo.precision = scale;
|
||||
break;
|
||||
case types.DB_TYPE_OBJECT:
|
||||
fetchInfo.dbTypeClass = this.connection._getDbObjectType(schema,
|
||||
typeName, undefined, oid);
|
||||
if (fetchInfo.dbTypeClass.partial) {
|
||||
this.connection._partialDbObjectTypes.push(fetchInfo.dbTypeClass);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -356,8 +348,6 @@ class MessageWithData extends Message {
|
|||
const oraTypeNum = dbType._oraTypeNum;
|
||||
const csfrm = dbType._csfrm;
|
||||
const maxSize = variable.maxSize;
|
||||
const outputType = (variable.fetchInfo) ? variable.fetchInfo.fetchType :
|
||||
variable.type;
|
||||
|
||||
let colValue = null;
|
||||
if (maxSize === 0 && oraTypeNum !== constants.TNS_DATA_TYPE_LONG
|
||||
|
@ -380,7 +370,9 @@ class MessageWithData extends Message {
|
|||
colValue = Buffer.from(colValue);
|
||||
}
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_NUMBER) {
|
||||
colValue = buf.readOracleNumber(outputType);
|
||||
colValue = buf.readOracleNumber();
|
||||
if (!this.inFetch && colValue !== null)
|
||||
colValue = parseFloat(colValue);
|
||||
} else if (
|
||||
oraTypeNum === constants.TNS_DATA_TYPE_DATE ||
|
||||
oraTypeNum === constants.TNS_DATA_TYPE_TIMESTAMP ||
|
||||
|
@ -389,7 +381,7 @@ class MessageWithData extends Message {
|
|||
) {
|
||||
const useLocalTime = (oraTypeNum === constants.TNS_DATA_TYPE_DATE ||
|
||||
oraTypeNum === constants.TNS_DATA_TYPE_TIMESTAMP);
|
||||
colValue = buf.readOracleDate(outputType, useLocalTime);
|
||||
colValue = buf.readOracleDate(useLocalTime);
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_ROWID) {
|
||||
if (!this.inFetch) {
|
||||
colValue = buf.readStr(constants.TNS_CS_IMPLICIT);
|
||||
|
@ -409,11 +401,13 @@ class MessageWithData extends Message {
|
|||
colValue = buf.readURowID();
|
||||
}
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_BINARY_DOUBLE) {
|
||||
colValue = buf.readBinaryDouble(outputType);
|
||||
colValue = buf.readBinaryDouble();
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_BINARY_FLOAT) {
|
||||
colValue = buf.readBinaryFloat(outputType);
|
||||
colValue = buf.readBinaryFloat();
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_BINARY_INTEGER) {
|
||||
colValue = buf.readOracleNumber(constants.NUM_TYPE_INT);
|
||||
colValue = buf.readOracleNumber();
|
||||
if (colValue !== null)
|
||||
colValue = parseFloat(colValue);
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_CURSOR) {
|
||||
let numBytes = buf.readUInt8();
|
||||
if (isNullLength(numBytes)) {
|
||||
|
@ -424,7 +418,11 @@ class MessageWithData extends Message {
|
|||
// If the cursor ID is 0 for the returned ref cursor then
|
||||
// it is an invalid cursor
|
||||
if (colValue.statement.cursorId === 0 && variable.dir !== constants.BIND_IN) {
|
||||
errors.throwErr(errors.ERR_INVALID_REF_CURSOR);
|
||||
if (this.options.nullifyInvalidCursor) {
|
||||
colValue = null;
|
||||
} else {
|
||||
errors.throwErr(errors.ERR_INVALID_REF_CURSOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_BOOLEAN) {
|
||||
|
@ -440,6 +438,15 @@ class MessageWithData extends Message {
|
|||
}
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_JSON) {
|
||||
colValue = buf.readOson();
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_INT_NAMED) {
|
||||
const obj = buf.readDbObject();
|
||||
if (obj.packedData) {
|
||||
const objType = (variable.fetchInfo) ? variable.fetchInfo.dbTypeClass :
|
||||
variable.typeClass;
|
||||
colValue = new ThinDbObjectImpl(objType, obj.packedData);
|
||||
colValue.toid = obj.toid;
|
||||
colValue.oid = obj.oid;
|
||||
}
|
||||
} else {
|
||||
errors.throwErr(errors.ERR_UNSUPPORTED_DATA_TYPE, dbType.num,
|
||||
variable.columnNum);
|
||||
|
@ -526,6 +533,24 @@ class MessageWithData extends Message {
|
|||
}
|
||||
}
|
||||
}
|
||||
await this.connection._populatePartialDbObjectTypes();
|
||||
for (const resultSet of this.resultSetsToSetup) {
|
||||
resultSet._setup(this.options, resultSet.metadata);
|
||||
// LOBs always require define and they change the type that is actually
|
||||
// returned by the server
|
||||
for (const variable of resultSet.statement.queryVars) {
|
||||
if (variable.type === types.DB_TYPE_CLOB ||
|
||||
variable.type === types.DB_TYPE_NCLOB ||
|
||||
variable.type === types.DB_TYPE_BLOB ||
|
||||
variable.type === types.DB_TYPE_JSON) {
|
||||
if (variable.type !== variable.fetchInfo.fetchType) {
|
||||
variable.type = variable.fetchInfo.fetchType;
|
||||
variable.maxSize = constants.TNS_MAX_LONG_LENGTH;
|
||||
}
|
||||
resultSet.statement.requiresDefine = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preProcess() {
|
||||
|
@ -640,8 +665,15 @@ class MessageWithData extends Message {
|
|||
buf.writeUB4(0); // max num elements
|
||||
}
|
||||
buf.writeUB4(contFlag);
|
||||
buf.writeUB4(0); // OID
|
||||
buf.writeUB4(0); // version
|
||||
if (variable.objType) {
|
||||
const objType = variable.objType;
|
||||
buf.writeUB4(objType.oid.length);
|
||||
buf.writeBytesWithLength(objType.oid);
|
||||
buf.writeUB4(objType.version);
|
||||
} else {
|
||||
buf.writeUB4(0); // OID
|
||||
buf.writeUB4(0); // version
|
||||
}
|
||||
if (variable.type._csfrm !== 0) {
|
||||
buf.writeUB4(constants.TNS_CHARSET_UTF8);
|
||||
} else {
|
||||
|
@ -697,6 +729,13 @@ class MessageWithData extends Message {
|
|||
if (oraTypeNum === constants.TNS_DATA_TYPE_BOOLEAN) {
|
||||
buf.writeUInt8(constants.TNS_ESCAPE_CHAR);
|
||||
buf.writeUInt8(1);
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_INT_NAMED) {
|
||||
buf.writeUB4(0); // TOID
|
||||
buf.writeUB4(0); // OID
|
||||
buf.writeUB4(0); // snapshot
|
||||
buf.writeUB4(0); // version
|
||||
buf.writeUB4(0); // packed data length
|
||||
buf.writeUB4(constants.TNS_OBJ_TOP_LEVEL); // flags
|
||||
} else {
|
||||
buf.writeUInt8(0);
|
||||
}
|
||||
|
@ -757,6 +796,8 @@ class MessageWithData extends Message {
|
|||
buf.writeBytesWithLength(Buffer.from(value));
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_JSON) {
|
||||
buf.writeOson(value);
|
||||
} else if (oraTypeNum === constants.TNS_DATA_TYPE_INT_NAMED) {
|
||||
buf.writeDbObject(value);
|
||||
} else {
|
||||
const message = `Binding data of type ${variable.type}`;
|
||||
errors.throwErr(errors.ERR_NOT_IMPLEMENTED, message);
|
||||
|
|
|
@ -139,7 +139,7 @@ class OsonDecoder extends BaseBuffer {
|
|||
} else if (nodeType === constants.TNS_JSON_TYPE_STRING_LENGTH_UINT32) {
|
||||
return this.readBytes(this.readUInt32BE()).toString();
|
||||
} else if (nodeType === constants.TNS_JSON_TYPE_NUMBER_LENGTH_UINT8) {
|
||||
return this.readOracleNumber(types.DB_TYPE_NUMBER);
|
||||
return parseFloat(this.readOracleNumber());
|
||||
} else if (nodeType === constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT16) {
|
||||
return Buffer.from(this.readBytes(this.readUInt16BE()));
|
||||
} else if (nodeType === constants.TNS_JSON_TYPE_BINARY_LENGTH_UINT32) {
|
||||
|
@ -150,13 +150,12 @@ class OsonDecoder extends BaseBuffer {
|
|||
const typeBits = nodeType & 0xf0;
|
||||
if (typeBits === 0x20 || typeBits === 0x60) {
|
||||
const len = nodeType & 0x0f;
|
||||
return this.parseOracleNumber(this.readBytes(len + 1),
|
||||
types.DB_TYPE_NUMBER);
|
||||
return parseFloat(this.parseOracleNumber(this.readBytes(len + 1)));
|
||||
|
||||
// handle integer with length stored inside the node itself
|
||||
} else if (typeBits === 0x40 || typeBits === 0x50) {
|
||||
const len = nodeType & 0x0f;
|
||||
return this.parseOracleNumber(this.readBytes(len), types.DB_TYPE_NUMBER);
|
||||
return parseFloat(this.parseOracleNumber(this.readBytes(len)));
|
||||
|
||||
// handle string with length stored inside the node itself
|
||||
} else if ((nodeType & 0xe0) == 0) {
|
||||
|
|
28
lib/types.js
28
lib/types.js
|
@ -32,6 +32,7 @@ const util = require('util');
|
|||
|
||||
const dbTypeByNum = new Map();
|
||||
const dbTypeByOraTypeNum = new Map();
|
||||
const dbTypeByColumnTypeName = new Map();
|
||||
|
||||
// define class used for database types
|
||||
class DbType {
|
||||
|
@ -46,6 +47,7 @@ class DbType {
|
|||
dbTypeByNum.set(num, this);
|
||||
const key = (options.csfrm || 0) * 256 + options.oraTypeNum;
|
||||
dbTypeByOraTypeNum.set(key, this);
|
||||
dbTypeByColumnTypeName.set(columnTypeName, this);
|
||||
}
|
||||
|
||||
[Symbol.toPrimitive](hint) {
|
||||
|
@ -67,6 +69,19 @@ class DbType {
|
|||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// getTypeByColumnTypeName()
|
||||
//
|
||||
// Return the type given a column type name. If the column type name cannot be
|
||||
// found an exception is thrown.
|
||||
//-----------------------------------------------------------------------------
|
||||
function getTypeByColumnTypeName(name) {
|
||||
const dbType = dbTypeByColumnTypeName.get(name);
|
||||
if (!dbType)
|
||||
errors.throwErr(errors.ERR_UNKNOWN_COLUMN_TYPE_NAME, name);
|
||||
return dbType;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// getTypeByNum()
|
||||
//
|
||||
|
@ -292,6 +307,18 @@ const DB_TYPE_FETCH_TYPE_MAP = new Map([
|
|||
[DB_TYPE_VARCHAR, DB_TYPE_VARCHAR]
|
||||
]);
|
||||
|
||||
// additional aliases for types by column type name
|
||||
dbTypeByColumnTypeName.set("DOUBLE PRECISION", DB_TYPE_NUMBER);
|
||||
dbTypeByColumnTypeName.set("FLOAT", DB_TYPE_NUMBER);
|
||||
dbTypeByColumnTypeName.set("INTEGER", DB_TYPE_NUMBER);
|
||||
dbTypeByColumnTypeName.set("PL/SQL BOOLEAN", DB_TYPE_BOOLEAN);
|
||||
dbTypeByColumnTypeName.set("PL/SQL BINARY INTEGER", DB_TYPE_BINARY_INTEGER);
|
||||
dbTypeByColumnTypeName.set("PL/SQL PLS INTEGER", DB_TYPE_BINARY_INTEGER);
|
||||
dbTypeByColumnTypeName.set("REAL", DB_TYPE_NUMBER);
|
||||
dbTypeByColumnTypeName.set("SMALLINT", DB_TYPE_NUMBER);
|
||||
dbTypeByColumnTypeName.set("TIMESTAMP WITH LOCAL TZ", DB_TYPE_TIMESTAMP_LTZ);
|
||||
dbTypeByColumnTypeName.set("TIMESTAMP WITH TZ", DB_TYPE_TIMESTAMP_TZ);
|
||||
|
||||
module.exports = {
|
||||
DbType,
|
||||
DB_TYPE_BFILE,
|
||||
|
@ -324,6 +351,7 @@ module.exports = {
|
|||
DB_TYPE_VARCHAR,
|
||||
DB_TYPE_CONVERSION_MAP,
|
||||
DB_TYPE_FETCH_TYPE_MAP,
|
||||
getTypeByColumnTypeName,
|
||||
getTypeByNum,
|
||||
getTypeByOraTypeNum
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('200. dbObject1.js', () => {
|
||||
describe('200. dbObject1.js', () => {
|
||||
|
||||
let conn;
|
||||
const TYPE = 'NODB_TYP_OBJ_1';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('209. dbObject10.js', () => {
|
||||
describe('209. dbObject10.js', () => {
|
||||
|
||||
let conn;
|
||||
const TYPE = 'NODB_PERSON_TYP';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('210. dbObject11.js', () => {
|
||||
describe('210. dbObject11.js', () => {
|
||||
|
||||
let conn;
|
||||
const TYPE = 'NODB_RV_FIELD_TYP';
|
||||
|
|
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('211. dbObject12.js', function() {
|
||||
describe('211. dbObject12.js', function() {
|
||||
|
||||
let isRunnable = false;
|
||||
let conn;
|
||||
|
|
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('212. dbObject13.js', function() {
|
||||
describe('212. dbObject13.js', function() {
|
||||
|
||||
let isRunnable = false;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('213. dbObject14.js', () => {
|
||||
describe('213. dbObject14.js', () => {
|
||||
let conn;
|
||||
|
||||
const TABLE = 'NODB_TAB_SPORTS';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2021, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2021, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -35,7 +35,7 @@ const oracledb = require('oracledb');
|
|||
const assert = require('assert');
|
||||
const dbConfig = require('./dbconfig.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('214. dbObject15.js', () => {
|
||||
describe('214. dbObject15.js', () => {
|
||||
|
||||
let conn, FrisbeeTeam;
|
||||
|
||||
|
@ -104,12 +104,9 @@ const dbConfig = require('./dbconfig.js');
|
|||
|
||||
it('214.3 Negative - delete the collection element directly', function() {
|
||||
assert.throws(
|
||||
function() {
|
||||
delete FrisbeeTeam[1];
|
||||
},
|
||||
/OCI-22164/
|
||||
() => delete FrisbeeTeam[1],
|
||||
/NJS-133:/
|
||||
);
|
||||
// OCI-22164: delete element operation is not allowed for variable-length array
|
||||
}); // 214.3
|
||||
|
||||
it('214.4 Negative - collection.deleteElement()', function() {
|
||||
|
@ -118,7 +115,7 @@ const dbConfig = require('./dbconfig.js');
|
|||
let firstIndex = FrisbeeTeam.getFirstIndex();
|
||||
FrisbeeTeam.deleteElement(firstIndex);
|
||||
},
|
||||
/OCI-22164/
|
||||
/NJS-133:/
|
||||
);
|
||||
}); // 214.4
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('215. dbObject16.js', () => {
|
||||
describe('215. dbObject16.js', () => {
|
||||
|
||||
let conn;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -37,7 +37,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('216. dbObject17.js', () => {
|
||||
describe('216. dbObject17.js', () => {
|
||||
|
||||
let conn;
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('242. dbObject18.js', () => {
|
||||
describe('242. dbObject18.js', () => {
|
||||
|
||||
describe('242.1 set oracledb.dbObjectAsPojo', () => {
|
||||
|
||||
|
@ -418,10 +418,14 @@ const testsUtil = require('./testsUtil.js');
|
|||
const row = result.rows[0];
|
||||
assert.strictEqual(row.SPORTNAME, 'Frisbee');
|
||||
|
||||
assert.throws(
|
||||
() => row.TEAM[0],
|
||||
/NJS-500:/
|
||||
);
|
||||
if (oracledb.thin) {
|
||||
assert.strictEqual(row.TEAM[0].SHIRTNUMBER, 11);
|
||||
} else {
|
||||
assert.throws(
|
||||
() => row.TEAM[0],
|
||||
/NJS-500:/
|
||||
);
|
||||
}
|
||||
|
||||
// restore the connection
|
||||
conn = await oracledb.getConnection(dbConfig);
|
||||
|
|
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('243. dbObject19.js', () => {
|
||||
describe('243. dbObject19.js', () => {
|
||||
let conn;
|
||||
const TYPE = 'NODB_PERSON_T';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('201. dbObject2.js', () => {
|
||||
describe('201. dbObject2.js', () => {
|
||||
|
||||
let conn;
|
||||
const TYPE = 'NODB_TYP_OBJ_2';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('202. dbObject3.js', () => {
|
||||
describe('202. dbObject3.js', () => {
|
||||
|
||||
let conn;
|
||||
const TYPE = 'NODB_TYP_OBJ_3';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('203. dbObject4.js', () => {
|
||||
describe('203. dbObject4.js', () => {
|
||||
let conn;
|
||||
const TYPE = 'NODB_TYP_OBJ_4';
|
||||
const TABLE = 'NODB_TAB_OBJ4';
|
||||
|
@ -87,6 +87,9 @@ const testsUtil = require('./testsUtil.js');
|
|||
)`;
|
||||
let plsql = testsUtil.sqlCreateTable(TABLE, sql);
|
||||
await conn.execute(plsql);
|
||||
await conn.execute(proc1);
|
||||
await conn.execute(proc2);
|
||||
await conn.execute(proc3);
|
||||
}); // before()
|
||||
|
||||
after(async () => {
|
||||
|
@ -193,9 +196,6 @@ const testsUtil = require('./testsUtil.js');
|
|||
}); // 203.4
|
||||
|
||||
it('203.5 call procedure with 2 OUT binds of DbObject', async function() {
|
||||
await conn.execute(proc1);
|
||||
await conn.execute(proc2);
|
||||
await conn.execute(proc3);
|
||||
|
||||
let result = await conn.execute(
|
||||
`BEGIN nodb_getDataCursor3(p_cur1 => :p_cur1,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('204. dbObject5.js', () => {
|
||||
describe('204. dbObject5.js', () => {
|
||||
let conn;
|
||||
const TYPE = 'NODB_TYP_OBJ_4';
|
||||
const TABLE = 'NODB_TAB_OBJ4';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('205. dbObject6.js', () => {
|
||||
describe('205. dbObject6.js', () => {
|
||||
|
||||
let conn;
|
||||
const TABLE = 'NODB_TAB_TESTGEOMETRY';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2019, 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('206. dbObject7.js', () => {
|
||||
describe('206. dbObject7.js', () => {
|
||||
let conn;
|
||||
const TYPE = 'NODB_PERSON_T';
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('207. dbObject8.js', () => {
|
||||
describe('207. dbObject8.js', () => {
|
||||
|
||||
let conn;
|
||||
const TYPE1 = 'NODB_HARVEST_T';
|
||||
|
|
|
@ -36,7 +36,7 @@ const assert = require('assert');
|
|||
const dbConfig = require('./dbconfig.js');
|
||||
const testsUtil = require('./testsUtil.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('208. dbObject9.js', function() {
|
||||
describe('208. dbObject9.js', function() {
|
||||
|
||||
let isRunnable = false;
|
||||
let conn;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2022, Oracle and/or its affiliates. */
|
||||
/* Copyright (c) 2022, 2023, Oracle and/or its affiliates. */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -35,7 +35,7 @@ const oracledb = require('oracledb');
|
|||
const assert = require('assert');
|
||||
const dbConfig = require('./dbconfig.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip)('262. dbObjectOutBind.js', function() {
|
||||
describe('262. dbObjectOutBind.js', function() {
|
||||
let conn = null;
|
||||
let proc1 =
|
||||
`create or replace procedure nodb_getDataCursor1(p_cur out sys_refcursor) is
|
||||
|
|
|
@ -35,7 +35,7 @@ const oracledb = require ('oracledb');
|
|||
const assert = require ('assert');
|
||||
const dbConfig = require ('./dbconfig.js');
|
||||
|
||||
(!oracledb.thin ? describe : describe.skip) ('197. dbObjectsNestedTable.js', () => {
|
||||
describe('197. dbObjectsNestedTable.js', () => {
|
||||
let connection = null;
|
||||
|
||||
before (async () => {
|
||||
|
|
|
@ -57,6 +57,7 @@ describe('162. getStmtInfo.js', function() {
|
|||
it('162.1 SELECT', async function() {
|
||||
const sql = "select 1 as col from dual";
|
||||
const info = await conn.getStatementInfo(sql);
|
||||
delete info.metaData[0].converter;
|
||||
assert.deepStrictEqual(info,
|
||||
{ bindNames: [], statementType: oracledb.STMT_TYPE_SELECT,
|
||||
metaData: [
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
/* Copyright (c) 2023, 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 https://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.
|
||||
*
|
||||
* NAME
|
||||
* 280. pipelinedTables.js
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Testing Pipelined Table Functions
|
||||
*
|
||||
*****************************************************************************/
|
||||
'use strict';
|
||||
|
||||
const oracledb = require('oracledb');
|
||||
const assert = require('assert');
|
||||
const dbConfig = require('./dbconfig.js');
|
||||
|
||||
describe('280. pipelinedTables.js', function() {
|
||||
let connection = null;
|
||||
|
||||
before('Get connection', async function() {
|
||||
connection = await oracledb.getConnection(dbConfig);
|
||||
});
|
||||
|
||||
after('Release connection', async function() {
|
||||
await connection.close();
|
||||
});
|
||||
|
||||
it('280.1 Creating and Invoking Pipelined Table Function', async function() {
|
||||
await connection.execute(`CREATE OR REPLACE PACKAGE pkg1 AUTHID DEFINER AS
|
||||
TYPE numset_t IS TABLE OF NUMBER;
|
||||
FUNCTION f1(x NUMBER) RETURN numset_t PIPELINED;
|
||||
END pkg1;`);
|
||||
|
||||
await connection.execute(`CREATE OR REPLACE PACKAGE BODY pkg1 AS
|
||||
FUNCTION f1(x NUMBER) RETURN numset_t PIPELINED IS
|
||||
BEGIN
|
||||
FOR i IN 1..x LOOP
|
||||
PIPE ROW(i);
|
||||
END LOOP;
|
||||
RETURN;
|
||||
END f1;
|
||||
END pkg1;`);
|
||||
|
||||
let result = await connection.execute(`SELECT * FROM TABLE (pkg1.f1(5))`);
|
||||
assert.deepEqual(result.rows, [[1], [2], [3], [4], [5]]);
|
||||
|
||||
result = await connection.execute(`SELECT * FROM TABLE (pkg1.f1(2))`);
|
||||
assert.deepEqual(result.rows, [[1], [2]]);
|
||||
});
|
||||
|
||||
it('280.2 Invoking Pipelined Table Function with invalid syntax', async function() {
|
||||
await connection.execute(`CREATE OR REPLACE PACKAGE pkg1 AUTHID DEFINER AS
|
||||
TYPE numset_t IS TABLE OF NUMBER;
|
||||
FUNCTION f1(x NUMBER) RETURN numset_t PIPELINED;
|
||||
END pkg1;
|
||||
/`);
|
||||
|
||||
await connection.execute(`CREATE OR REPLACE PACKAGE BODY pkg1 AS
|
||||
FUNCTION f1(x NUMBER) RETURN numset_t PIPELINED IS
|
||||
BEGIN
|
||||
FOR i IN 1..x LOOP
|
||||
PIPE ROW(j);
|
||||
END LOOP;
|
||||
RETURN;
|
||||
END f1;
|
||||
END pkg1;
|
||||
`);
|
||||
|
||||
await assert.rejects(
|
||||
async () => await connection.execute(`SELECT * FROM TABLE (pkg1.f1(5))`),
|
||||
/ORA-06575:/ //ORA-06575: Package or function PKG1 is in an invalid state
|
||||
);
|
||||
});
|
||||
|
||||
it('280.3 Invoking normal Table followed by Pipelined table', async function() {
|
||||
// create a schema-level nested table type of strings
|
||||
await connection.execute(`CREATE OR REPLACE TYPE strings_t IS TABLE OF VARCHAR2 (100)`);
|
||||
|
||||
// compile a table function that returns a nested table of that type with a single string inside it
|
||||
await connection.execute(`CREATE OR REPLACE FUNCTION strings
|
||||
RETURN strings_t
|
||||
AUTHID DEFINER
|
||||
IS
|
||||
l_strings strings_t := strings_t ('abc');
|
||||
BEGIN
|
||||
RETURN l_strings;
|
||||
END;`);
|
||||
|
||||
// call the table function
|
||||
let result = await connection.execute(`SELECT COLUMN_VALUE my_string FROM TABLE (strings ())`);
|
||||
assert.deepEqual(result.rows, [['abc']]);
|
||||
|
||||
// create a pipelined version of that same table function
|
||||
await connection.execute(`CREATE OR REPLACE FUNCTION strings_pl
|
||||
RETURN strings_t
|
||||
PIPELINED
|
||||
AUTHID DEFINER
|
||||
IS
|
||||
BEGIN
|
||||
PIPE ROW ('abc');
|
||||
RETURN;
|
||||
END;`);
|
||||
|
||||
result = await connection.execute(`SELECT COLUMN_VALUE my_string FROM TABLE (strings ())`);
|
||||
assert.strictEqual(result.rows[0][0], 'abc');
|
||||
});
|
||||
|
||||
it('280.4 Pipelined Table Functions with types', async function() {
|
||||
// Create the types to support the Pipelined table function
|
||||
await connection.execute(`CREATE TYPE ptf_row AS OBJECT (
|
||||
id NUMBER,
|
||||
description VARCHAR2(50)
|
||||
)`);
|
||||
|
||||
await connection.execute(`CREATE TYPE ptf_tab IS TABLE OF ptf_row`);
|
||||
|
||||
// Build a pipelined table function
|
||||
await connection.execute(`CREATE OR REPLACE FUNCTION get_tab_ptf (p_rows IN NUMBER) RETURN ptf_tab PIPELINED AS
|
||||
BEGIN
|
||||
FOR i IN 1 .. p_rows LOOP
|
||||
PIPE ROW(ptf_row(i, 'Description for ' || i));
|
||||
END LOOP;
|
||||
RETURN;
|
||||
END;`);
|
||||
|
||||
let result = await connection.execute(`SELECT *
|
||||
FROM TABLE(get_tab_ptf(10))
|
||||
ORDER BY id DESC`);
|
||||
|
||||
assert.deepEqual(result.rows, [[10, "Description for 10"],
|
||||
[9, "Description for 9"],
|
||||
[8, "Description for 8"],
|
||||
[7, "Description for 7"],
|
||||
[6, "Description for 6"],
|
||||
[5, "Description for 5"],
|
||||
[4, "Description for 4"],
|
||||
[3, "Description for 3"],
|
||||
[2, "Description for 2"],
|
||||
[1, "Description for 1"]]);
|
||||
|
||||
await connection.execute(`DROP FUNCTION get_tab_ptf`);
|
||||
await connection.execute(`DROP TYPE ptf_tab`);
|
||||
await connection.execute(`DROP TYPE ptf_row`);
|
||||
});
|
||||
|
||||
it('280.5 Parallel Enabled Pipelined Table Functions', async function() {
|
||||
await connection.execute(`CREATE TABLE parallel_test (
|
||||
id NUMBER(10),
|
||||
country_code VARCHAR2(5),
|
||||
description VARCHAR2(50)
|
||||
)`);
|
||||
|
||||
await connection.execute(`INSERT /*+ APPEND */ INTO parallel_test
|
||||
SELECT level AS id,
|
||||
(CASE TRUNC(MOD(level, 4))
|
||||
WHEN 1 THEN 'IN'
|
||||
WHEN 2 THEN 'UK'
|
||||
ELSE 'US'
|
||||
END) AS country_code,
|
||||
'Description or ' || level AS description
|
||||
FROM dual
|
||||
CONNECT BY level <= 100000`);
|
||||
|
||||
await connection.commit();
|
||||
let result = await connection.execute(`SELECT country_code, count(*) FROM parallel_test GROUP BY country_code ORDER BY country_code ASC`);
|
||||
assert.deepEqual(result.rows, [["IN", 25000], ["UK", 25000], ["US", 50000]]);
|
||||
await connection.execute(`drop table parallel_test PURGE`);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue