Move code from C to JS and fix fetchType

This commit is contained in:
Christopher Jones 2023-02-21 17:21:26 +11:00
parent 2d2fce0dda
commit 81cf2df5a4
13 changed files with 260 additions and 432 deletions

View File

@ -68,8 +68,6 @@ class Connection extends EventEmitter {
"autoCommit",
"dbObjectAsPojo",
"fetchArraySize",
"fetchAsBuffer",
"fetchAsString",
"maxRows",
"outFormat",
"prefetchRows");
@ -793,10 +791,10 @@ class Connection extends EventEmitter {
if (options.fetchInfo !== undefined) {
errors.assertParamPropValue(nodbUtil.isObject(options.fetchInfo), 3,
"fetchInfo");
const keys = Object.getOwnPropertyNames(options.fetchInfo);
outOptions.fetchInfo = new Array(keys.length);
for (let i = 0; i < keys.length; i++) {
const info = options.fetchInfo[keys[i]];
const names = Object.getOwnPropertyNames(options.fetchInfo);
const map = new Map(settings.fetchTypeMap);
for (const name of names) {
const info = options.fetchInfo[name];
if (info.type === undefined)
errors.throwErr(errors.ERR_NO_TYPE_FOR_CONVERSION);
if (info.type !== constants.DEFAULT &&
@ -804,8 +802,9 @@ class Connection extends EventEmitter {
info.type !== constants.DB_TYPE_RAW) {
errors.throwErr(errors.ERR_INVALID_TYPE_FOR_CONVERSION);
}
outOptions.fetchInfo[i] = {name: keys[i], type: info.type};
map.set(name, info.type);
}
outOptions.fetchTypeMap = map;
}
// maxRows must be a positive integer (or 0)
@ -1230,7 +1229,9 @@ class Connection extends EventEmitter {
const info = await this._impl.getStatementInfo(sql);
if (info.metaData) {
for (let i = 0; i < info.metaData.length; i++) {
nodbUtil.addTypeProperties(info.metaData[i], "dbType");
const m = info.metaData[i];
nodbUtil.addTypeProperties(m, "dbType");
m.fetchType = constants.DB_TYPE_FETCH_TYPE_MAP.get(m.dbType);
}
}
return info;

View File

@ -154,6 +154,36 @@ module.exports = {
[DB_TYPE_VARCHAR, "VARCHAR2"]
]),
// default fetch type map
DB_TYPE_FETCH_TYPE_MAP: new Map([
[DB_TYPE_BFILE, DB_TYPE_BFILE],
[DB_TYPE_BINARY_DOUBLE, DB_TYPE_BINARY_DOUBLE],
[DB_TYPE_BINARY_FLOAT, DB_TYPE_BINARY_FLOAT],
[DB_TYPE_BINARY_INTEGER, DB_TYPE_BINARY_INTEGER],
[DB_TYPE_BLOB, DB_TYPE_BLOB],
[DB_TYPE_BOOLEAN, DB_TYPE_BOOLEAN],
[DB_TYPE_CHAR, DB_TYPE_CHAR],
[DB_TYPE_CLOB, DB_TYPE_CLOB],
[DB_TYPE_CURSOR, DB_TYPE_CURSOR],
[DB_TYPE_DATE, DB_TYPE_TIMESTAMP_LTZ],
[DB_TYPE_INTERVAL_DS, DB_TYPE_INTERVAL_DS],
[DB_TYPE_INTERVAL_YM, DB_TYPE_INTERVAL_YM],
[DB_TYPE_JSON, DB_TYPE_JSON],
[DB_TYPE_LONG, DB_TYPE_LONG],
[DB_TYPE_LONG_RAW, DB_TYPE_LONG_RAW],
[DB_TYPE_NCHAR, DB_TYPE_NCHAR],
[DB_TYPE_NCLOB, DB_TYPE_NCLOB],
[DB_TYPE_NUMBER, DB_TYPE_NUMBER],
[DB_TYPE_NVARCHAR, DB_TYPE_NVARCHAR],
[DB_TYPE_OBJECT, DB_TYPE_OBJECT],
[DB_TYPE_RAW, DB_TYPE_RAW],
[DB_TYPE_ROWID, DB_TYPE_ROWID],
[DB_TYPE_TIMESTAMP, DB_TYPE_TIMESTAMP_LTZ],
[DB_TYPE_TIMESTAMP_LTZ, DB_TYPE_TIMESTAMP_LTZ],
[DB_TYPE_TIMESTAMP_TZ, DB_TYPE_TIMESTAMP_LTZ],
[DB_TYPE_VARCHAR, DB_TYPE_VARCHAR]
]),
// fetchInfo type defaulting
DEFAULT: 0,

View File

@ -72,6 +72,15 @@ class ResultSetImpl {
errors.throwNotImplemented("getting rows");
}
//---------------------------------------------------------------------------
// setFetchTypes()
//
// Sets the types to use when fetching data.
//---------------------------------------------------------------------------
setFetchTypes() {
errors.throwNotImplemented("setting fetch types");
}
}
module.exports = ResultSetImpl;

View File

@ -1034,26 +1034,13 @@ module.exports = {
set fetchAsBuffer(value) {
errors.assertPropValue(Array.isArray(value), "fetchAsBuffer");
for (const element of value) {
if (element !== constants.DB_TYPE_BLOB) {
errors.throwErr(errors.ERR_INVALID_TYPE_FOR_CONVERSION);
}
}
settings.createFetchTypeMap(settings.fetchAsString, value);
settings.fetchAsBuffer = value;
},
set fetchAsString(value) {
errors.assertPropValue(Array.isArray(value), "fetchAsString");
for (const element of value) {
if (element != constants.DB_TYPE_NUMBER &&
element != constants.DB_TYPE_TIMESTAMP_LTZ &&
element != constants.DB_TYPE_RAW &&
element != constants.DB_TYPE_CLOB &&
element != constants.DB_TYPE_NCLOB &&
element != constants.DB_TYPE_JSON) {
errors.throwErr(errors.ERR_INVALID_TYPE_FOR_CONVERSION);
}
}
settings.createFetchTypeMap(value, settings.fetchAsBuffer);
settings.fetchAsString = value;
},

View File

@ -30,6 +30,7 @@ const QueryStream = require('./queryStream.js');
const BaseDbObject = require('./dbObject.js');
const nodbUtil = require('./util.js');
const constants = require('./constants.js');
const settings = require('./settings.js');
const Lob = require('./lob.js');
const errors = require('./errors.js');
@ -43,6 +44,23 @@ class ResultSet {
this._isActive = false;
}
//---------------------------------------------------------------------------
// _determineFetchType()
//
// Determine the fetch type to use for the specified metadata.
//---------------------------------------------------------------------------
_determineFetchType(metadata, options) {
if (options.fetchTypeMap && options.fetchTypeMap.has(metadata.name)) {
metadata.fetchType = options.fetchTypeMap.get(metadata.name);
if (metadata.fetchType === constants.DEFAULT) {
metadata.fetchType =
constants.DB_TYPE_FETCH_TYPE_MAP.get(metadata.dbType);
}
} else {
metadata.fetchType = settings.fetchTypeMap.get(metadata.dbType);
}
}
//---------------------------------------------------------------------------
// _getAllRows()
//
@ -189,6 +207,7 @@ class ResultSet {
}
for (let i = 0; i < setupData.metaData.length; i++) {
const info = setupData.metaData[i];
this._determineFetchType(info, options);
if (info.fetchType === constants.DB_TYPE_CURSOR) {
setupData.nestedCursorSetupData.push({"index": i});
} else if (info.fetchType === constants.DB_TYPE_CLOB ||
@ -213,6 +232,7 @@ class ResultSet {
}
}
}
resultSetImpl.setFetchTypes(setupData.metaData);
}
//---------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022, Oracle and/or its affiliates.
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
@ -27,6 +27,7 @@
'use strict';
const constants = require('./constants.js');
const errors = require('./errors.js');
class Settings {
@ -54,6 +55,7 @@ class Settings {
this.queueTimeout = 60000;
this.queueMax = 500;
this.stmtCacheSize = 30;
this.createFetchTypeMap(this.fetchAsString, this.fetchAsBuffer);
}
//---------------------------------------------------------------------------
@ -70,6 +72,66 @@ class Settings {
}
}
//---------------------------------------------------------------------------
// createFetchTypeMap()
//
// Creates the fetch type map. This overrides the default fetch type mapping
// used by the driver with the contents of the fetchAsString and
// fetchAsBuffer arrays. The error checking is performed here as well in
// order to eliminate repeated code.
// ---------------------------------------------------------------------------
createFetchTypeMap(fetchAsString, fetchAsBuffer) {
// create a copy of the default fetch type map
const map = new Map(constants.DB_TYPE_FETCH_TYPE_MAP);
// adjust map for fetchAsString settings
for (const element of fetchAsString) {
switch (element) {
case constants.DB_TYPE_NUMBER:
map.set(constants.DB_TYPE_BINARY_DOUBLE, constants.DB_TYPE_VARCHAR);
map.set(constants.DB_TYPE_BINARY_FLOAT, constants.DB_TYPE_VARCHAR);
map.set(constants.DB_TYPE_BINARY_INTEGER, constants.DB_TYPE_VARCHAR);
map.set(constants.DB_TYPE_NUMBER, constants.DB_TYPE_VARCHAR);
break;
case constants.DB_TYPE_TIMESTAMP_LTZ:
map.set(constants.DB_TYPE_DATE, constants.DB_TYPE_VARCHAR);
map.set(constants.DB_TYPE_TIMESTAMP, constants.DB_TYPE_VARCHAR);
map.set(constants.DB_TYPE_TIMESTAMP_TZ, constants.DB_TYPE_VARCHAR);
map.set(constants.DB_TYPE_TIMESTAMP_LTZ, constants.DB_TYPE_VARCHAR);
break;
case constants.DB_TYPE_CLOB:
case constants.DB_TYPE_NCLOB:
map.set(constants.DB_TYPE_CLOB, constants.DB_TYPE_VARCHAR);
map.set(constants.DB_TYPE_NCLOB, constants.DB_TYPE_NVARCHAR);
break;
case constants.DB_TYPE_RAW:
map.set(constants.DB_TYPE_RAW, constants.DB_TYPE_VARCHAR);
break;
case constants.DB_TYPE_JSON:
map.set(constants.DB_TYPE_JSON, constants.DB_TYPE_VARCHAR);
break;
default:
errors.throwErr(errors.ERR_INVALID_TYPE_FOR_CONVERSION);
}
}
// adjust map for fetchAsBuffer settings
for (const element of fetchAsBuffer) {
switch (element) {
case constants.DB_TYPE_BLOB:
map.set(constants.DB_TYPE_BLOB, constants.DB_TYPE_RAW);
break;
default:
errors.throwErr(errors.ERR_INVALID_TYPE_FOR_CONVERSION);
}
}
// assign calculated fetchTypeMap for later use
this.fetchTypeMap = map;
}
}
module.exports = new Settings();

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015, 2022, Oracle and/or its affiliates.
// Copyright (c) 2015, 2023, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
@ -333,18 +333,6 @@ void njsBaton_free(njsBaton *baton, napi_env env)
baton->implicitResults = baton->implicitResults->next;
}
// free mapping type arrays
if (baton->fetchInfo) {
for (i = 0; i < baton->numFetchInfo; i++) {
NJS_FREE_AND_CLEAR(baton->fetchInfo[i].name);
}
free(baton->fetchInfo);
baton->fetchInfo = NULL;
}
NJS_FREE_AND_CLEAR(baton->fetchAsStringTypes);
NJS_FREE_AND_CLEAR(baton->fetchAsBufferTypes);
// remove references to JS objects
NJS_DELETE_REF_AND_CLEAR(baton->jsBufferRef);
NJS_DELETE_REF_AND_CLEAR(baton->jsCallingObjRef);
@ -423,53 +411,6 @@ static bool njsBaton_getErrorInfo(njsBaton *baton, napi_env env,
}
//-----------------------------------------------------------------------------
// njsBaton_getFetchInfoFromArg()
// Gets fetchInfo data from the specified Javascript object property, if
// possible. If the given property is undefined, no error is set and the value
// is left untouched; otherwise, if the value is not valid, an error is set on
// the baton.
//-----------------------------------------------------------------------------
bool njsBaton_getFetchInfoFromArg(njsBaton *baton, napi_env env,
napi_value props, uint32_t *numFetchInfo, njsFetchInfo **fetchInfo)
{
napi_value value, element, temp;
njsFetchInfo *tempFetchInfo;
uint32_t i;
// determine number of fetch info; if none, nothing more to do!
NJS_CHECK_NAPI(env, napi_get_named_property(env, props, "fetchInfo",
&value))
NJS_CHECK_NAPI(env, napi_get_array_length(env, value, numFetchInfo))
if (*numFetchInfo == 0) {
*fetchInfo = NULL;
return true;
}
// allocate space for fetchInfo structures
tempFetchInfo = calloc(*numFetchInfo, sizeof(njsFetchInfo));
if (!tempFetchInfo)
return njsBaton_setErrorInsufficientMemory(baton);
*fetchInfo = tempFetchInfo;
// process each key
for (i = 0; i < *numFetchInfo; i++) {
NJS_CHECK_NAPI(env, napi_get_element(env, value, i, &element))
NJS_CHECK_NAPI(env, napi_get_named_property(env, element, "name",
&temp))
if (!njsUtils_copyStringFromJS(env, temp, &tempFetchInfo[i].name,
&tempFetchInfo[i].nameLength))
return false;
NJS_CHECK_NAPI(env, napi_get_named_property(env, element, "type",
&temp))
NJS_CHECK_NAPI(env, napi_get_value_uint32(env, temp,
&tempFetchInfo[i].type))
}
return true;
}
//-----------------------------------------------------------------------------
// njsBaton_getNumOutBinds()
// Return the number of IN/OUT and OUT binds created by the baton.
@ -703,21 +644,6 @@ bool njsBaton_setErrorInsufficientMemory(njsBaton *baton)
}
//-----------------------------------------------------------------------------
// njsBaton_setErrorUnsupportedDataType()
// Set the error on the baton to indicate that an unsupported data type was
// encountered during a fetch. Returns false as a convenience to the caller.
//-----------------------------------------------------------------------------
bool njsBaton_setErrorUnsupportedDataType(njsBaton *baton,
uint32_t oracleTypeNum, uint32_t columnNum)
{
(void) snprintf(baton->error, sizeof(baton->error),
NJS_ERR_UNSUPPORTED_DATA_TYPE, oracleTypeNum, columnNum);
baton->hasError = true;
return false;
}
//-----------------------------------------------------------------------------
// njsBaton_setErrorUnsupportedDataTypeInJson()
// Set the error on the baton to indicate that an unsupported data type was

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015, 2022, Oracle and/or its affiliates.
// Copyright (c) 2015, 2023, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
@ -574,14 +574,6 @@ NJS_NAPI_METHOD_IMPL_ASYNC(njsConnection_execute, 5, NULL)
&baton->dmlRowCounts))
return false;
} else {
if (!njsUtils_getNamedPropertyUnsignedIntArray(env, args[3],
"fetchAsBuffer", &baton->numFetchAsBufferTypes,
&baton->fetchAsBufferTypes))
return false;
if (!njsUtils_getNamedPropertyUnsignedIntArray(env, args[3],
"fetchAsString", &baton->numFetchAsStringTypes,
&baton->fetchAsStringTypes))
return false;
NJS_CHECK_NAPI(env, napi_get_named_property(env, args[3],
"fetchArraySize", &temp))
NJS_CHECK_NAPI(env, napi_get_value_uint32(env, temp,
@ -590,9 +582,6 @@ NJS_NAPI_METHOD_IMPL_ASYNC(njsConnection_execute, 5, NULL)
"prefetchRows", &temp))
NJS_CHECK_NAPI(env, napi_get_value_uint32(env, temp,
&baton->prefetchRows))
if (!njsBaton_getFetchInfoFromArg(baton, env, args[3],
&baton->numFetchInfo, &baton->fetchInfo))
return false;
}
NJS_CHECK_NAPI(env, napi_get_named_property(env, args[3], "autoCommit",
&temp))

