Code optimizations with DB objects and error name standardization

This commit is contained in:
Sharad Chandran R 2023-05-23 23:24:53 +05:30
parent 889ca3ca6e
commit 4d05ad311a
11 changed files with 333 additions and 265 deletions

View File

@ -39,6 +39,7 @@ const impl = require('./impl');
const util = require('util');
const constants = require('./constants.js');
const settings = require('./settings.js');
const transformer = require('./transformer.js');
const types = require('./types.js');
// global mapping of subscriptions; these cannot be tied to a particular
@ -128,38 +129,6 @@ class Connection extends EventEmitter {
return (DbObject);
}
//---------------------------------------------------------------------------
// _checkBindType()
//
// Check that the bind type matches one of the given bind types. If the bind
// type has not been specified yet, the first bind type is assumed to be the
// correct one.
//---------------------------------------------------------------------------
_checkBindType(bindInfo, pos) {
if (bindInfo.type === undefined && arguments.length > 2) {
bindInfo.type = arguments[2];
} else {
let matches = false;
for (let i = 2; i < arguments.length; i++) {
if (bindInfo.type === arguments[i]) {
matches = true;
break;
}
}
if (!matches) {
if (bindInfo.isArray && bindInfo.name) {
errors.throwErr(errors.ERR_INCOMPATIBLE_TYPE_ARRAY_BIND, pos,
bindInfo.name);
} else if (bindInfo.isArray) {
errors.throwErr(errors.ERR_INCOMPATIBLE_TYPE_ARRAY_INDEX_BIND, pos,
bindInfo.pos);
} else {
errors.throwErr(errors.ERR_BIND_VALUE_AND_TYPE_MISMATCH);
}
}
}
}
//---------------------------------------------------------------------------
// _getDbObjectClass()
//
@ -327,127 +296,18 @@ class Connection extends EventEmitter {
// on the value and normalizes it for use by the implementation class. If no
// bind info has been defined yet, the value defines that.
//---------------------------------------------------------------------------
async _processBindValue(bindInfo, value, iterNum, allowArray) {
// null and undefined can always be bound so nothing needs to be done
if (value === undefined || value === null) {
bindInfo.values[iterNum] = undefined;
return;
}
// handle binding plain JS values to database objects
if (bindInfo.type === types.DB_TYPE_OBJECT) {
let obj = value;
if (!(value instanceof BaseDbObject)) {
obj = new bindInfo.typeClass(value);
}
bindInfo.values[iterNum] = obj._impl;
// handle binding plain JS values to JSON
} else if (bindInfo.type === types.DB_TYPE_JSON) {
bindInfo.values[iterNum] = this._processJsonValue(value);
// handle binding strings
} else if (typeof value === 'string') {
this._checkBindType(bindInfo, iterNum,
types.DB_TYPE_VARCHAR,
types.DB_TYPE_NVARCHAR,
types.DB_TYPE_CHAR,
types.DB_TYPE_NCHAR,
types.DB_TYPE_CLOB,
types.DB_TYPE_NCLOB);
if (bindInfo.type !== constants.DB_TYPE_CLOB &&
bindInfo.type !== constants.DB_TYPE_NCLOB &&
(bindInfo.maxSize === undefined || value.length > bindInfo.maxSize)) {
if (bindInfo.checkSize) {
errors.throwErr(errors.ERR_MAX_SIZE_TOO_SMALL, bindInfo.maxSize,
value.length, iterNum);
}
bindInfo.maxSize = value.length;
}
bindInfo.values[iterNum] = value;
// handle binding numbers
} else if (typeof value === 'number') {
this._checkBindType(bindInfo, iterNum,
types.DB_TYPE_NUMBER,
types.DB_TYPE_BINARY_INTEGER,
types.DB_TYPE_BINARY_FLOAT,
types.DB_TYPE_BINARY_DOUBLE);
bindInfo.values[iterNum] = value;
if (Number.isNaN(value) && bindInfo.type === types.DB_TYPE_NUMBER) {
errors.throwErr(errors.ERR_NAN_VALUE);
}
// handle binding booleans
} else if (typeof value === 'boolean') {
this._checkBindType(bindInfo, iterNum, types.DB_TYPE_BOOLEAN);
bindInfo.values[iterNum] = value;
// handle binding dates
} else if (util.isDate(value)) {
this._checkBindType(bindInfo, iterNum,
types.DB_TYPE_TIMESTAMP,
types.DB_TYPE_TIMESTAMP_TZ,
types.DB_TYPE_TIMESTAMP_LTZ,
types.DB_TYPE_DATE);
bindInfo.values[iterNum] = value;
// handle binding buffers
} else if (Buffer.isBuffer(value)) {
this._checkBindType(bindInfo, iterNum,
types.DB_TYPE_RAW,
types.DB_TYPE_BLOB);
bindInfo.values[iterNum] = value;
if (bindInfo.type === types.DB_TYPE_RAW &&
(bindInfo.maxSize === undefined || value.length > bindInfo.maxSize)) {
if (bindInfo.checkSize) {
errors.throwErr(errors.ERR_MAX_SIZE_TOO_SMALL, bindInfo.maxSize,
value.length, iterNum);
}
bindInfo.maxSize = value.length;
}
// handle binding result sets
} else if (value instanceof ResultSet) {
this._checkBindType(bindInfo, iterNum, types.DB_TYPE_CURSOR);
bindInfo.values[iterNum] = value._impl;
// handle binding LOBs
} else if (value instanceof Lob) {
this._checkBindType(bindInfo, iterNum, value.type);
bindInfo.values[iterNum] = value._impl;
// handle binding database objects
} else if (value instanceof BaseDbObject) {
this._checkBindType(bindInfo, iterNum, types.DB_TYPE_OBJECT);
if (!bindInfo.typeClass) {
bindInfo.typeClass = await this._getDbObjectClass(value._objType);
bindInfo.objType = bindInfo.typeClass._objType;
}
bindInfo.values[iterNum] = value._impl;
// handle binding arrays
} else if (allowArray && Array.isArray(value)) {
bindInfo.isArray = true;
if (bindInfo.dir === constants.BIND_IN) {
bindInfo.maxArraySize = value.length || 1;
} else if (bindInfo.maxArraySize === undefined) {
errors.throwErr(errors.ERR_REQUIRED_MAX_ARRAY_SIZE);
} else if (value.length > bindInfo.maxArraySize) {
errors.throwErr(errors.ERR_INVALID_ARRAY_SIZE);
}
for (let i = 0; i < value.length; i++) {
await this._processBindValue(bindInfo, value[i], i, false);
}
// no suitable bind value found
async _processBindValue(bindInfo, value, options) {
const transformed = transformer.transformValueIn(bindInfo, value, options);
if (bindInfo.isArray) {
bindInfo.values = transformed.concat(bindInfo.values.slice(transformed.length));
} else {
if (bindInfo.type === undefined)
errors.throwErr(errors.ERR_INVALID_BIND_DATA_TYPE, 2);
this._checkBindType(bindInfo, iterNum);
bindInfo.values[options.pos] = transformed;
}
if (bindInfo.type === types.DB_TYPE_OBJECT &&
bindInfo.typeClass === undefined) {
bindInfo.typeClass = await this._getDbObjectClass(value._objType);
bindInfo.objType = bindInfo.typeClass._objType;
}
}
//---------------------------------------------------------------------------
@ -473,7 +333,8 @@ class Connection extends EventEmitter {
// for IN and IN/OUT binds, process the value
if (bindInfo.dir !== constants.BIND_OUT) {
await this._processBindValue(bindInfo, bindValue, 0, true);
const options = {pos: 0, allowArray: true};
await this._processBindValue(bindInfo, bindValue, options);
}
// if only null values were found (or an OUT bind was specified), type
@ -581,12 +442,13 @@ class Connection extends EventEmitter {
// process each of the rows
for (let i = 0; i < binds.length; i++) {
const row = binds[i];
const options = {pos: i, allowArray: false};
errors.assert((byPosition && Array.isArray(row)) ||
(!byPosition && nodbUtil.isObject(row)), errors.ERR_MIXED_BIND);
for (let j = 0; j < normBinds.length; j++) {
const bindInfo = normBinds[j];
const value = (byPosition) ? row[j] : row[bindInfo.name];
await this._processBindValue(bindInfo, value, i, false);
await this._processBindValue(bindInfo, value, options);
}
}
@ -603,48 +465,6 @@ class Connection extends EventEmitter {
return normBinds;
}
//---------------------------------------------------------------------------
// _processJsonValue()
//
// Returns a normalized JSON value. Scalar values are returned unchanged.
// Arrays are returned as a new array with processed JSON values. Objects are
// returned as new objects with keys "fields" and "values", both of which
// are arrays with processes JSON values.
//---------------------------------------------------------------------------
_processJsonValue(value) {
// handle simple scalars
if (value === undefined || value === null ||
typeof value === 'number' || typeof value === 'string' ||
typeof value === 'boolean' || Buffer.isBuffer(value) ||
util.isDate(value))
return value;
// arrays are transformed to a new array with processed values
if (Array.isArray(value)) {
const outValue = new Array(value.length);
for (let i = 0; i < value.length; i++) {
outValue[i] = this._processJsonValue(value[i]);
}
return outValue;
}
// database objects are treated as empty objects
if (value instanceof BaseDbObject)
return {fields: [], values: []};
// all other objects are transformed to an object with two arrays (fields
// and values)
const outValue = {};
outValue.fields = Object.getOwnPropertyNames(value);
outValue.values = new Array(outValue.fields.length);
for (let i = 0; i < outValue.fields.length; i++) {
outValue.values[i] = this._processJsonValue(value[outValue.fields[i]]);
}
return outValue;
}
//---------------------------------------------------------------------------
// _transformOutBind()
//

View File

@ -53,7 +53,14 @@ class BaseDbObject {
// Sets the value of the attribute on the object to the given value.
//---------------------------------------------------------------------------
_setAttrValue(attr, value) {
value = this._transformValueIn(value, attr.typeClass);
const info = {
fqn: this._objType.fqn,
attrName: attr.name,
type: attr.type,
typeClass: attr.typeClass
};
const options = {allowArray: false};
value = transformer.transformValueIn(info, value, options);
this._impl.setAttrValue(attr, value);
}
@ -83,25 +90,6 @@ class BaseDbObject {
return (result);
}
//---------------------------------------------------------------------------
// _transformValueIn()
//
// Transforms a value coming from the caller to the implementation.
//---------------------------------------------------------------------------
_transformValueIn(value, cls) {
let outValue = value;
if (cls && value !== null && value !== undefined) {
if (value instanceof cls) {
outValue = value._impl;
} else {
outValue = new cls(value)._impl;
}
} else if (value instanceof Lob) {
outValue = value._impl;
}
return outValue;
}
//---------------------------------------------------------------------------
// _transformValueOut()
//
@ -129,7 +117,13 @@ class BaseDbObject {
//---------------------------------------------------------------------------
append(value) {
errors.assertArgCount(arguments, 1, 1);
value = this._transformValueIn(value, this.elementTypeClass);
const info = {
fqn: this._objType.fqn,
type: this._objType.elementType,
typeClass: this._objType.elementTypeClass
};
const options = {allowArray: false};
value = transformer.transformValueIn(info, value, options);
this._impl.append(value);
}
@ -334,7 +328,13 @@ class BaseDbObject {
setElement(index, value) {
errors.assertArgCount(arguments, 2, 2);
errors.assertParamValue(Number.isInteger(index), 1);
value = this._transformValueIn(value, this.elementTypeClass);
const info = {
fqn: this._objType.fqn,
type: this._objType.elementType,
typeClass: this._objType.elementTypeClass
};
const options = {allowArray: false};
value = transformer.transformValueIn(info, value, options);
this._impl.setElement(index, value);
}
@ -458,5 +458,7 @@ BaseDbObject._collectionProxyHandler = {
};
module.exports = BaseDbObject;
// load this after the module exports are set so that it is available
const transformer = require('./transformer.js');

View File

@ -123,7 +123,7 @@ const ERR_FETCH_TYPE_HANDLER_RETURN_VALUE = 120;
const ERR_FETCH_TYPE_HANDLER_TYPE = 121;
const ERR_FETCH_TYPE_HANDLER_CONVERTER = 122;
const ERR_CALL_TIMEOUT_EXCEEDED = 123;
const ERR_EMPTY_CONNECTION_STRING = 125;
const ERR_EMPTY_CONNECT_STRING = 125;
const ERR_OSON_VERSION_NOT_SUPPORTED = 126;
const ERR_UNKOWN_SERVER_SIDE_PIGGYBACK = 127;
const ERR_UNKNOWN_COLUMN_TYPE_NAME = 128;
@ -132,12 +132,14 @@ 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;
const ERR_WRONG_VALUE_FOR_DBOBJECT_ATTR = 134;
const ERR_WRONG_VALUE_FOR_DBOBJECT_ELEM = 135;
// Oracle Net layer errors start from 500
const ERR_CONNECTION_CLOSED = 500;
const ERR_CONNECTION_LOSTCONTACT = 501;
const ERR_CONNECTION_INCOMPLETE = 503;
const ERR_PROXY_CONNECT_FAILURE = 504;
const ERR_PROXY_CONNECTION_FAILURE = 504;
const ERR_TLS_INIT_FAILURE = 505;
const ERR_TLS_AUTH_FAILURE = 506;
const ERR_TLS_DNMATCH_FAILURE = 507;
@ -145,7 +147,7 @@ const ERR_TLS_HOSTMATCH_FAILURE = 508;
const ERR_INVALID_PACKET = 509;
const ERR_CONNECTION_TIMEDOUT = 510;
const ERR_CONNECTION_REFUSED = 511;
const ERR_INVALID_CONNECTSTRING_PARAMETERS = 512;
const ERR_INVALID_CONNECT_STRING_PARAMETERS = 512;
const ERR_CONNECTION_INBAND = 513;
const ERR_INVALID_CONNECT_STRING_SYNTAX = 514;
const ERR_INVALID_EZCONNECT_SYNTAX = 515;
@ -363,7 +365,7 @@ messages.set(ERR_FETCH_TYPE_HANDLER_CONVERTER, // NJS-122
'fetchTypeHandler return value attribute "converter" must be a function');
messages.set(ERR_CALL_TIMEOUT_EXCEEDED, // NJS-123
'call timeout of %d ms exceeded');
messages.set(ERR_EMPTY_CONNECTION_STRING, // NJS-125
messages.set(ERR_EMPTY_CONNECT_STRING, // NJS-125
'"connectString" cannot be empty or undefined. Bequeath connections are not supported in Thin mode');
messages.set(ERR_OSON_VERSION_NOT_SUPPORTED, // NJS-126
'OSON version %s is not supported');
@ -381,6 +383,10 @@ 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');
messages.set(ERR_WRONG_VALUE_FOR_DBOBJECT_ATTR, // NJS-134
'value is of wrong type for attribute %s of object %s');
messages.set(ERR_WRONG_VALUE_FOR_DBOBJECT_ELEM, // NJS-135
'value is of wrong type for an element of object %s');
// Oracle Net layer errors
@ -390,7 +396,7 @@ messages.set(ERR_CONNECTION_LOSTCONTACT, // NJS-501
'connection to host %s port %d terminated unexpectedly. (CONNECTION_ID=%s)\n%s');
messages.set(ERR_CONNECTION_INCOMPLETE, // NJS-503
'connection to host %s port %d could not be established. (CONNECTION_ID=%s)\n%s');
messages.set(ERR_PROXY_CONNECT_FAILURE, // NJS-504
messages.set(ERR_PROXY_CONNECTION_FAILURE, // NJS-504
'connection establishment through a web proxy at host %s port %d failed. (CONNECTION_ID=%s)\n%s');
messages.set(ERR_TLS_INIT_FAILURE, // NJS-505
'unable to initiate TLS connection. Please check if wallet credentials are valid');
@ -406,7 +412,7 @@ messages.set(ERR_CONNECTION_TIMEDOUT, // NJS-510
'connection to host %s port %d timed out. Request exceeded "%s" of %d seconds. (CONNECTION_ID=%s)');
messages.set(ERR_CONNECTION_REFUSED, // NJS-511
'connection to listener at host %s port %d was refused. (CONNECTION_ID=%s)\nCause: %s');
messages.set(ERR_INVALID_CONNECTSTRING_PARAMETERS, // NJS-512
messages.set(ERR_INVALID_CONNECT_STRING_PARAMETERS, // NJS-512
'invalid connection string parameters.\n%s');
messages.set(ERR_CONNECTION_INBAND, // NJS-513
'error received through in-band notification: %s');
@ -693,7 +699,7 @@ module.exports = {
ERR_CONNECTION_CLOSED,
ERR_CONNECTION_LOSTCONTACT,
ERR_CONNECTION_INCOMPLETE,
ERR_PROXY_CONNECT_FAILURE,
ERR_PROXY_CONNECTION_FAILURE,
ERR_TLS_INIT_FAILURE,
ERR_TLS_AUTH_FAILURE,
ERR_TLS_DNMATCH_FAILURE,
@ -701,7 +707,7 @@ module.exports = {
ERR_INVALID_PACKET,
ERR_CONNECTION_TIMEDOUT,
ERR_CONNECTION_REFUSED,
ERR_INVALID_CONNECTSTRING_PARAMETERS,
ERR_INVALID_CONNECT_STRING_PARAMETERS,
ERR_CONNECTION_INBAND,
ERR_INVALID_CONNECT_STRING_SYNTAX,
ERR_INVALID_EZCONNECT_SYNTAX,
@ -739,7 +745,7 @@ module.exports = {
ERR_FETCH_TYPE_HANDLER_TYPE,
ERR_FETCH_TYPE_HANDLER_CONVERTER,
ERR_CALL_TIMEOUT_EXCEEDED,
ERR_EMPTY_CONNECTION_STRING,
ERR_EMPTY_CONNECT_STRING,
ERR_UNKOWN_SERVER_SIDE_PIGGYBACK,
ERR_UNKNOWN_COLUMN_TYPE_NAME,
ERR_INVALID_OBJECT_TYPE_NAME,
@ -747,6 +753,8 @@ module.exports = {
ERR_INVALID_COLL_INDEX_SET,
ERR_INVALID_COLL_INDEX_GET,
ERR_DELETE_ELEMENTS_OF_VARRAY,
ERR_WRONG_VALUE_FOR_DBOBJECT_ATTR,
ERR_WRONG_VALUE_FOR_DBOBJECT_ELEM,
ERR_CONNECTION_CLOSED_CODE: `${ERR_PREFIX}-${ERR_CONNECTION_CLOSED}`,
assert,
assertArgCount,

View File

@ -112,7 +112,6 @@ function _initCLib() {
function _initializeThinDriver() {
require('./thin');
_thinDriverInitialized = true;
}
//---------------------------------------------------------------------------
@ -615,17 +614,19 @@ async function getConnection(a1) {
"events",
"externalAuth",
"stmtCacheSize");
if (_initOracleClientArgs === undefined && !_thinDriverInitialized) {
if (_initOracleClientArgs === undefined && !settings.thinDriverInitialized) {
_initializeThinDriver();
}
const conn = new Connection();
conn._impl = new impl.ConnectionImpl();
await conn._impl.connect(options);
if (_initOracleClientArgs === undefined) {
settings.thinDriverInitialized = true;
}
return conn;
}
//-----------------------------------------------------------------------------
// getPool()
//
@ -668,7 +669,7 @@ function initOracleClient(arg1) {
errors.assertParamPropString(options, 1, "errorUrl");
errors.assertParamPropString(options, 1, "driverName");
}
if (_thinDriverInitialized) {
if (settings.thinDriverInitialized) {
errors.throwErr(errors.ERR_THIN_CONNECTION_ALREADY_CREATED);
}
if (_initOracleClientArgs === undefined) {

View File

@ -57,6 +57,7 @@ class Settings {
this.queueMax = 500;
this.stmtCacheSize = 30;
this.thin = true;
this.thinDriverInitialized = false;
this.createFetchTypeMap(this.fetchAsString, this.fetchAsBuffer);
this.fetchTypeHandler = undefined;
}

View File

@ -469,7 +469,7 @@ class ThinConnectionImpl extends ConnectionImpl {
if (params.password === undefined && params.token === undefined) {
errors.throwErr(errors.ERR_MISSING_CREDENTIALS);
} else if (!params.connectString) {
errors.throwErr(errors.ERR_EMPTY_CONNECTION_STRING);
errors.throwErr(errors.ERR_EMPTY_CONNECT_STRING);
}
this.sessionID = 0;

View File

@ -125,7 +125,7 @@ class ThinDbObjectImpl extends DbObjectImpl {
if (packedData) {
this.unpackedAssocArray = new Map();
this.unpackedAssocKeys = undefined;
} else {
} else if (objType) {
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]);

View File

@ -167,7 +167,7 @@ class NetworkSession {
if (address.protocol && (address.protocol.toUpperCase() == 'TCP' || address.protocol.toUpperCase() == 'TCPS')) {
this.ntAdapter = new NTTCP(this.sAtts.nt);
} else {
errors.throwErr(errors.ERR_INVALID_CONNECTSTRING_PARAMETERS, address.protocol + " protocol not supported");
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_PARAMETERS, address.protocol + " protocol not supported");
}
await this.ntAdapter.connect(address);
this.ntAdapter.startRead();
@ -514,9 +514,9 @@ class NetworkSession {
address = await this.getAddress(addressNode, userConfig);
} catch (err) {
if (err.message == "All options tried") /* Not even one valid Address */
errors.throwErr(errors.ERR_INVALID_CONNECTSTRING_PARAMETERS, "Ensure the ADDRESS parameters have been entered correctly, the most likely incorrect parameter is the host name");
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_PARAMETERS, "Ensure the ADDRESS parameters have been entered correctly, the most likely incorrect parameter is the host name");
else
errors.throwErr(errors.ERR_INVALID_CONNECTSTRING_PARAMETERS, err.message);
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_PARAMETERS, err.message);
}
await this.connect1(address, addressNode, userConfig);
}

View File

@ -173,7 +173,7 @@ class NTTCP {
req.removeAllListeners();
if (!this.connected) {
if (proxyConnectErrCause) {
errors.throwErr(errors.ERR_PROXY_CONNECT_FAILURE, httpsProxy, httpsProxyPort, this.atts.connectionId, proxyConnectErrCause);
errors.throwErr(errors.ERR_PROXY_CONNECTION_FAILURE, httpsProxy, httpsProxyPort, this.atts.connectionId, proxyConnectErrCause);
} else {
errors.throwErr(errors.ERR_CONNECTION_INCOMPLETE, this.host, this.port, this.atts.connectionId, connectErrCause);
}

250
lib/transformer.js Normal file
View File

@ -0,0 +1,250 @@
// 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 BaseDbObject = require('./dbObject.js');
const Lob = require('./lob.js');
const ResultSet = require('./resultset.js');
const constants = require('./constants.js');
const errors = require('./errors.js');
const util = require('util');
const types = require('./types.js');
//-----------------------------------------------------------------------------
// checkType()
//
// Checks that the type of the data matches one of the given types. If the type
// has not been specified yet, the first type is assumed to be the correct one.
//
// A failure to match results in an exception being thrown. The data in the
// info parameter is used to determine which error should be thrown.
//-----------------------------------------------------------------------------
function checkType(info, options) {
if (info.type === undefined && arguments.length > 2) {
info.type = arguments[2];
} else {
let matches = false;
for (let i = 2; i < arguments.length; i++) {
if (info.type === arguments[i]) {
matches = true;
break;
}
}
if (!matches) {
if (info.attrName) {
errors.throwErr(errors.ERR_WRONG_VALUE_FOR_DBOBJECT_ATTR,
info.attrName, info.fqn);
} else if (info.fqn) {
errors.throwErr(errors.ERR_WRONG_VALUE_FOR_DBOBJECT_ELEM, info.fqn);
} else if (info.isArray && info.name) {
errors.throwErr(errors.ERR_INCOMPATIBLE_TYPE_ARRAY_BIND, options.pos,
info.name);
} else if (info.isArray) {
errors.throwErr(errors.ERR_INCOMPATIBLE_TYPE_ARRAY_INDEX_BIND,
options.pos, info.pos);
} else {
errors.throwErr(errors.ERR_BIND_VALUE_AND_TYPE_MISMATCH);
}
}
}
}
//-----------------------------------------------------------------------------
// transformJsonValue()
//
// Returns a normalized JSON value. Scalar values are returned unchanged.
// Arrays are returned as a new array with transformed JSON values. Objects are
// returned as new objects with keys "fields" and "values", both of which
// are arrays (with the value transformed to JSON values).
//-----------------------------------------------------------------------------
function transformJsonValue(value) {
// handle simple scalars
if (value === undefined || value === null ||
typeof value === 'number' || typeof value === 'string' ||
typeof value === 'boolean' || Buffer.isBuffer(value) ||
util.isDate(value))
return value;
// arrays are transformed to a new array with processed values
if (Array.isArray(value)) {
const outValue = new Array(value.length);
for (let i = 0; i < value.length; i++) {
outValue[i] = transformJsonValue(value[i]);
}
return outValue;
}
// database objects are treated as empty objects
if (value instanceof BaseDbObject)
return {fields: [], values: []};
// all other objects are transformed to an object with two arrays (fields
// and values)
const outValue = {};
outValue.fields = Object.getOwnPropertyNames(value);
outValue.values = new Array(outValue.fields.length);
for (let i = 0; i < outValue.fields.length; i++) {
outValue.values[i] = transformJsonValue(value[outValue.fields[i]]);
}
return outValue;
}
//-----------------------------------------------------------------------------
// transformValueIn()
//
// Processes the value supplied by the caller and returns a normalized value,
// if necessary, for use by the implementation. All checks are performed on the
// value to ensure it is suitable for the type information supplied. If no type
// information is supplied, however, the value defines it instead!
//-----------------------------------------------------------------------------
function transformValueIn(info, value, options) {
// null and undefined can always be set so nothing needs to be done
if (value === undefined || value === null)
return undefined;
// handle setting plain JS values to database objects
if (info.type === types.DB_TYPE_OBJECT) {
let obj = value;
if (!(value instanceof BaseDbObject)) {
obj = new info.typeClass(value);
}
return obj._impl;
// handle setting plain JS values to JSON
} else if (info.type === types.DB_TYPE_JSON) {
return transformJsonValue(value);
// handle strings
} else if (typeof value === 'string') {
checkType(info, options,
types.DB_TYPE_VARCHAR,
types.DB_TYPE_NVARCHAR,
types.DB_TYPE_CHAR,
types.DB_TYPE_NCHAR,
types.DB_TYPE_CLOB,
types.DB_TYPE_NCLOB);
if (info.type !== types.DB_TYPE_CLOB &&
info.type !== types.DB_TYPE_NCLOB &&
info.cqn === undefined &&
(info.maxSize === undefined || value.length > info.maxSize)) {
if (info.checkSize) {
errors.throwErr(errors.ERR_MAX_SIZE_TOO_SMALL, info.maxSize,
value.length, options.pos);
}
info.maxSize = value.length;
}
return value;
// handle numbers
} else if (typeof value === 'number') {
checkType(info, options,
types.DB_TYPE_NUMBER,
types.DB_TYPE_BINARY_INTEGER,
types.DB_TYPE_BINARY_FLOAT,
types.DB_TYPE_BINARY_DOUBLE);
if (Number.isNaN(value) && info.type === types.DB_TYPE_NUMBER) {
errors.throwErr(errors.ERR_NAN_VALUE);
}
return value;
// handle booleans
} else if (typeof value === 'boolean') {
checkType(info, options, types.DB_TYPE_BOOLEAN);
return value;
// handle dates
} else if (util.isDate(value)) {
checkType(info, options,
types.DB_TYPE_TIMESTAMP,
types.DB_TYPE_TIMESTAMP_TZ,
types.DB_TYPE_TIMESTAMP_LTZ,
types.DB_TYPE_DATE);
return value;
// handle binding buffers
} else if (Buffer.isBuffer(value)) {
checkType(info, options,
types.DB_TYPE_RAW,
types.DB_TYPE_BLOB);
if (info.type === types.DB_TYPE_RAW && info.cqn === undefined &&
(info.maxSize === undefined || value.length > info.maxSize)) {
if (info.checkSize) {
errors.throwErr(errors.ERR_MAX_SIZE_TOO_SMALL, info.maxSize,
value.length, options.pos);
}
info.maxSize = value.length;
}
return value;
// handle result sets
} else if (value instanceof ResultSet) {
checkType(info, options, types.DB_TYPE_CURSOR);
return value._impl;
// handle binding LOBs
} else if (value instanceof Lob) {
checkType(info, options, value.type);
return value._impl;
// handle database objects
} else if (value instanceof BaseDbObject) {
checkType(info, options, types.DB_TYPE_OBJECT);
return value._impl;
// handle arrays
} else if (options.allowArray && Array.isArray(value)) {
info.isArray = true;
if (info.dir === constants.BIND_IN) {
info.maxArraySize = value.length || 1;
} else if (info.maxArraySize === undefined) {
errors.throwErr(errors.ERR_REQUIRED_MAX_ARRAY_SIZE);
} else if (value.length > info.maxArraySize) {
errors.throwErr(errors.ERR_INVALID_ARRAY_SIZE);
}
options.allowArray = false;
const transformed = new Array(value.length);
for (let i = 0; i < value.length; i++) {
options.pos = i;
transformed[i] = transformValueIn(info, value[i], options);
}
return transformed;
}
// no suitable bind value found
if (info.type === undefined)
errors.throwErr(errors.ERR_INVALID_BIND_DATA_TYPE, 2);
checkType(info, options);
}
// define exports
module.exports = {
transformValueIn,
};

View File

@ -1,4 +1,4 @@
// Copyright (c) 2019, 2022, Oracle and/or its affiliates.
// Copyright (c) 2019, 2023, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
@ -100,9 +100,8 @@ static bool njsDbObject_transformFromOracle(njsDbObject *obj, napi_env env,
njsDataTypeInfo *typeInfo, dpiData *data, napi_value *value,
njsDbObjectAttr *attr, njsModuleGlobals *globals);
static bool njsDbObject_transformToOracle(njsDbObject *obj, napi_env env,
napi_value value, dpiOracleTypeNum oracleTypeNum,
dpiNativeTypeNum *nativeTypeNum, dpiData *data, char **strBuffer,
njsDbObjectAttr *attr, njsModuleGlobals *globals);
napi_value value, dpiOracleTypeNum oracleTypeNum, dpiData *data,
char **strBuffer, njsDbObjectAttr *attr, njsModuleGlobals *globals);
static bool njsDbObjectType_populate(njsDbObjectType *objType,
dpiObjectType *objectTypeHandle, napi_env env, napi_value jsObjectType,
dpiObjectTypeInfo *info, njsBaton *baton);
@ -119,17 +118,16 @@ static bool njsDbObject_wrap(napi_env env, napi_value value,
NJS_NAPI_METHOD_IMPL_SYNC(njsDbObject_append, 1, &njsClassDefDbObject)
{
njsDbObject *obj = (njsDbObject*) callingInstance;
dpiNativeTypeNum nativeTypeNum;
char *str = NULL;
dpiData data;
int status;
nativeTypeNum = obj->type->elementTypeInfo.nativeTypeNum;
if (!njsDbObject_transformToOracle(obj, env, args[0],
obj->type->elementTypeInfo.oracleTypeNum, &nativeTypeNum, &data,
&str, NULL, globals))
obj->type->elementTypeInfo.oracleTypeNum, &data, &str, NULL,
globals))
return false;
status = dpiObject_appendElement(obj->handle, nativeTypeNum, &data);
status = dpiObject_appendElement(obj->handle,
obj->type->elementTypeInfo.nativeTypeNum, &data);
if (str)
free(str);
if (status < 0)
@ -538,7 +536,6 @@ bool njsDbObject_new(njsDbObjectType *objType, dpiObject *objHandle,
NJS_NAPI_METHOD_IMPL_SYNC(njsDbObject_setAttrValue, 2, &njsClassDefDbObject)
{
njsDbObject *obj = (njsDbObject*) callingInstance;
dpiNativeTypeNum nativeTypeNum;
njsDbObjectAttr *attr;
char *str = NULL;
dpiData data;
@ -548,15 +545,13 @@ NJS_NAPI_METHOD_IMPL_SYNC(njsDbObject_setAttrValue, 2, &njsClassDefDbObject)
NJS_CHECK_NAPI(env, napi_unwrap(env, args[0], (void**) &attr))
// transform to value required by ODPI-C
nativeTypeNum = attr->typeInfo.nativeTypeNum;
if (!njsDbObject_transformToOracle(obj, env, args[1],
attr->typeInfo.oracleTypeNum, &nativeTypeNum, &data, &str, attr,
globals))
attr->typeInfo.oracleTypeNum, &data, &str, attr, globals))
return false;
// set value
status = dpiObject_setAttributeValue(obj->handle, attr->handle,
nativeTypeNum, &data);
attr->typeInfo.nativeTypeNum, &data);
if (str)
free(str);
if (status < 0)
@ -573,20 +568,18 @@ NJS_NAPI_METHOD_IMPL_SYNC(njsDbObject_setAttrValue, 2, &njsClassDefDbObject)
NJS_NAPI_METHOD_IMPL_SYNC(njsDbObject_setElement, 2, &njsClassDefDbObject)
{
njsDbObject *obj = (njsDbObject*) callingInstance;
dpiNativeTypeNum nativeTypeNum;
char *str = NULL;
int32_t index;
dpiData data;
int status;
NJS_CHECK_NAPI(env, napi_get_value_int32(env, args[0], &index))
nativeTypeNum = obj->type->elementTypeInfo.nativeTypeNum;
if (!njsDbObject_transformToOracle(obj, env, args[1],
obj->type->elementTypeInfo.oracleTypeNum, &nativeTypeNum, &data,
&str, NULL, globals))
obj->type->elementTypeInfo.oracleTypeNum, &data, &str, NULL,
globals))
return false;
status = dpiObject_setElementValueByIndex(obj->handle, index,
nativeTypeNum, &data);
obj->type->elementTypeInfo.nativeTypeNum, &data);
if (str)
free(str);
if (status < 0)
@ -700,9 +693,8 @@ static bool njsDbObject_transformFromOracle(njsDbObject *obj, napi_env env,
// Transforms a JavaScript value into the value that ODPI-C expects.
//-----------------------------------------------------------------------------
static bool njsDbObject_transformToOracle(njsDbObject *obj, napi_env env,
napi_value value, dpiOracleTypeNum oracleTypeNum,
dpiNativeTypeNum *nativeTypeNum, dpiData *data, char **strBuffer,
njsDbObjectAttr *attr, njsModuleGlobals *globals)
napi_value value, dpiOracleTypeNum oracleTypeNum, dpiData *data,
char **strBuffer, njsDbObjectAttr *attr, njsModuleGlobals *globals)
{
napi_value constructor, getComponentsFn;
napi_valuetype valueType;
@ -727,7 +719,6 @@ static bool njsDbObject_transformToOracle(njsDbObject *obj, napi_env env,
case napi_string:
if (!njsUtils_copyStringFromJS(env, value, strBuffer, &length))
return false;
*nativeTypeNum = DPI_NATIVE_TYPE_BYTES;
dpiData_setBytes(data, *strBuffer, (uint32_t) length);
return true;
@ -735,13 +726,11 @@ static bool njsDbObject_transformToOracle(njsDbObject *obj, napi_env env,
case napi_number:
NJS_CHECK_NAPI(env, napi_get_value_double(env, value,
&data->value.asDouble));
*nativeTypeNum = DPI_NATIVE_TYPE_DOUBLE;
return true;
// handle booleans
case napi_boolean:
NJS_CHECK_NAPI(env, napi_get_value_bool(env, value, &tempBool))
*nativeTypeNum = DPI_NATIVE_TYPE_BOOLEAN;
data->value.asBoolean = (int) tempBool;
return true;
@ -751,7 +740,6 @@ static bool njsDbObject_transformToOracle(njsDbObject *obj, napi_env env,
// handle dates
NJS_CHECK_NAPI(env, napi_is_date(env, value, &check))
if (check) {
*nativeTypeNum = DPI_NATIVE_TYPE_TIMESTAMP;
NJS_CHECK_NAPI(env, napi_get_reference_value(env,
globals->jsGetDateComponentsFn, &getComponentsFn))
return njsUtils_setDateValue(oracleTypeNum, env, value,
@ -764,7 +752,6 @@ static bool njsDbObject_transformToOracle(njsDbObject *obj, napi_env env,
NJS_CHECK_NAPI(env, napi_get_buffer_info(env, value,
&bufferData, &length))
dpiData_setBytes(data, bufferData, (uint32_t) length);
*nativeTypeNum = DPI_NATIVE_TYPE_BYTES;
return true;
}
@ -777,7 +764,6 @@ static bool njsDbObject_transformToOracle(njsDbObject *obj, napi_env env,
if (!njsDbObject_getInstance(globals, env, value, &valueObj))
return false;
dpiData_setObject(data, valueObj->handle);
*nativeTypeNum = DPI_NATIVE_TYPE_OBJECT;
return true;
}