Use Result Sets exclusively in C++ code and move direct fetch to JavaScript.
This results in a net performance benefit and simplifies the C++ code considerably.
This commit is contained in:
parent
549dc5bef5
commit
b713c76bc8
|
@ -69,30 +69,81 @@ function queryStream(sql, binding, options) {
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fetchRowsToReturn is used to materialize the rows for an execute call using
|
||||||
|
// the resultSet returned from the C layer.
|
||||||
|
function fetchRowsToReturn(oracledb, executeOpts, result, executeCb) {
|
||||||
|
var rowsFetched = [];
|
||||||
|
var fetchArraySize;
|
||||||
|
var maxRows;
|
||||||
|
|
||||||
|
fetchArraySize = executeOpts.fetchArraySize;
|
||||||
|
if (fetchArraySize === undefined) {
|
||||||
|
fetchArraySize = oracledb.fetchArraySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxRows = executeOpts.maxRows;
|
||||||
|
if (maxRows === undefined) {
|
||||||
|
maxRows = oracledb.maxRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fetchRowsCb = function(err, rows) {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
executeCb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows) {
|
||||||
|
rowsFetched = rowsFetched.concat(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rowsFetched.length == maxRows || rows.length < fetchArraySize) {
|
||||||
|
result.rows = rowsFetched;
|
||||||
|
|
||||||
|
delete result.resultSet;
|
||||||
|
|
||||||
|
executeCb(null, result);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.resultSet.getRows(fetchArraySize, fetchRowsCb);
|
||||||
|
};
|
||||||
|
|
||||||
|
result.resultSet.getRows(fetchArraySize, fetchRowsCb);
|
||||||
|
}
|
||||||
|
|
||||||
// This execute function is used to override the execute method of the Connection
|
// This execute function is used to override the execute method of the Connection
|
||||||
// class, which is defined in the C layer. The override allows us to do things
|
// class, which is defined in the C layer. The override allows us to do things
|
||||||
// like extend out the resultSet instance prior to passing it to the caller.
|
// like extend out the resultSet instance prior to passing it to the caller.
|
||||||
function execute(a1, a2, a3, a4) {
|
function execute(sql, a2, a3, a4) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var binds = [];
|
||||||
|
var executeOpts = {};
|
||||||
var executeCb;
|
var executeCb;
|
||||||
var executeOpts;
|
|
||||||
var custExecuteCb;
|
var custExecuteCb;
|
||||||
|
|
||||||
nodbUtil.assert(arguments.length > 1 && arguments.length < 5, 'NJS-009');
|
nodbUtil.assert(arguments.length > 1 && arguments.length < 5, 'NJS-009');
|
||||||
nodbUtil.assert(typeof a1 === 'string', 'NJS-006', 1);
|
nodbUtil.assert(typeof sql === 'string', 'NJS-006', 1);
|
||||||
|
|
||||||
switch (arguments.length) {
|
switch (arguments.length) {
|
||||||
case 2:
|
case 2:
|
||||||
nodbUtil.assert(typeof a2 === 'function', 'NJS-006', 2);
|
nodbUtil.assert(typeof a2 === 'function', 'NJS-006', 2);
|
||||||
|
executeCb = a2;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
nodbUtil.assert(nodbUtil.isObjectOrArray(a2), 'NJS-006', 2);
|
nodbUtil.assert(nodbUtil.isObjectOrArray(a2), 'NJS-006', 2);
|
||||||
nodbUtil.assert(typeof a3 === 'function', 'NJS-006', 3);
|
nodbUtil.assert(typeof a3 === 'function', 'NJS-006', 3);
|
||||||
|
binds = a2;
|
||||||
|
executeCb = a3;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
nodbUtil.assert(nodbUtil.isObjectOrArray(a2), 'NJS-006', 2);
|
nodbUtil.assert(nodbUtil.isObjectOrArray(a2), 'NJS-006', 2);
|
||||||
nodbUtil.assert(nodbUtil.isObject(a3), 'NJS-006', 3);
|
nodbUtil.assert(nodbUtil.isObject(a3), 'NJS-006', 3);
|
||||||
nodbUtil.assert(typeof a4 === 'function', 'NJS-006', 4);
|
nodbUtil.assert(typeof a4 === 'function', 'NJS-006', 4);
|
||||||
|
binds = a2;
|
||||||
|
executeOpts = a3;
|
||||||
|
executeCb = a4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,36 +159,28 @@ function execute(a1, a2, a3, a4) {
|
||||||
// Need to extend resultsets which may come from either the query results
|
// Need to extend resultsets which may come from either the query results
|
||||||
// or outBinds.
|
// or outBinds.
|
||||||
if (result.resultSet) {
|
if (result.resultSet) {
|
||||||
resultset.extend(result.resultSet, self._oracledb, executeOpts);
|
if (executeOpts.resultSet) {
|
||||||
} else if (result.outBinds) {
|
resultset.extend(result.resultSet, self._oracledb, executeOpts);
|
||||||
outBindsKeys = Object.keys(result.outBinds);
|
executeCb(null, result);
|
||||||
|
} else {
|
||||||
|
fetchRowsToReturn(self._oracledb, executeOpts, result, executeCb);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (result.outBinds) {
|
||||||
|
outBindsKeys = Object.keys(result.outBinds);
|
||||||
|
|
||||||
for (outBindsIdx = 0; outBindsIdx < outBindsKeys.length; outBindsIdx += 1) {
|
for (outBindsIdx = 0; outBindsIdx < outBindsKeys.length; outBindsIdx += 1) {
|
||||||
if (result.outBinds[outBindsKeys[outBindsIdx]] instanceof self._oracledb.ResultSet) {
|
if (result.outBinds[outBindsKeys[outBindsIdx]] instanceof self._oracledb.ResultSet) {
|
||||||
resultset.extend(result.outBinds[outBindsKeys[outBindsIdx]], self._oracledb, executeOpts);
|
resultset.extend(result.outBinds[outBindsKeys[outBindsIdx]], self._oracledb, executeOpts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
executeCb(null, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
executeCb(null, result);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (arguments.length) {
|
self._execute.call(self, sql, binds, executeOpts, custExecuteCb);
|
||||||
case 4:
|
|
||||||
executeCb = a4;
|
|
||||||
executeOpts = a3;
|
|
||||||
self._execute.call(self, a1, a2, a3, custExecuteCb);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
executeCb = a3;
|
|
||||||
executeOpts = a2;
|
|
||||||
self._execute.call(self, a1, a2, custExecuteCb);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
executeCb = a2;
|
|
||||||
self._execute.call(self, a1, custExecuteCb);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
executePromisified = nodbUtil.promisify(execute);
|
executePromisified = nodbUtil.promisify(execute);
|
||||||
|
|
|
@ -181,7 +181,6 @@ njsBaton::~njsBaton()
|
||||||
jsCallingObj.Reset();
|
jsCallingObj.Reset();
|
||||||
jsOracledb.Reset();
|
jsOracledb.Reset();
|
||||||
jsBuffer.Reset();
|
jsBuffer.Reset();
|
||||||
jsRows.Reset();
|
|
||||||
ClearAsyncData();
|
ClearAsyncData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,27 +235,25 @@ void njsBaton::ClearAsyncData()
|
||||||
delete protoILob;
|
delete protoILob;
|
||||||
protoILob = NULL;
|
protoILob = NULL;
|
||||||
}
|
}
|
||||||
if (!keepQueryInfo) {
|
if (queryVars) {
|
||||||
if (queryVars) {
|
delete [] queryVars;
|
||||||
delete [] queryVars;
|
queryVars = NULL;
|
||||||
queryVars = NULL;
|
numQueryVars = 0;
|
||||||
numQueryVars = 0;
|
}
|
||||||
}
|
if (fetchInfo) {
|
||||||
if (fetchInfo) {
|
delete [] fetchInfo;
|
||||||
delete [] fetchInfo;
|
fetchInfo = NULL;
|
||||||
fetchInfo = NULL;
|
numFetchInfo = 0;
|
||||||
numFetchInfo = 0;
|
}
|
||||||
}
|
if (fetchAsStringTypes) {
|
||||||
if (fetchAsStringTypes) {
|
delete [] fetchAsStringTypes;
|
||||||
delete [] fetchAsStringTypes;
|
fetchAsStringTypes = NULL;
|
||||||
fetchAsStringTypes = NULL;
|
numFetchAsStringTypes = 0;
|
||||||
numFetchAsStringTypes = 0;
|
}
|
||||||
}
|
if (fetchAsBufferTypes) {
|
||||||
if (fetchAsBufferTypes) {
|
delete [] fetchAsBufferTypes;
|
||||||
delete [] fetchAsBufferTypes;
|
fetchAsBufferTypes = NULL;
|
||||||
fetchAsBufferTypes = NULL;
|
numFetchAsBufferTypes = 0;
|
||||||
numFetchAsBufferTypes = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,26 +320,18 @@ void njsBaton::AsyncAfterWorkCallback(uv_work_t *req, int status)
|
||||||
callbackArgs[i] = Nan::Undefined();
|
callbackArgs[i] = Nan::Undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no JS callback available, just delete the baton
|
// if this baton is considered the active baton, clear it
|
||||||
if (baton->jsCallback.IsEmpty())
|
if (baton->callingObj && baton == baton->callingObj->activeBaton)
|
||||||
delete baton;
|
baton->callingObj->activeBaton = NULL;
|
||||||
|
|
||||||
// otherwise, call the JS callback
|
// delete the baton before the callback is made so any unnecessary
|
||||||
else {
|
// ODPI-C handles are released as soon as possible
|
||||||
Local<Function> callback = Nan::New<Function>(baton->jsCallback);
|
Local<Function> callback = Nan::New<Function>(baton->jsCallback);
|
||||||
|
delete baton;
|
||||||
|
|
||||||
// if this baton is considered the active baton, clear it
|
// make JS callback
|
||||||
if (baton->callingObj && baton == baton->callingObj->activeBaton)
|
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), callback,
|
||||||
baton->callingObj->activeBaton = NULL;
|
numCallbackArgs, callbackArgs);
|
||||||
|
|
||||||
// delete the baton before the callback is made so any unnecessary
|
|
||||||
// ODPI-C handles are released as soon as possible
|
|
||||||
delete baton;
|
|
||||||
|
|
||||||
// make JS callback
|
|
||||||
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), callback,
|
|
||||||
numCallbackArgs, callbackArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we no longer need the callback args
|
// we no longer need the callback args
|
||||||
delete [] callbackArgs;
|
delete [] callbackArgs;
|
||||||
|
|
|
@ -282,7 +282,6 @@ public:
|
||||||
bool getRS;
|
bool getRS;
|
||||||
bool autoCommit;
|
bool autoCommit;
|
||||||
bool extendedMetaData;
|
bool extendedMetaData;
|
||||||
bool keepQueryInfo;
|
|
||||||
bool isReturning;
|
bool isReturning;
|
||||||
bool isPLSQL;
|
bool isPLSQL;
|
||||||
uint64_t bufferSize;
|
uint64_t bufferSize;
|
||||||
|
@ -293,7 +292,6 @@ public:
|
||||||
Nan::Persistent<Object> jsCallingObj;
|
Nan::Persistent<Object> jsCallingObj;
|
||||||
Nan::Persistent<Object> jsOracledb;
|
Nan::Persistent<Object> jsOracledb;
|
||||||
Nan::Persistent<Object> jsBuffer;
|
Nan::Persistent<Object> jsBuffer;
|
||||||
Nan::Persistent<Object> jsRows;
|
|
||||||
Nan::Persistent<Function> jsCallback;
|
Nan::Persistent<Function> jsCallback;
|
||||||
|
|
||||||
njsBaton(Local<Function> callback, Local<Object> callingObj) :
|
njsBaton(Local<Function> callback, Local<Object> callingObj) :
|
||||||
|
@ -307,8 +305,8 @@ public:
|
||||||
fetchAsStringTypes(NULL), numFetchAsBufferTypes(0),
|
fetchAsStringTypes(NULL), numFetchAsBufferTypes(0),
|
||||||
fetchAsBufferTypes(NULL), protoILob(NULL), externalAuth(false),
|
fetchAsBufferTypes(NULL), protoILob(NULL), externalAuth(false),
|
||||||
getRS(false), autoCommit(false), extendedMetaData(false),
|
getRS(false), autoCommit(false), extendedMetaData(false),
|
||||||
keepQueryInfo(false), isReturning(false), isPLSQL(false),
|
isReturning(false), isPLSQL(false), bufferSize(0), bufferPtr(NULL),
|
||||||
bufferSize(0), bufferPtr(NULL), lobOffset(0), lobAmount(0) {
|
lobOffset(0), lobAmount(0) {
|
||||||
this->jsCallback.Reset(callback);
|
this->jsCallback.Reset(callback);
|
||||||
this->jsCallingObj.Reset(callingObj);
|
this->jsCallingObj.Reset(callingObj);
|
||||||
this->callingObj = Nan::ObjectWrap::Unwrap<njsCommon>(callingObj);
|
this->callingObj = Nan::ObjectWrap::Unwrap<njsCommon>(callingObj);
|
||||||
|
|
|
@ -230,64 +230,6 @@ bool njsConnection::ProcessQueryVars(njsBaton *baton, dpiStmt *dpiStmtHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// njsConnection::ProcessFetch()
|
|
||||||
// Process fetch from DPI statement.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool njsConnection::ProcessFetch(njsBaton *baton)
|
|
||||||
{
|
|
||||||
uint32_t i, numRowsToFetch;
|
|
||||||
njsVariable *var;
|
|
||||||
int moreRows;
|
|
||||||
|
|
||||||
// determine how many rows to fetch; use fetchArraySize unless it is less
|
|
||||||
// than maxRows (no need to waste memory!)
|
|
||||||
numRowsToFetch = baton->fetchArraySize;
|
|
||||||
if (baton->maxRows > 0 && baton->maxRows < baton->fetchArraySize)
|
|
||||||
numRowsToFetch = baton->maxRows;
|
|
||||||
|
|
||||||
// create ODPI-C variables and define them, if necessary
|
|
||||||
for (i = 0; i < baton->numQueryVars; i++) {
|
|
||||||
var = &baton->queryVars[i];
|
|
||||||
if (var->dpiVarHandle && var->maxArraySize >= numRowsToFetch)
|
|
||||||
continue;
|
|
||||||
if (var->dpiVarHandle) {
|
|
||||||
dpiVar_release(var->dpiVarHandle);
|
|
||||||
var->dpiVarHandle = NULL;
|
|
||||||
}
|
|
||||||
if (dpiConn_newVar(baton->dpiConnHandle, var->varTypeNum,
|
|
||||||
var->nativeTypeNum, numRowsToFetch, var->maxSize, 1, 0,
|
|
||||||
NULL, &var->dpiVarHandle, &var->dpiVarData) < 0) {
|
|
||||||
baton->GetDPIError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var->maxArraySize = numRowsToFetch;
|
|
||||||
if (dpiStmt_define(baton->dpiStmtHandle, i + 1,
|
|
||||||
var->dpiVarHandle) < 0) {
|
|
||||||
baton->GetDPIError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set fetch array size as requested
|
|
||||||
if (dpiStmt_setFetchArraySize(baton->dpiStmtHandle, numRowsToFetch) < 0) {
|
|
||||||
baton->GetDPIError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// perform fetch
|
|
||||||
if (dpiStmt_fetchRows(baton->dpiStmtHandle, numRowsToFetch,
|
|
||||||
&baton->bufferRowIndex, &baton->rowsFetched, &moreRows) < 0) {
|
|
||||||
baton->GetDPIError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!moreRows)
|
|
||||||
baton->maxRows = baton->rowsFetched;
|
|
||||||
return ProcessVars(baton, baton->queryVars, baton->numQueryVars,
|
|
||||||
baton->rowsFetched);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// njsConnection::ProcessVars()
|
// njsConnection::ProcessVars()
|
||||||
// Process variables used during binding or fetching. REF cursors must have
|
// Process variables used during binding or fetching. REF cursors must have
|
||||||
|
@ -571,71 +513,6 @@ Local<Value> njsConnection::GetMetaData(njsVariable *vars, uint32_t numVars,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// njsConnection::GetRows()
|
|
||||||
// Populate rows array with the number of rows fetched into buffers.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool njsConnection::GetRows(njsBaton *baton, Local<Object> &rows)
|
|
||||||
{
|
|
||||||
Nan::EscapableHandleScope scope;
|
|
||||||
Local<Object> rowAsObj, tempRows;
|
|
||||||
Local<Array> rowAsArray;
|
|
||||||
Local<Value> val, keyVal;
|
|
||||||
uint32_t rowOffset;
|
|
||||||
njsVariable *var;
|
|
||||||
|
|
||||||
// check to see if we have any rows from a previous invocation
|
|
||||||
Local<Object> origRowsObj = Nan::New(baton->jsRows);
|
|
||||||
if (origRowsObj.IsEmpty()) {
|
|
||||||
rowOffset = 0;
|
|
||||||
tempRows = Nan::New<Array>(baton->rowsFetched);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// if no rows fetched, just return previous invocation's array as there
|
|
||||||
// is no need to concatenate!
|
|
||||||
if (baton->rowsFetched == 0) {
|
|
||||||
rows = scope.Escape(origRowsObj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new array that can contain the previous invocation's array
|
|
||||||
// and the new rows fetched this invocation
|
|
||||||
Local<Array> origRows = Local<Array>::Cast(origRowsObj);
|
|
||||||
uint32_t numRows = baton->rowsFetched + origRows->Length();
|
|
||||||
tempRows = Nan::New<Array>(numRows);
|
|
||||||
for (uint32_t row = 0; row < origRows->Length(); row++) {
|
|
||||||
Local<Value> val = Nan::Get(origRows, row).ToLocalChecked();
|
|
||||||
Nan::Set(tempRows, row, val);
|
|
||||||
}
|
|
||||||
rowOffset = origRows->Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// populate rows fetched this invocation
|
|
||||||
for (uint32_t row = 0; row < baton->rowsFetched; row++) {
|
|
||||||
if (baton->outFormat == NJS_ROWS_ARRAY)
|
|
||||||
rowAsArray = Nan::New<Array>(baton->numQueryVars);
|
|
||||||
else rowAsObj = Nan::New<Object>();
|
|
||||||
for (uint32_t col = 0; col < baton->numQueryVars; col++) {
|
|
||||||
var = &baton->queryVars[col];
|
|
||||||
if (!njsConnection::GetScalarValueFromVar(baton, var, row, val))
|
|
||||||
return false;
|
|
||||||
if (baton->outFormat == NJS_ROWS_ARRAY)
|
|
||||||
Nan::Set(rowAsArray, col, val);
|
|
||||||
else {
|
|
||||||
keyVal = Nan::New<String>(var->name).ToLocalChecked();
|
|
||||||
Nan::Set(rowAsObj, keyVal, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (baton->outFormat == NJS_ROWS_ARRAY)
|
|
||||||
Nan::Set(tempRows, row + rowOffset, rowAsArray);
|
|
||||||
else Nan::Set(tempRows, row + rowOffset, rowAsObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
rows = scope.Escape(tempRows);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// njsConnection::ProcessBinds()
|
// njsConnection::ProcessBinds()
|
||||||
// Process binds passed through to execute call. A variable is created for
|
// Process binds passed through to execute call. A variable is created for
|
||||||
|
@ -1450,7 +1327,7 @@ NAN_METHOD(njsConnection::Execute)
|
||||||
std::string sql;
|
std::string sql;
|
||||||
njsBaton *baton;
|
njsBaton *baton;
|
||||||
|
|
||||||
connection = (njsConnection*) ValidateArgs(info, 2, 4);
|
connection = (njsConnection*) ValidateArgs(info, 4, 4);
|
||||||
if (!connection)
|
if (!connection)
|
||||||
return;
|
return;
|
||||||
if (!connection->GetStringArg(info, 0, sql))
|
if (!connection->GetStringArg(info, 0, sql))
|
||||||
|
@ -1472,11 +1349,10 @@ NAN_METHOD(njsConnection::Execute)
|
||||||
baton->outFormat = oracledb->getOutFormat();
|
baton->outFormat = oracledb->getOutFormat();
|
||||||
baton->autoCommit = oracledb->getAutoCommit();
|
baton->autoCommit = oracledb->getAutoCommit();
|
||||||
baton->extendedMetaData = oracledb->getExtendedMetaData();
|
baton->extendedMetaData = oracledb->getExtendedMetaData();
|
||||||
baton->getRS = false;
|
|
||||||
}
|
}
|
||||||
if (ok && info.Length() > 2)
|
if (ok)
|
||||||
ok = ProcessBinds(info, 1, baton);
|
ok = ProcessBinds(info, 1, baton);
|
||||||
if (ok && info.Length() > 3)
|
if (ok)
|
||||||
ProcessOptions(info, 2, baton);
|
ProcessOptions(info, 2, baton);
|
||||||
baton->CheckJSException(&tryCatch);
|
baton->CheckJSException(&tryCatch);
|
||||||
baton->QueueWork("Execute", Async_Execute, Async_AfterExecute, 2);
|
baton->QueueWork("Execute", Async_Execute, Async_AfterExecute, 2);
|
||||||
|
@ -1504,7 +1380,7 @@ void njsConnection::Async_Execute(njsBaton *baton)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for queries, perform defines and ensure that rows have been fetched
|
// for queries, perform defines
|
||||||
if (baton->numQueryVars > 0) {
|
if (baton->numQueryVars > 0) {
|
||||||
|
|
||||||
// perform defines
|
// perform defines
|
||||||
|
@ -1513,12 +1389,6 @@ void njsConnection::Async_Execute(njsBaton *baton)
|
||||||
baton->numQueryVars))
|
baton->numQueryVars))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// when not getting a result set, process fetch completely
|
|
||||||
if (!baton->getRS) {
|
|
||||||
if (!ProcessFetch(baton))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for all other statements, determine the number of rows affected
|
// for all other statements, determine the number of rows affected
|
||||||
// and process any LOBS for out binds, as needed
|
// and process any LOBS for out binds, as needed
|
||||||
} else {
|
} else {
|
||||||
|
@ -1531,14 +1401,6 @@ void njsConnection::Async_Execute(njsBaton *baton)
|
||||||
if (!ProcessVars(baton, baton->bindVars, baton->numBindVars, 1))
|
if (!ProcessVars(baton, baton->bindVars, baton->numBindVars, 1))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if not getting a result set and there are no more rows to fetch, we no
|
|
||||||
// longer require the statement so release it now
|
|
||||||
if (!baton->getRS && baton->rowsFetched == baton->maxRows) {
|
|
||||||
if (dpiStmt_release(baton->dpiStmtHandle) < 0)
|
|
||||||
baton->GetDPIError();
|
|
||||||
baton->dpiStmtHandle = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1552,37 +1414,6 @@ void njsConnection::Async_AfterExecute(njsBaton *baton, Local<Value> argv[])
|
||||||
Local<Object> result = Nan::New<v8::Object>();
|
Local<Object> result = Nan::New<v8::Object>();
|
||||||
Local<Object> callingObj, rows;
|
Local<Object> callingObj, rows;
|
||||||
Local<Function> callback;
|
Local<Function> callback;
|
||||||
njsBaton *newBaton;
|
|
||||||
|
|
||||||
// for direct fetch, first check to see if more round trips are required
|
|
||||||
if (baton->queryVars && !baton->getRS) {
|
|
||||||
if (!njsConnection::GetRows(baton, rows))
|
|
||||||
return;
|
|
||||||
if (baton->rowsFetched > 0 &&
|
|
||||||
(baton->maxRows == 0 || baton->rowsFetched < baton->maxRows)) {
|
|
||||||
callback = Nan::New<Function>(baton->jsCallback);
|
|
||||||
callingObj = Nan::New(baton->jsCallingObj);
|
|
||||||
newBaton = new njsBaton(callback, callingObj);
|
|
||||||
baton->jsCallback.Reset();
|
|
||||||
newBaton->fetchArraySize = baton->fetchArraySize;
|
|
||||||
if (baton->maxRows > 0)
|
|
||||||
newBaton->maxRows = baton->maxRows - baton->rowsFetched;
|
|
||||||
newBaton->jsRows.Reset(rows);
|
|
||||||
newBaton->dpiStmtHandle = baton->dpiStmtHandle;
|
|
||||||
baton->dpiStmtHandle = NULL;
|
|
||||||
newBaton->dpiConnHandle = baton->dpiConnHandle;
|
|
||||||
baton->dpiConnHandle = NULL;
|
|
||||||
newBaton->jsOracledb.Reset(baton->jsOracledb);
|
|
||||||
newBaton->queryVars = baton->queryVars;
|
|
||||||
newBaton->numQueryVars = baton->numQueryVars;
|
|
||||||
newBaton->outFormat = baton->outFormat;
|
|
||||||
newBaton->getRS = false;
|
|
||||||
baton->keepQueryInfo = true;
|
|
||||||
newBaton->QueueWork("Execute", Async_ExecuteGetMoreRows,
|
|
||||||
Async_AfterExecute, 2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle queries
|
// handle queries
|
||||||
if (baton->queryVars) {
|
if (baton->queryVars) {
|
||||||
|
@ -1598,22 +1429,10 @@ void njsConnection::Async_AfterExecute(njsBaton *baton, Local<Value> argv[])
|
||||||
GetMetaData(baton->queryVars, baton->numQueryVars,
|
GetMetaData(baton->queryVars, baton->numQueryVars,
|
||||||
baton->extendedMetaData));
|
baton->extendedMetaData));
|
||||||
|
|
||||||
// return result set, if requested to do so
|
// assign result set
|
||||||
if (baton->getRS) {
|
Local<Object> resultSet = njsResultSet::CreateFromBaton(baton);
|
||||||
Local<Object> resultSet = njsResultSet::CreateFromBaton(baton);
|
Nan::Set(result, Nan::New<String>("resultSet").ToLocalChecked(),
|
||||||
Nan::Set(result, Nan::New<String>("rows").ToLocalChecked(),
|
resultSet);
|
||||||
Nan::Undefined());
|
|
||||||
Nan::Set(result, Nan::New<String>("resultSet").ToLocalChecked(),
|
|
||||||
resultSet);
|
|
||||||
|
|
||||||
// otherwise, return rows
|
|
||||||
} else {
|
|
||||||
Nan::Set(result, Nan::New<v8::String>("rows").ToLocalChecked(),
|
|
||||||
rows);
|
|
||||||
Nan::Set(result,
|
|
||||||
Nan::New<v8::String>("resultSet").ToLocalChecked(),
|
|
||||||
Nan::Undefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Nan::DefineOwnProperty (result,
|
Nan::DefineOwnProperty (result,
|
||||||
|
@ -1637,17 +1456,6 @@ void njsConnection::Async_AfterExecute(njsBaton *baton, Local<Value> argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// njsConnection::Async_ExecuteGetMoreRows()
|
|
||||||
// Worker function for njsConnection::Execute() method called when additional
|
|
||||||
// round trips to the database are required.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void njsConnection::Async_ExecuteGetMoreRows(njsBaton *baton)
|
|
||||||
{
|
|
||||||
ProcessFetch(baton);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// njsConnection::Release()
|
// njsConnection::Release()
|
||||||
// Releases the connection from use by JS. This releases the connection back
|
// Releases the connection from use by JS. This releases the connection back
|
||||||
|
|
|
@ -71,10 +71,12 @@ public:
|
||||||
static Local<Object> CreateFromBaton(njsBaton *baton);
|
static Local<Object> CreateFromBaton(njsBaton *baton);
|
||||||
static Local<Value> GetMetaData(njsVariable *vars, uint32_t numVars,
|
static Local<Value> GetMetaData(njsVariable *vars, uint32_t numVars,
|
||||||
bool extendedMetaData);
|
bool extendedMetaData);
|
||||||
static bool GetRows(njsBaton* baton, Local<Object> &rows);
|
static bool GetScalarValueFromVar(njsBaton *baton, njsVariable *var,
|
||||||
static bool ProcessFetch(njsBaton* baton);
|
uint32_t pos, Local<Value> &value);
|
||||||
static bool ProcessQueryVars(njsBaton* baton, dpiStmt *dpiStmtHandle,
|
static bool ProcessQueryVars(njsBaton* baton, dpiStmt *dpiStmtHandle,
|
||||||
njsVariable *vars, uint32_t numVars);
|
njsVariable *vars, uint32_t numVars);
|
||||||
|
static bool ProcessVars(njsBaton *baton, njsVariable *vars,
|
||||||
|
uint32_t numVars, uint32_t numElements);
|
||||||
static void Init(Handle<Object> target);
|
static void Init(Handle<Object> target);
|
||||||
bool IsValid() const { return (dpiConnHandle) ? true : false; }
|
bool IsValid() const { return (dpiConnHandle) ? true : false; }
|
||||||
njsErrorType GetInvalidErrorType() const { return errInvalidConnection; }
|
njsErrorType GetInvalidErrorType() const { return errInvalidConnection; }
|
||||||
|
@ -95,7 +97,6 @@ private:
|
||||||
static NAN_METHOD(Execute);
|
static NAN_METHOD(Execute);
|
||||||
static void Async_Execute(njsBaton *baton);
|
static void Async_Execute(njsBaton *baton);
|
||||||
static void Async_AfterExecute(njsBaton *baton, Local<Value> argv[]);
|
static void Async_AfterExecute(njsBaton *baton, Local<Value> argv[]);
|
||||||
static void Async_ExecuteGetMoreRows(njsBaton *baton);
|
|
||||||
|
|
||||||
// Release Method on Connection class
|
// Release Method on Connection class
|
||||||
static NAN_METHOD(Release);
|
static NAN_METHOD(Release);
|
||||||
|
@ -138,8 +139,6 @@ private:
|
||||||
Local<Value> value, uint32_t *bindType, uint32_t *maxSize,
|
Local<Value> value, uint32_t *bindType, uint32_t *maxSize,
|
||||||
njsBaton *baton, bool scalarOnly = false);
|
njsBaton *baton, bool scalarOnly = false);
|
||||||
static Local<Value> GetOutBinds(njsBaton *baton);
|
static Local<Value> GetOutBinds(njsBaton *baton);
|
||||||
static bool GetScalarValueFromVar(njsBaton *baton, njsVariable *var,
|
|
||||||
uint32_t pos, Local<Value> &value);
|
|
||||||
static bool GetValueFromVar(njsBaton *baton, njsVariable *var,
|
static bool GetValueFromVar(njsBaton *baton, njsVariable *var,
|
||||||
Local<Value> &value);
|
Local<Value> &value);
|
||||||
static bool MapByName(njsBaton *baton, dpiQueryInfo *queryInfo,
|
static bool MapByName(njsBaton *baton, dpiQueryInfo *queryInfo,
|
||||||
|
@ -157,8 +156,6 @@ private:
|
||||||
njsBaton *baton);
|
njsBaton *baton);
|
||||||
static bool ProcessScalarBindValue(Local<Value> bindValue,
|
static bool ProcessScalarBindValue(Local<Value> bindValue,
|
||||||
njsVariable *var, uint32_t pos, njsBaton *baton);
|
njsVariable *var, uint32_t pos, njsBaton *baton);
|
||||||
static bool ProcessVars(njsBaton *baton, njsVariable *vars,
|
|
||||||
uint32_t numVars, uint32_t numElements);
|
|
||||||
static bool ProcessOptions(Nan::NAN_METHOD_ARGS_TYPE args,
|
static bool ProcessOptions(Nan::NAN_METHOD_ARGS_TYPE args,
|
||||||
unsigned int index, njsBaton *baton);
|
unsigned int index, njsBaton *baton);
|
||||||
static void SetTextAttribute(Nan::NAN_SETTER_ARGS_TYPE args,
|
static void SetTextAttribute(Nan::NAN_SETTER_ARGS_TYPE args,
|
||||||
|
|
|
@ -108,7 +108,7 @@ void njsResultSet::Init(Handle<Object> target)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// njsResultSet::CreateFromBaton()
|
// njsResultSet::CreateFromBaton()
|
||||||
// Create a new result set from the baton (
|
// Create a new result set from the baton.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
Local<Object> njsResultSet::CreateFromBaton(njsBaton *baton)
|
Local<Object> njsResultSet::CreateFromBaton(njsBaton *baton)
|
||||||
{
|
{
|
||||||
|
@ -128,6 +128,10 @@ Local<Object> njsResultSet::CreateFromBaton(njsBaton *baton)
|
||||||
resultSet->outFormat = baton->outFormat;
|
resultSet->outFormat = baton->outFormat;
|
||||||
resultSet->numQueryVars = baton->numQueryVars;
|
resultSet->numQueryVars = baton->numQueryVars;
|
||||||
resultSet->queryVars = baton->queryVars;
|
resultSet->queryVars = baton->queryVars;
|
||||||
|
if (!baton->getRS) {
|
||||||
|
resultSet->autoClose = true;
|
||||||
|
resultSet->maxRows = baton->maxRows;
|
||||||
|
}
|
||||||
baton->queryVars = NULL;
|
baton->queryVars = NULL;
|
||||||
resultSet->activeBaton = NULL;
|
resultSet->activeBaton = NULL;
|
||||||
resultSet->jsConnection.Reset(baton->jsCallingObj);
|
resultSet->jsConnection.Reset(baton->jsCallingObj);
|
||||||
|
@ -163,6 +167,7 @@ bool njsResultSet::CreateFromRefCursor(njsBaton *baton, dpiStmt *dpiStmtHandle,
|
||||||
resultSet->activeBaton = NULL;
|
resultSet->activeBaton = NULL;
|
||||||
resultSet->queryVars = queryVars;
|
resultSet->queryVars = queryVars;
|
||||||
resultSet->numQueryVars = numQueryVars;
|
resultSet->numQueryVars = numQueryVars;
|
||||||
|
baton->queryVars = NULL;
|
||||||
value = scope.Escape(obj);
|
value = scope.Escape(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -257,17 +262,11 @@ NAN_METHOD(njsResultSet::GetRows)
|
||||||
baton->error = njsMessages::Get(errBusyResultSet);
|
baton->error = njsMessages::Get(errBusyResultSet);
|
||||||
else if (baton->error.empty()) {
|
else if (baton->error.empty()) {
|
||||||
resultSet->activeBaton = baton;
|
resultSet->activeBaton = baton;
|
||||||
baton->SetDPIStmtHandle(resultSet->dpiStmtHandle);
|
|
||||||
baton->SetDPIConnHandle(resultSet->dpiConnHandle);
|
|
||||||
baton->outFormat = resultSet->outFormat;
|
baton->outFormat = resultSet->outFormat;
|
||||||
baton->queryVars = resultSet->queryVars;
|
|
||||||
baton->numQueryVars = resultSet->numQueryVars;
|
|
||||||
baton->keepQueryInfo = true;
|
|
||||||
baton->jsOracledb.Reset(resultSet->jsOracledb);
|
baton->jsOracledb.Reset(resultSet->jsOracledb);
|
||||||
baton->maxRows = maxRows;
|
|
||||||
baton->fetchArraySize = maxRows;
|
baton->fetchArraySize = maxRows;
|
||||||
}
|
}
|
||||||
baton->QueueWork("GetRowsCommon", Async_GetRows, Async_AfterGetRows, 2);
|
baton->QueueWork("GetRows", Async_GetRows, Async_AfterGetRows, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -277,7 +276,71 @@ NAN_METHOD(njsResultSet::GetRows)
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void njsResultSet::Async_GetRows(njsBaton *baton)
|
void njsResultSet::Async_GetRows(njsBaton *baton)
|
||||||
{
|
{
|
||||||
njsConnection::ProcessFetch(baton);
|
njsResultSet *resultSet = (njsResultSet*) baton->callingObj;
|
||||||
|
uint32_t i, numRowsToFetch;
|
||||||
|
njsVariable *var;
|
||||||
|
int moreRows;
|
||||||
|
|
||||||
|
// determine how many rows to fetch; use fetchArraySize unless it is less
|
||||||
|
// than maxRows (no need to waste memory!)
|
||||||
|
numRowsToFetch = baton->fetchArraySize;
|
||||||
|
if (resultSet->maxRows > 0 && resultSet->maxRows < numRowsToFetch)
|
||||||
|
numRowsToFetch = resultSet->maxRows;
|
||||||
|
|
||||||
|
// create ODPI-C variables and define them, if necessary
|
||||||
|
for (i = 0; i < resultSet->numQueryVars; i++) {
|
||||||
|
var = &resultSet->queryVars[i];
|
||||||
|
if (var->dpiVarHandle && var->maxArraySize >= numRowsToFetch)
|
||||||
|
continue;
|
||||||
|
if (var->dpiVarHandle) {
|
||||||
|
if (dpiVar_release(var->dpiVarHandle) < 0) {
|
||||||
|
baton->GetDPIError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var->dpiVarHandle = NULL;
|
||||||
|
}
|
||||||
|
if (dpiConn_newVar(resultSet->dpiConnHandle, var->varTypeNum,
|
||||||
|
var->nativeTypeNum, numRowsToFetch, var->maxSize, 1, 0, NULL,
|
||||||
|
&var->dpiVarHandle, &var->dpiVarData) < 0) {
|
||||||
|
baton->GetDPIError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var->maxArraySize = numRowsToFetch;
|
||||||
|
if (dpiStmt_define(resultSet->dpiStmtHandle, i + 1,
|
||||||
|
var->dpiVarHandle) < 0) {
|
||||||
|
baton->GetDPIError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set fetch array size as requested
|
||||||
|
if (dpiStmt_setFetchArraySize(resultSet->dpiStmtHandle,
|
||||||
|
numRowsToFetch) < 0) {
|
||||||
|
baton->GetDPIError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform fetch
|
||||||
|
if (dpiStmt_fetchRows(resultSet->dpiStmtHandle, numRowsToFetch,
|
||||||
|
&baton->bufferRowIndex, &baton->rowsFetched, &moreRows) < 0) {
|
||||||
|
baton->GetDPIError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// result sets that should be auto closed are closed if the result set
|
||||||
|
// is exhaused or the maximum number of rows has been fetched
|
||||||
|
if (moreRows && resultSet->maxRows > 0) {
|
||||||
|
if (baton->rowsFetched == resultSet->maxRows)
|
||||||
|
moreRows = 0;
|
||||||
|
else resultSet->maxRows -= baton->rowsFetched;
|
||||||
|
}
|
||||||
|
if (!moreRows && resultSet->autoClose) {
|
||||||
|
dpiStmt_release(resultSet->dpiStmtHandle);
|
||||||
|
resultSet->dpiStmtHandle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
njsConnection::ProcessVars(baton, resultSet->queryVars,
|
||||||
|
resultSet->numQueryVars, baton->rowsFetched);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -288,10 +351,37 @@ void njsResultSet::Async_GetRows(njsBaton *baton)
|
||||||
void njsResultSet::Async_AfterGetRows(njsBaton *baton, Local<Value> argv[])
|
void njsResultSet::Async_AfterGetRows(njsBaton *baton, Local<Value> argv[])
|
||||||
{
|
{
|
||||||
Nan::EscapableHandleScope scope;
|
Nan::EscapableHandleScope scope;
|
||||||
|
Local<Object> rowAsObj, rows;
|
||||||
|
Local<Value> val, keyVal;
|
||||||
|
Local<Array> rowAsArray;
|
||||||
|
njsResultSet *resultSet;
|
||||||
|
njsVariable *var;
|
||||||
|
|
||||||
Local<Object> rows;
|
rows = Nan::New<Array>(baton->rowsFetched);
|
||||||
if (!njsConnection::GetRows(baton, rows))
|
resultSet = (njsResultSet*) baton->callingObj;
|
||||||
return;
|
for (uint32_t row = 0; row < baton->rowsFetched; row++) {
|
||||||
|
if (baton->outFormat == NJS_ROWS_ARRAY)
|
||||||
|
rowAsArray = Nan::New<Array>(resultSet->numQueryVars);
|
||||||
|
else rowAsObj = Nan::New<Object>();
|
||||||
|
for (uint32_t col = 0; col < resultSet->numQueryVars; col++) {
|
||||||
|
var = &resultSet->queryVars[col];
|
||||||
|
if (!njsConnection::GetScalarValueFromVar(baton, var, row, val))
|
||||||
|
return;
|
||||||
|
if (baton->outFormat == NJS_ROWS_ARRAY)
|
||||||
|
Nan::Set(rowAsArray, col, val);
|
||||||
|
else {
|
||||||
|
keyVal = Nan::New<String>(var->name).ToLocalChecked();
|
||||||
|
Nan::Set(rowAsObj, keyVal, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (baton->outFormat == NJS_ROWS_ARRAY)
|
||||||
|
Nan::Set(rows, row, rowAsArray);
|
||||||
|
else Nan::Set(rows, row, rowAsObj);
|
||||||
|
}
|
||||||
|
if (!resultSet->dpiStmtHandle) {
|
||||||
|
delete [] resultSet->queryVars;
|
||||||
|
resultSet->queryVars = NULL;
|
||||||
|
}
|
||||||
argv[1] = scope.Escape(rows);
|
argv[1] = scope.Escape(rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,7 +444,6 @@ void njsResultSet::Async_AfterClose(njsBaton *baton, Local<Value> argv[])
|
||||||
|
|
||||||
resultSet->jsConnection.Reset();
|
resultSet->jsConnection.Reset();
|
||||||
resultSet->jsOracledb.Reset();
|
resultSet->jsOracledb.Reset();
|
||||||
baton->keepQueryInfo = false;
|
|
||||||
baton->queryVars = resultSet->queryVars;
|
baton->queryVars = resultSet->queryVars;
|
||||||
baton->numQueryVars = resultSet->numQueryVars;
|
baton->numQueryVars = resultSet->numQueryVars;
|
||||||
resultSet->queryVars = NULL;
|
resultSet->queryVars = NULL;
|
||||||
|
|
|
@ -79,7 +79,8 @@ public:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
njsResultSet() : dpiStmtHandle(NULL), dpiConnHandle(NULL), numQueryVars(0),
|
njsResultSet() : dpiStmtHandle(NULL), dpiConnHandle(NULL), numQueryVars(0),
|
||||||
queryVars(NULL), outFormat(0), extendedMetaData(false) {}
|
queryVars(NULL), outFormat(0), maxRows(0),
|
||||||
|
extendedMetaData(false), autoClose(false) {}
|
||||||
~njsResultSet();
|
~njsResultSet();
|
||||||
|
|
||||||
static NAN_METHOD(New);
|
static NAN_METHOD(New);
|
||||||
|
@ -105,7 +106,9 @@ private:
|
||||||
uint32_t numQueryVars;
|
uint32_t numQueryVars;
|
||||||
njsVariable *queryVars;
|
njsVariable *queryVars;
|
||||||
uint32_t outFormat;
|
uint32_t outFormat;
|
||||||
|
uint32_t maxRows;
|
||||||
bool extendedMetaData;
|
bool extendedMetaData;
|
||||||
|
bool autoClose;
|
||||||
Nan::Persistent<Object> jsOracledb;
|
Nan::Persistent<Object> jsOracledb;
|
||||||
Nan::Persistent<Object> jsConnection;
|
Nan::Persistent<Object> jsConnection;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue