Refactor hook location

This commit is contained in:
Christopher Jones 2023-02-21 17:27:09 +11:00
parent 3a72435272
commit 8ca832a157
7 changed files with 151 additions and 163 deletions

View File

@ -60,8 +60,7 @@ class Connection extends EventEmitter {
// Add values to the execute options from the global settings, if needed.
//---------------------------------------------------------------------------
_addDefaultsToExecOpts(options) {
if (options.fetchInfo === undefined)
options.fetchInfo = [];
options.connection = this;
if (options.keepInStmtCache === undefined)
options.keepInStmtCache = true;
settings.addToOptions(options,
@ -699,7 +698,7 @@ class Connection extends EventEmitter {
outVal.push(this._transformOutBind(val[i], options));
} else if (val instanceof impl.ResultSetImpl) {
outVal = new ResultSet();
outVal._setup(this, val, options);
outVal._setup(this, val);
} else if (val instanceof impl.LobImpl) {
outVal = new Lob();
outVal._setup(val, true);
@ -1067,8 +1066,8 @@ class Connection extends EventEmitter {
// from the result set and then destroy the result set
if (result.resultSet !== undefined) {
const resultSet = new ResultSet();
resultSet._setup(this, result.resultSet, options);
result.metaData = resultSet._setupData.metaData;
resultSet._setup(this, result.resultSet);
result.metaData = resultSet._impl.metaData;
if (options.resultSet) {
result.resultSet = resultSet;
} else {
@ -1092,7 +1091,7 @@ class Connection extends EventEmitter {
for (const key in result.implicitResults) {
const resultSetImpl = result.implicitResults[key];
const resultSet = new ResultSet();
resultSet._setup(this, resultSetImpl, options);
resultSet._setup(this, resultSetImpl);
if (options.resultSet) {
result.implicitResults[key] = resultSet;
} else {

View File

@ -26,12 +26,31 @@
'use strict';
const ConnectionImpl = require('./connection.js');
const constants = require('../constants.js');
const errors = require('../errors.js');
const nodbUtil = require('../util.js');
const settings = require('../settings.js');
// define implementation class
class ResultSetImpl {
//---------------------------------------------------------------------------
// _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);
}
}
//---------------------------------------------------------------------------
// _getConnImpl()
//
@ -39,10 +58,60 @@ class ResultSetImpl {
// ensure serialization of all use of the connection.
//---------------------------------------------------------------------------
_getConnImpl() {
let parentObj = this._parentObj;
while (!(parentObj instanceof ConnectionImpl))
parentObj = parentObj._parentObj;
return parentObj;
return this._parentObj;
}
//---------------------------------------------------------------------------
// _setup()
//
// Setup a result set. The metadata is examined to to determine if any
// columns need to be manipulated before being returned to the caller. If the
// rows fetched from the result set are expected to be objects, a unique set
// of attribute names are also determined.
//---------------------------------------------------------------------------
_setup(options, metaData) {
this._parentObj = options.connection._impl;
this.metaData = metaData;
this.lobIndices = [];
this.dbObjectIndices = [];
this.nestedCursorIndices = [];
this.outFormat = options.outFormat;
this.fetchArraySize = options.fetchArraySize;
this.dbObjectAsPojo = options.dbObjectAsPojo;
this.maxRows = options.maxRows;
const names = new Map();
for (let i = 0; i < metaData.length; i++) {
const name = metaData[i].name;
if (!names.has(name)) {
names.set(name, i);
}
}
for (let i = 0; i < metaData.length; i++) {
const info = metaData[i];
this._determineFetchType(info, options);
if (info.fetchType === constants.DB_TYPE_CURSOR) {
this.nestedCursorIndices.push(i);
} else if (info.fetchType === constants.DB_TYPE_CLOB ||
info.fetchType === constants.DB_TYPE_NCLOB ||
info.fetchType === constants.DB_TYPE_BLOB) {
this.lobIndices.push(i);
} else if (info.fetchType === constants.DB_TYPE_OBJECT) {
const cls = options.connection._getDbObjectClass(info.dbTypeClass);
info.dbTypeClass = cls;
this.dbObjectIndices.push(i);
}
nodbUtil.addTypeProperties(info, "dbType");
let name = info.name;
if (names.get(name) !== i) {
let seqNum = 0;
while (names.has(name)) {
seqNum = seqNum + 1;
name = `${info.name}_${seqNum}`;
}
names.set(name, i);
info.name = name;
}
}
}
//---------------------------------------------------------------------------
@ -54,15 +123,6 @@ class ResultSetImpl {
errors.throwNotImplemented("closing a result set");
}
//---------------------------------------------------------------------------
// getMetaData()
//
// Returns the metadata from the result set.
//---------------------------------------------------------------------------
getMetaData() {
errors.throwNotImplemented("getting metadata");
}
//---------------------------------------------------------------------------
// getRows()
//
@ -72,15 +132,6 @@ 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

@ -30,7 +30,6 @@ 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');
@ -44,23 +43,6 @@ 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()
//
@ -72,8 +54,8 @@ class ResultSet {
// retain initial values of the maximum number of rows to fetch and the
// number of rows to fetch from the database at a single time
let maxRows = this._setupData.maxRows;
let fetchArraySize = this._setupData.fetchArraySize;
let maxRows = this._impl.maxRows;
let fetchArraySize = this._impl.fetchArraySize;
// fetch all rows
let rowsFetched = [];
@ -81,7 +63,7 @@ class ResultSet {
if (maxRows > 0 && fetchArraySize >= maxRows) {
fetchArraySize = maxRows;
}
const rows = await this._impl.getRows(fetchArraySize);
const rows = await this._getRows(fetchArraySize);
if (rows) {
await this._processRows(rows, true);
rowsFetched = rowsFetched.concat(rows);
@ -103,6 +85,28 @@ class ResultSet {
}
//---------------------------------------------------------------------------
// _getRows()
//
// Return up to the specified number of rows from the result set. If nested
// cursors are possible, setup the execute options so that they can be
// examined within the implementation's setup routine.
//---------------------------------------------------------------------------
async _getRows(numRows) {
let options = {};
if (this._impl.nestedCursorIndices.length > 0) {
options = {
connection: this._connection,
outFormat: this._impl.outFormat,
fetchArraySize: this._impl.fetchArraySize,
dbObjectAsPojo: this._impl.dbObjectAsPojo,
maxRows: this._impl.maxRows,
fetchTypeMap: this._impl.fetchTypeMap
};
}
return await this._impl.getRows(numRows, options);
}
//---------------------------------------------------------------------------
// _processRows()
//
@ -113,28 +117,24 @@ class ResultSet {
async _processRows(rows, expandNestedCursors) {
// transform any nested cursors into user facing objects
for (let i = 0; i < this._setupData.nestedCursorSetupData.length; i++) {
const setupData = this._setupData.nestedCursorSetupData[i];
for (const i of this._impl.nestedCursorIndices) {
for (let j = 0; j < rows.length; j++) {
const row = rows[j];
const val = row[setupData.index];
const val = rows[j][i];
if (val) {
const resultSet = new ResultSet();
resultSet._setup(this._connection, val, this._setupData, setupData);
const metaData = this._setupData.metaData[setupData.index];
if (!metaData.metaData)
metaData.metaData = setupData.metaData;
resultSet._setup(this._connection, val);
this._impl.metaData[i].metaData = val.metaData;
if (expandNestedCursors) {
row[setupData.index] = await resultSet._getAllRows();
rows[j][i] = await resultSet._getAllRows();
} else {
row[setupData.index] = resultSet;
rows[j][i] = resultSet;
}
}
}
}
// transform any LOBs into user facing objects
for (const i of this._setupData.lobIndices) {
for (const i of this._impl.lobIndices) {
for (let j = 0; j < rows.length; j++) {
const val = rows[j][i];
if (val) {
@ -145,14 +145,14 @@ class ResultSet {
}
// transform any database objects into user facing objects
for (const i of this._setupData.dbObjectIndices) {
const dbObjectClass = this._setupData.metaData[i].dbTypeClass;
for (const i of this._impl.dbObjectIndices) {
const dbObjectClass = this._impl.metaData[i].dbTypeClass;
for (let j = 0; j < rows.length; j++) {
const val = rows[j][i];
if (val) {
const obj = rows[j][i] = Object.create(dbObjectClass.prototype);
obj._impl = val;
if (this._setupData.dbObjectAsPojo) {
if (this._impl.dbObjectAsPojo) {
rows[j][i] = obj._toPojo();
} else if (obj.isCollection) {
rows[j][i] = new Proxy(obj, BaseDbObject._collectionProxyHandler);
@ -162,11 +162,11 @@ class ResultSet {
}
// create objects, if desired
if (this._setupData.outFormat === constants.OUT_FORMAT_OBJECT) {
if (this._impl.outFormat === constants.OUT_FORMAT_OBJECT) {
for (let i = 0; i < rows.length; i++) {
const origRow = rows[i];
const newRow = rows[i] = {};
const metaData = this._setupData.metaData;
const metaData = this._impl.metaData;
for (let j = 0; j < metaData.length; j++) {
newRow[metaData[j].name] = origRow[j];
}
@ -177,62 +177,11 @@ class ResultSet {
//---------------------------------------------------------------------------
// _setup()
//
// Setup a result set. The metadata is acquired from the implementation and
// used to determine if any columns need to be manipulated before being
// returned to the caller. If the rows fetched from the result set are
// expected to be objects, a unique set of attribute names are also
// determined.
// Setup a result set.
// ---------------------------------------------------------------------------
_setup(connection, resultSetImpl, options, setupData) {
_setup(connection, resultSetImpl) {
this._connection = connection;
this._impl = resultSetImpl;
if (setupData === undefined)
setupData = {};
this._setupData = setupData;
if (setupData.metaData === undefined) {
setupData.metaData = this._impl.getMetaData();
setupData.nestedCursorSetupData = [];
setupData.lobIndices = [];
setupData.dbObjectIndices = [];
setupData.outFormat = options.outFormat;
setupData.fetchArraySize = options.fetchArraySize;
setupData.dbObjectAsPojo = options.dbObjectAsPojo;
setupData.maxRows = options.maxRows;
const names = new Map();
for (let i = 0; i < setupData.metaData.length; i++) {
const name = setupData.metaData[i].name;
if (!names.has(name)) {
names.set(name, i);
}
}
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 ||
info.fetchType === constants.DB_TYPE_NCLOB ||
info.fetchType === constants.DB_TYPE_BLOB) {
setupData.lobIndices.push(i);
} else if (info.fetchType === constants.DB_TYPE_OBJECT) {
const cls = this._connection._getDbObjectClass(info.dbTypeClass);
info.dbTypeClass = cls;
setupData.dbObjectIndices.push(i);
}
nodbUtil.addTypeProperties(info, "dbType");
let name = info.name;
if (names.get(name) !== i) {
let seqNum = 0;
while (names.has(name)) {
seqNum = seqNum + 1;
name = `${info.name}_${seqNum}`;
}
names.set(name, i);
info.name = name;
}
}
}
resultSetImpl.setFetchTypes(setupData.metaData);
}
//---------------------------------------------------------------------------
@ -251,7 +200,6 @@ class ResultSet {
this._processingStarted = true;
const resultSetImpl = this._impl;
delete this._impl;
delete this._setupData;
await resultSetImpl.close();
}
@ -274,7 +222,7 @@ class ResultSet {
this._processingStarted = true;
if (this._rowCache.length == 0) {
const rows = await this._impl.getRows(this._setupData.fetchArraySize);
const rows = await this._getRows(this._impl.fetchArraySize);
await this._processRows(rows, false);
this._rowCache = rows;
}
@ -311,9 +259,9 @@ class ResultSet {
let requestedRows;
if (numRows == 0) {
requestedRows = this._rowCache;
const fetchArraySize = this._setupData.fetchArraySize;
const fetchArraySize = this._impl.fetchArraySize;
while (true) { // eslint-disable-line
const rows = await this._impl.getRows(fetchArraySize);
const rows = await this._getRows(fetchArraySize);
if (rows) {
await this._processRows(rows, false);
requestedRows = requestedRows.concat(rows);
@ -325,14 +273,14 @@ class ResultSet {
}
if (this._rowCache.length === 0) {
requestedRows = await this._impl.getRows(numRows);
requestedRows = await this._getRows(numRows);
await this._processRows(requestedRows, false);
} else {
rowsNeeded = numRows - this._rowCache.length;
if (rowsNeeded <= 0) {
requestedRows = this._rowCache.splice(0, numRows);
} else {
const rows = await this._impl.getRows(rowsNeeded);
const rows = await this._getRows(rowsNeeded);
await this._processRows(rows, false);
requestedRows = this._rowCache.concat(rows);
this._rowCache = [];
@ -349,7 +297,7 @@ class ResultSet {
//---------------------------------------------------------------------------
get metaData() {
if (this._impl) {
return this._setupData.metaData;
return this._impl.metaData;
}
return undefined;
}

View File

@ -337,6 +337,7 @@ void njsBaton_free(njsBaton *baton, napi_env env)
NJS_DELETE_REF_AND_CLEAR(baton->jsBufferRef);
NJS_DELETE_REF_AND_CLEAR(baton->jsCallingObjRef);
NJS_DELETE_REF_AND_CLEAR(baton->jsSubscriptionRef);
NJS_DELETE_REF_AND_CLEAR(baton->jsExecuteOptionsRef);
if (baton->asyncWork) {
napi_delete_async_work(env, baton->asyncWork);
baton->asyncWork = NULL;

View File

@ -566,6 +566,8 @@ NJS_NAPI_METHOD_IMPL_ASYNC(njsConnection_execute, 5, NULL)
NJS_CHECK_NAPI(env, napi_get_value_bool(env, args[4], &executeMany))
// process options
NJS_CHECK_NAPI(env, napi_create_reference(env, args[3], 1,
&baton->jsExecuteOptionsRef))
if (executeMany) {
if (!njsUtils_getNamedPropertyBool(env, args[3], "batchErrors",
&baton->batchErrors))

View File

@ -451,6 +451,7 @@ struct njsBaton {
napi_ref jsBufferRef;
napi_ref jsCallingObjRef;
napi_ref jsSubscriptionRef;
napi_ref jsExecuteOptionsRef;
// constructors
napi_value jsLobConstructor;

View File

@ -34,9 +34,7 @@
// class methods
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);
@ -52,12 +50,8 @@ static NJS_NAPI_FINALIZE(njsResultSet_finalize);
static const napi_property_descriptor njsClassProperties[] = {
{ "close", NULL, njsResultSet_close, NULL, NULL, NULL, napi_default,
NULL },
{ "getMetaData", NULL, njsResultSet_getMetaData, NULL, NULL, NULL,
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 }
};
@ -67,6 +61,9 @@ const njsClassDef njsClassDefResultSet = {
njsClassProperties, false
};
// other methods used internally
static bool njsResultSet_setFetchTypes(napi_env env, njsResultSet *rs,
napi_value allMetadata);
//-----------------------------------------------------------------------------
// njsResultSet_close()
@ -128,24 +125,6 @@ static void njsResultSet_finalize(napi_env env, void *finalizeData,
}
//-----------------------------------------------------------------------------
// njsResultSet_getMetaData()
// Get accessor of "metaData" property.
//-----------------------------------------------------------------------------
NJS_NAPI_METHOD_IMPL_SYNC(njsResultSet_getMetaData, 0, NULL)
{
njsResultSet *rs = (njsResultSet*) callingInstance;
if (rs->handle && rs->conn->handle) {
if (!njsVariable_getMetadataMany(rs->queryVars, rs->numQueryVars, env,
returnValue))
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// njsResultSet_getRows()
// Get a number of rows from the result set.
@ -153,10 +132,12 @@ NJS_NAPI_METHOD_IMPL_SYNC(njsResultSet_getMetaData, 0, NULL)
// PARAMETERS
// - max number of rows to fetch at this time
//-----------------------------------------------------------------------------
NJS_NAPI_METHOD_IMPL_ASYNC(njsResultSet_getRows, 1, NULL)
NJS_NAPI_METHOD_IMPL_ASYNC(njsResultSet_getRows, 2, NULL)
{
NJS_CHECK_NAPI(env, napi_get_value_uint32(env, args[0],
&baton->fetchArraySize))
NJS_CHECK_NAPI(env, napi_create_reference(env, args[1], 1,
&baton->jsExecuteOptionsRef))
return njsBaton_queueWork(baton, env, "GetRows", njsResultSet_getRowsAsync,
njsResultSet_getRowsPostAsync, returnValue);
}
@ -275,7 +256,7 @@ bool njsResultSet_new(njsBaton *baton, napi_env env, njsConnection *conn,
dpiStmt *handle, njsVariable *vars, uint32_t numVars,
napi_value *rsObj)
{
napi_value callingObj;
napi_value fn, temp, args[2];
njsResultSet *rs;
if (baton->outFormat == NJS_ROWS_OBJECT) {
@ -288,13 +269,13 @@ bool njsResultSet_new(njsBaton *baton, napi_env env, njsConnection *conn,
baton->globals->jsResultSetConstructor, rsObj, (void**) &rs))
return false;
// store a reference to the parent object (a connection or a parent result
// set) to ensure that it is not garbage collected during the lifetime of
// the result set
NJS_CHECK_NAPI(env, napi_get_reference_value(env, baton->jsCallingObjRef,
&callingObj))
NJS_CHECK_NAPI(env, napi_set_named_property(env, *rsObj, "_parentObj",
callingObj))
// setup the result set (calls into JavaScript)
NJS_CHECK_NAPI(env, napi_get_reference_value(env,
baton->jsExecuteOptionsRef, &args[0]))
if (!njsVariable_getMetadataMany(vars, numVars, env, &args[1]))
return false;
NJS_CHECK_NAPI(env, napi_get_named_property(env, *rsObj, "_setup", &fn))
NJS_CHECK_NAPI(env, napi_call_function(env, *rsObj, fn, 2, args, &temp))
// perform some initializations
rs->handle = handle;
@ -304,17 +285,22 @@ bool njsResultSet_new(njsBaton *baton, napi_env env, njsConnection *conn,
rs->fetchArraySize = baton->fetchArraySize;
rs->isNested = (baton->callingInstance != (void*) conn);
// set fetch types
if (!njsResultSet_setFetchTypes(env, rs, args[1]))
return false;
return true;
}
//-----------------------------------------------------------------------------
// njsResultSet_setFetchTypes()
// Get accessor of "metaData" property.
// Sets the fetch types after the fetchAsString, fetchAsBuffer and fetchInfo
// rules have all been applied.
//-----------------------------------------------------------------------------
NJS_NAPI_METHOD_IMPL_SYNC(njsResultSet_setFetchTypes, 1, NULL)
static bool njsResultSet_setFetchTypes(napi_env env, njsResultSet *rs,
napi_value allMetadata)
{
njsResultSet *rs = (njsResultSet*) callingInstance;
napi_value metadata, temp;
njsVariable *var;
uint32_t i;
@ -323,7 +309,7 @@ NJS_NAPI_METHOD_IMPL_SYNC(njsResultSet_setFetchTypes, 1, NULL)
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_element(env, allMetadata, 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))