View File

@ -1,4 +1,4 @@
// Copyright (c) 2019, 2022, Oracle and/or its affiliates.
// Copyright (c) 2019, 2023, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
@ -198,7 +198,6 @@ typedef struct njsBaton njsBaton;
typedef struct njsClassDef njsClassDef;
typedef struct njsConnection njsConnection;
typedef struct njsDataTypeInfo njsDataTypeInfo;
typedef struct njsFetchInfo njsFetchInfo;
typedef struct njsImplicitResult njsImplicitResult;
typedef struct njsJsonBuffer njsJsonBuffer;
typedef struct njsLob njsLob;
@ -369,14 +368,6 @@ struct njsBaton {
uint32_t numRowCounts;
uint64_t *rowCounts;
// mapping types (requires free)
uint32_t numFetchInfo;
njsFetchInfo *fetchInfo;
uint32_t numFetchAsStringTypes;
uint32_t *fetchAsStringTypes;
uint32_t numFetchAsBufferTypes;
uint32_t *fetchAsBufferTypes;
// implicit results (requires free)
njsImplicitResult *implicitResults;
@ -493,13 +484,6 @@ struct njsConnection {
bool retag;
};
// data for adjusting fetch types
struct njsFetchInfo {
char *name;
size_t nameLength;
uint32_t type;
};
// data for acquiring implicit results
struct njsImplicitResult {
dpiStmt *stmt;
@ -730,8 +714,6 @@ bool njsBaton_commonConnectProcessArgs(njsBaton *baton, napi_env env,
bool njsBaton_create(njsBaton *baton, napi_env env, napi_callback_info info,
size_t numArgs, napi_value *args, const njsClassDef *classDef);
void njsBaton_free(njsBaton *baton, napi_env env);
bool njsBaton_getFetchInfoFromArg(njsBaton *baton, napi_env env,
napi_value props, uint32_t *numFetchInfo, njsFetchInfo **fetchInfo);
uint32_t njsBaton_getNumOutBinds(njsBaton *baton);
bool njsBaton_getSodaDocument(njsBaton *baton, njsSodaDatabase *db,
napi_env env, napi_value obj, dpiSodaDoc **handle);
@ -893,8 +875,10 @@ bool njsUtils_getNamedPropertyUnsignedIntArray(napi_env env, napi_value value,
const char *name, uint32_t *numElements, uint32_t **elements);
bool njsUtils_getXid(napi_env env, napi_value xidObj, dpiXid **xid);
bool njsUtils_isInstance(napi_env env, napi_value value, const char *name);
bool njsUtils_throwInsufficientMemory(napi_env env);
bool njsUtils_throwErrorDPI(napi_env env, njsModuleGlobals *globals);
bool njsUtils_throwInsufficientMemory(napi_env env);
bool njsUtils_throwUnsupportedDataType(napi_env env, uint32_t oracleTypeNum,
uint32_t columnNum);
bool njsUtils_validateArgs(napi_env env, napi_callback_info info,
size_t numArgs, napi_value *args, njsModuleGlobals **globals,
napi_value *callingObj, const njsClassDef *classDef, void **instance);
@ -919,8 +903,6 @@ bool njsVariable_initForQuery(njsVariable *vars, uint32_t numVars,
dpiStmt *handle, njsBaton *baton);
bool njsVariable_initForQueryJS(njsVariable *vars, uint32_t numVars,
napi_env env, njsBaton *baton);
bool njsVariable_performMapping(njsVariable *var, dpiQueryInfo *queryInfo,
njsBaton *baton);
bool njsVariable_process(njsVariable *vars, uint32_t numVars, uint32_t numRows,
njsBaton *baton);
bool njsVariable_processJS(njsVariable *vars, uint32_t numVars, napi_env env,

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015, 2022, Oracle and/or its affiliates.
// Copyright (c) 2015, 2023, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
@ -36,6 +36,7 @@
NJS_NAPI_METHOD_DECL_ASYNC(njsResultSet_close);
NJS_NAPI_METHOD_DECL_SYNC(njsResultSet_getMetaData);
NJS_NAPI_METHOD_DECL_ASYNC(njsResultSet_getRows);
NJS_NAPI_METHOD_DECL_SYNC(njsResultSet_setFetchTypes);
// asynchronous methods
static NJS_ASYNC_METHOD(njsResultSet_closeAsync);
@ -55,6 +56,8 @@ static const napi_property_descriptor njsClassProperties[] = {
napi_default, NULL },
{ "getRows", NULL, njsResultSet_getRows, NULL, NULL, NULL, napi_default,
NULL },
{ "setFetchTypes", NULL, njsResultSet_setFetchTypes, NULL, NULL, NULL,
napi_default, NULL },
{ NULL, NULL, NULL, NULL, NULL, NULL, napi_default, NULL }
};
@ -181,12 +184,9 @@ static bool njsResultSet_getRowsAsync(njsBaton *baton)
return njsBaton_setErrorDPI(baton);
var->dpiVarHandle = NULL;
}
if (dpiConn_newVar(rs->conn->handle, var->varTypeNum,
var->nativeTypeNum, baton->fetchArraySize, var->maxSize, 1, 0,
var->dpiObjectTypeHandle, &var->dpiVarHandle,
&var->buffer->dpiVarData) < 0)
return njsBaton_setErrorDPI(baton);
var->maxArraySize = baton->fetchArraySize;
if (!njsVariable_createBuffer(var, rs->conn, baton))
return false;
}
// perform define, if necessary
@ -306,3 +306,76 @@ bool njsResultSet_new(njsBaton *baton, napi_env env, njsConnection *conn,
return true;
}
//-----------------------------------------------------------------------------
// njsResultSet_setFetchTypes()
// Get accessor of "metaData" property.
//-----------------------------------------------------------------------------
NJS_NAPI_METHOD_IMPL_SYNC(njsResultSet_setFetchTypes, 1, NULL)
{
njsResultSet *rs = (njsResultSet*) callingInstance;
napi_value metadata, temp;
njsVariable *var;
uint32_t i;
for (i = 0; i < rs->numQueryVars; i++) {
var = &rs->queryVars[i];
// determine fetch type to use
NJS_CHECK_NAPI(env, napi_get_element(env, args[0], i, &metadata))
NJS_CHECK_NAPI(env, napi_get_named_property(env, metadata, "fetchType",
&temp))
NJS_CHECK_NAPI(env, napi_get_value_uint32(env, temp, &var->varTypeNum))
// if RAW data is being returned as VARCHAR, need to have twice as much
// space available to account for the hex encoding that the server does
if (var->dbTypeNum == DPI_ORACLE_TYPE_RAW &&
var->varTypeNum == DPI_ORACLE_TYPE_VARCHAR) {
var->maxSize *= 2;
}
// adjust max size to use based on fetch type and verify fetch type
switch (var->varTypeNum) {
case DPI_ORACLE_TYPE_VARCHAR:
case DPI_ORACLE_TYPE_NVARCHAR:
case DPI_ORACLE_TYPE_CHAR:
case DPI_ORACLE_TYPE_NCHAR:
case DPI_ORACLE_TYPE_RAW:
if (var->dbTypeNum == DPI_ORACLE_TYPE_CLOB ||
var->dbTypeNum == DPI_ORACLE_TYPE_NCLOB ||
var->dbTypeNum == DPI_ORACLE_TYPE_BLOB) {
var->maxSize = (uint32_t) -1;
} else if (var->maxSize == 0) {
var->maxSize = NJS_MAX_FETCH_AS_STRING_SIZE;
}
break;
case DPI_ORACLE_TYPE_LONG_VARCHAR:
case DPI_ORACLE_TYPE_LONG_RAW:
var->maxSize = (uint32_t) -1;
break;
case DPI_ORACLE_TYPE_DATE:
case DPI_ORACLE_TYPE_TIMESTAMP:
case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
case DPI_ORACLE_TYPE_CLOB:
case DPI_ORACLE_TYPE_NCLOB:
case DPI_ORACLE_TYPE_BLOB:
case DPI_ORACLE_TYPE_OBJECT:
case DPI_ORACLE_TYPE_NUMBER:
case DPI_ORACLE_TYPE_NATIVE_INT:
case DPI_ORACLE_TYPE_NATIVE_FLOAT:
case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
case DPI_ORACLE_TYPE_ROWID:
case DPI_ORACLE_TYPE_STMT:
case DPI_ORACLE_TYPE_JSON:
break;
default:
return njsUtils_throwUnsupportedDataType(env, var->varTypeNum,
i + 1);
}
}
return true;
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015, 2022, Oracle and/or its affiliates.
// Copyright (c) 2015, 2023, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
@ -533,46 +533,6 @@ bool njsUtils_getNamedPropertyUnsignedInt(napi_env env, napi_value value,
}
//-----------------------------------------------------------------------------
// njsUtils_getNamedPropertyUnsignedIntArray()
// Returns the value of the named property, which is assumed to be an array
// of unsigned integers. If the value is not found, the array is left
// unchanged.
//-----------------------------------------------------------------------------
bool njsUtils_getNamedPropertyUnsignedIntArray(napi_env env, napi_value value,
const char *name, uint32_t *numElements, uint32_t **elements)
{
napi_value array, element;
uint32_t i;
// get value of the named property, if not found, nothing to do
if (!njsUtils_getNamedProperty(env, value, name, &array))
return false;
if (!array)
return true;
// free memory, if applicable
if (*elements) {
free(*elements);
*elements = NULL;
*numElements = 0;
}
// get the elements from the array
NJS_CHECK_NAPI(env, napi_get_array_length(env, array, numElements))
*elements = calloc(*numElements, sizeof(uint32_t));
if (!elements && *numElements > 0)
return njsUtils_throwInsufficientMemory(env);
for (i = 0; i < *numElements; i++) {
NJS_CHECK_NAPI(env, napi_get_element(env, array, i, &element))
NJS_CHECK_NAPI(env, napi_get_value_uint32(env, element,
&((*elements)[i])))
}
return true;
}
//-----------------------------------------------------------------------------
// njsUtils_getXid()
// Returns the XID from the specified N-API value.
@ -620,18 +580,6 @@ bool njsUtils_getXid(napi_env env, napi_value value, dpiXid **xid)
}
//-----------------------------------------------------------------------------
// njsUtils_throwInsufficientMemory()
// Throw an error indicating that insufficient memory could be allocated. The
// value false is returned as a convenience to the caller.
//-----------------------------------------------------------------------------
bool njsUtils_throwInsufficientMemory(napi_env env)
{
napi_throw_error(env, NULL, NJS_ERR_INSUFFICIENT_MEMORY);
return false;
}
//-----------------------------------------------------------------------------
// njsUtils_throwErrorDPI()
// Get the error message from ODPI-C and throw an equivalent JavaScript
@ -650,6 +598,35 @@ bool njsUtils_throwErrorDPI(napi_env env, njsModuleGlobals *globals)
}
//-----------------------------------------------------------------------------
// njsUtils_throwInsufficientMemory()
// Throw an error indicating that insufficient memory could be allocated. The
// value false is returned as a convenience to the caller.
//-----------------------------------------------------------------------------
bool njsUtils_throwInsufficientMemory(napi_env env)
{
napi_throw_error(env, NULL, NJS_ERR_INSUFFICIENT_MEMORY);
return false;
}
//-----------------------------------------------------------------------------
// njsUtils_throwUnsupportedDataType()
// Set the error on the baton to indicate that an unsupported data type was
// encountered during a fetch. Returns false as a convenience to the caller.
//-----------------------------------------------------------------------------
bool njsUtils_throwUnsupportedDataType(napi_env env, uint32_t oracleTypeNum,
uint32_t columnNum)
{
char errorMessage[100];
(void) snprintf(errorMessage, sizeof(errorMessage),
NJS_ERR_UNSUPPORTED_DATA_TYPE, oracleTypeNum, columnNum);
napi_throw_error(env, NULL, errorMessage);
return false;
}
//-----------------------------------------------------------------------------
// njsUtils_validateArgs()
// Gets the instance associated with the object and gets the arguments as

View File

@ -1,4 +1,4 @@
// Copyright (c) 2019, 2022, Oracle and/or its affiliates.
// Copyright (c) 2019, 2023, Oracle and/or its affiliates.
//-----------------------------------------------------------------------------
//
@ -56,6 +56,9 @@ bool njsVariable_createBuffer(njsVariable *var, njsConnection *conn,
case DPI_ORACLE_TYPE_NVARCHAR:
case DPI_ORACLE_TYPE_CHAR:
case DPI_ORACLE_TYPE_NCHAR:
case DPI_ORACLE_TYPE_RAW:
case DPI_ORACLE_TYPE_LONG_VARCHAR:
case DPI_ORACLE_TYPE_LONG_RAW:
var->nativeTypeNum = DPI_NATIVE_TYPE_BYTES;
break;
case DPI_ORACLE_TYPE_NATIVE_FLOAT:
@ -74,13 +77,8 @@ bool njsVariable_createBuffer(njsVariable *var, njsConnection *conn,
case DPI_ORACLE_TYPE_STMT:
var->nativeTypeNum = DPI_NATIVE_TYPE_STMT;
break;
case DPI_ORACLE_TYPE_RAW:
var->nativeTypeNum = DPI_NATIVE_TYPE_BYTES;
break;
case DPI_ORACLE_TYPE_CLOB:
case DPI_ORACLE_TYPE_NCLOB:
var->nativeTypeNum = DPI_NATIVE_TYPE_LOB;
break;
case DPI_ORACLE_TYPE_BLOB:
var->nativeTypeNum = DPI_NATIVE_TYPE_LOB;
break;
@ -96,6 +94,9 @@ bool njsVariable_createBuffer(njsVariable *var, njsConnection *conn,
case DPI_ORACLE_TYPE_NATIVE_INT:
var->nativeTypeNum = DPI_NATIVE_TYPE_INT64;
break;
case DPI_ORACLE_TYPE_ROWID:
var->nativeTypeNum = DPI_NATIVE_TYPE_ROWID;
break;
}
// allocate buffer
@ -212,53 +213,6 @@ bool njsVariable_getArrayValue(njsVariable *var, njsConnection *conn,
}
//-----------------------------------------------------------------------------
// njsVariable_getDataType()
// Return the data type that is being used by the variable. This is an
// enumeration that is publicly available in JavaScript.
//-----------------------------------------------------------------------------
static uint32_t njsVariable_getDataType(njsVariable *var)
{
switch (var->varTypeNum) {
case DPI_ORACLE_TYPE_VARCHAR:
case DPI_ORACLE_TYPE_NVARCHAR:
case DPI_ORACLE_TYPE_CHAR:
case DPI_ORACLE_TYPE_NCHAR:
case DPI_ORACLE_TYPE_ROWID:
case DPI_ORACLE_TYPE_LONG_VARCHAR:
return NJS_DATATYPE_STR;
case DPI_ORACLE_TYPE_RAW:
case DPI_ORACLE_TYPE_LONG_RAW:
return NJS_DATATYPE_BUFFER;
case DPI_ORACLE_TYPE_NATIVE_FLOAT:
case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
case DPI_ORACLE_TYPE_NATIVE_INT:
case DPI_ORACLE_TYPE_NUMBER:
return NJS_DATATYPE_NUM;
case DPI_ORACLE_TYPE_DATE:
case DPI_ORACLE_TYPE_TIMESTAMP:
case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
return NJS_DATATYPE_DATE;
case DPI_ORACLE_TYPE_CLOB:
return NJS_DATATYPE_CLOB;
case DPI_ORACLE_TYPE_NCLOB:
return NJS_DATATYPE_NCLOB;
case DPI_ORACLE_TYPE_BLOB:
return NJS_DATATYPE_BLOB;
case DPI_ORACLE_TYPE_OBJECT:
return NJS_DATATYPE_OBJECT;
case DPI_ORACLE_TYPE_STMT:
return NJS_DATATYPE_CURSOR;
case DPI_ORACLE_TYPE_JSON:
return NJS_DATATYPE_JSON;
default:
break;
}
return NJS_DATATYPE_DEFAULT;
}
//-----------------------------------------------------------------------------
// njsVariable_getMetadataMany()
// Return metadata about many variables.
@ -300,12 +254,6 @@ bool njsVariable_getMetadataOne(njsVariable *var, napi_env env,
var->nameLength, &temp))
NJS_CHECK_NAPI(env, napi_set_named_property(env, *metadata, "name", temp))
// store JavaScript fetch type
NJS_CHECK_NAPI(env, napi_create_uint32(env, njsVariable_getDataType(var),
&temp))
NJS_CHECK_NAPI(env, napi_set_named_property(env, *metadata, "fetchType",
temp))
// store database type, name and class, as needed
if (!njsUtils_addTypeProperties(env, *metadata, "dbType",
var->dbTypeNum, var->objectType))
@ -552,15 +500,7 @@ bool njsVariable_initForQuery(njsVariable *vars, uint32_t numVars,
dpiQueryInfo queryInfo;
uint32_t i;
// populate variables with query metadata
for (i = 0; i < numVars; i++) {
// allocate buffer
vars[i].buffer = calloc(1, sizeof(njsVariable));
if (!vars[i].buffer)
return njsBaton_setErrorInsufficientMemory(baton);
// get query information for the specified column
vars[i].pos = i + 1;
vars[i].isArray = false;
vars[i].bindDir = NJS_BIND_OUT;
@ -573,86 +513,14 @@ bool njsVariable_initForQuery(njsVariable *vars, uint32_t numVars,
vars[i].nameLength = queryInfo.nameLength;
vars[i].maxArraySize = baton->fetchArraySize;
vars[i].dbSizeInBytes = queryInfo.typeInfo.dbSizeInBytes;
vars[i].maxSize = queryInfo.typeInfo.clientSizeInBytes;
vars[i].precision = queryInfo.typeInfo.precision +
queryInfo.typeInfo.fsPrecision;
vars[i].scale = queryInfo.typeInfo.scale;
vars[i].isNullable = queryInfo.nullOk;
// determine the type of data
vars[i].dbTypeNum = queryInfo.typeInfo.oracleTypeNum;
vars[i].varTypeNum = queryInfo.typeInfo.oracleTypeNum;
vars[i].nativeTypeNum = queryInfo.typeInfo.defaultNativeTypeNum;
if (queryInfo.typeInfo.oracleTypeNum != DPI_ORACLE_TYPE_VARCHAR &&
queryInfo.typeInfo.oracleTypeNum != DPI_ORACLE_TYPE_NVARCHAR &&
queryInfo.typeInfo.oracleTypeNum != DPI_ORACLE_TYPE_CHAR &&
queryInfo.typeInfo.oracleTypeNum != DPI_ORACLE_TYPE_NCHAR &&
queryInfo.typeInfo.oracleTypeNum != DPI_ORACLE_TYPE_ROWID) {
if (!njsVariable_performMapping(&vars[i], &queryInfo, baton))
return false;
}
// validate data type and determine size
if (vars[i].varTypeNum == DPI_ORACLE_TYPE_VARCHAR ||
vars[i].varTypeNum == DPI_ORACLE_TYPE_NVARCHAR ||
vars[i].varTypeNum == DPI_ORACLE_TYPE_RAW) {
vars[i].maxSize = NJS_MAX_FETCH_AS_STRING_SIZE;
vars[i].nativeTypeNum = DPI_NATIVE_TYPE_BYTES;
} else {
vars[i].maxSize = 0;
}
switch (queryInfo.typeInfo.oracleTypeNum) {
case DPI_ORACLE_TYPE_VARCHAR:
case DPI_ORACLE_TYPE_NVARCHAR:
case DPI_ORACLE_TYPE_CHAR:
case DPI_ORACLE_TYPE_NCHAR:
case DPI_ORACLE_TYPE_RAW:
vars[i].maxSize = queryInfo.typeInfo.clientSizeInBytes;
if (queryInfo.typeInfo.oracleTypeNum == DPI_ORACLE_TYPE_RAW &&
vars[i].varTypeNum == DPI_ORACLE_TYPE_VARCHAR)
vars[i].maxSize *= 2;
break;
case DPI_ORACLE_TYPE_DATE:
case DPI_ORACLE_TYPE_TIMESTAMP:
case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
if (vars[i].varTypeNum != DPI_ORACLE_TYPE_VARCHAR) {
vars[i].varTypeNum = DPI_ORACLE_TYPE_TIMESTAMP_LTZ;
vars[i].nativeTypeNum = DPI_NATIVE_TYPE_DOUBLE;
}
break;
case DPI_ORACLE_TYPE_CLOB:
case DPI_ORACLE_TYPE_NCLOB:
if (vars[i].varTypeNum == DPI_ORACLE_TYPE_VARCHAR ||
vars[i].varTypeNum == DPI_ORACLE_TYPE_NVARCHAR)
vars[i].maxSize = (uint32_t) -1;
break;
case DPI_ORACLE_TYPE_BLOB:
if (vars[i].varTypeNum == DPI_ORACLE_TYPE_RAW)
vars[i].maxSize = (uint32_t) -1;
break;
case DPI_ORACLE_TYPE_LONG_VARCHAR:
case DPI_ORACLE_TYPE_LONG_RAW:
vars[i].maxSize = (uint32_t) -1;
break;
case DPI_ORACLE_TYPE_OBJECT:
vars[i].dpiObjectTypeHandle = queryInfo.typeInfo.objectType;
break;
// the remaining types are valid but no special processing needs to
// be done
case DPI_ORACLE_TYPE_NUMBER:
case DPI_ORACLE_TYPE_NATIVE_INT:
case DPI_ORACLE_TYPE_NATIVE_FLOAT:
case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
case DPI_ORACLE_TYPE_ROWID:
case DPI_ORACLE_TYPE_STMT:
case DPI_ORACLE_TYPE_JSON:
break;
default:
return njsBaton_setErrorUnsupportedDataType(baton,
queryInfo.typeInfo.oracleTypeNum, i + 1);
}
if (queryInfo.typeInfo.objectType)
vars[i].dpiObjectTypeHandle = queryInfo.typeInfo.objectType;
}
return true;
@ -683,102 +551,6 @@ bool njsVariable_initForQueryJS(njsVariable *vars, uint32_t numVars,
}
//-----------------------------------------------------------------------------
// njsVariable_performMapping()
// Apply any mapping rules that have been specified.
//-----------------------------------------------------------------------------
bool njsVariable_performMapping(njsVariable *var, dpiQueryInfo *queryInfo,
njsBaton *baton)
{
uint32_t i, oracleTypeNum = queryInfo->typeInfo.oracleTypeNum;
// apply "by-name" rules
for (i = 0; i < baton->numFetchInfo; i++) {
// ignore rule if the name does not match
if (queryInfo->nameLength != baton->fetchInfo[i].nameLength)
continue;
if (strncmp(queryInfo->name, baton->fetchInfo[i].name,
queryInfo->nameLength) != 0)
continue;
// perform any mapping specified
if (baton->fetchInfo[i].type == NJS_DATATYPE_STR) {
var->varTypeNum = (oracleTypeNum == DPI_ORACLE_TYPE_NCLOB) ?
DPI_ORACLE_TYPE_NVARCHAR : DPI_ORACLE_TYPE_VARCHAR;
} else if (baton->fetchInfo[i].type == NJS_DATATYPE_BUFFER) {
var->varTypeNum = DPI_ORACLE_TYPE_RAW;
} else if (baton->fetchInfo[i].type == NJS_DATATYPE_DEFAULT) {
var->varTypeNum = queryInfo->typeInfo.oracleTypeNum;
}
return true;
}
// apply fetchAsString rules
for (i = 0; i < baton->numFetchAsStringTypes; i++) {
switch (oracleTypeNum) {
case DPI_ORACLE_TYPE_NUMBER:
case DPI_ORACLE_TYPE_NATIVE_FLOAT:
case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
case DPI_ORACLE_TYPE_NATIVE_INT:
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_NUM) {
var->varTypeNum = DPI_ORACLE_TYPE_VARCHAR;
return true;
}
break;
case DPI_ORACLE_TYPE_DATE:
case DPI_ORACLE_TYPE_TIMESTAMP:
case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_DATE) {
var->varTypeNum = DPI_ORACLE_TYPE_VARCHAR;
return true;
}
break;
case DPI_ORACLE_TYPE_CLOB:
case DPI_ORACLE_TYPE_NCLOB:
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_CLOB) {
var->varTypeNum = (oracleTypeNum == DPI_ORACLE_TYPE_CLOB) ?
DPI_ORACLE_TYPE_VARCHAR : DPI_ORACLE_TYPE_NVARCHAR;
return true;
}
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_NCLOB) {
var->varTypeNum = DPI_ORACLE_TYPE_NVARCHAR;
return true;
}
break;
case DPI_ORACLE_TYPE_RAW:
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_BUFFER) {
var->varTypeNum = DPI_ORACLE_TYPE_VARCHAR;
return true;
}
break;
case DPI_ORACLE_TYPE_JSON:
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_JSON) {
var->varTypeNum = DPI_ORACLE_TYPE_VARCHAR;
return true;
}
break;
default:
break;
}
}
// apply fetchAsBuffer rules
for (i = 0; i < baton->numFetchAsBufferTypes; i++) {
if (queryInfo->typeInfo.oracleTypeNum == DPI_ORACLE_TYPE_BLOB &&
baton->fetchAsBufferTypes[i] == NJS_DATATYPE_BLOB) {
var->varTypeNum = DPI_ORACLE_TYPE_RAW;
return true;
}
}
return true;
}
//-----------------------------------------------------------------------------
// njsVariable_process()
// Process variables used during binding or fetching. REF cursors must have

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2015, 2022, Oracle and/or its affiliates. */
/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. */
/******************************************************************************
*
@ -740,7 +740,7 @@ describe('4. binding.js', function() {
const cursor = result.outBinds.cursor;
const expectedBind = {
name: "STRINGVALUE",
fetchType: oracledb.DB_TYPE_VARCHAR,
fetchType: oracledb.DB_TYPE_CHAR,
dbType: oracledb.DB_TYPE_CHAR,
dbTypeName: "CHAR",
nullable: true,