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;
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
function execute(a1, a2, a3, a4) {
|
||||
function execute(sql, a2, a3, a4) {
|
||||
var self = this;
|
||||
var binds = [];
|
||||
var executeOpts = {};
|
||||
var executeCb;
|
||||
var executeOpts;
|
||||
var custExecuteCb;
|
||||
|
||||
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) {
|
||||
case 2:
|
||||
nodbUtil.assert(typeof a2 === 'function', 'NJS-006', 2);
|
||||
executeCb = a2;
|
||||
break;
|
||||
case 3:
|
||||
nodbUtil.assert(nodbUtil.isObjectOrArray(a2), 'NJS-006', 2);
|
||||
nodbUtil.assert(typeof a3 === 'function', 'NJS-006', 3);
|
||||
binds = a2;
|
||||
executeCb = a3;
|
||||
break;
|
||||
case 4:
|
||||
nodbUtil.assert(nodbUtil.isObjectOrArray(a2), 'NJS-006', 2);
|
||||
nodbUtil.assert(nodbUtil.isObject(a3), 'NJS-006', 3);
|
||||
nodbUtil.assert(typeof a4 === 'function', 'NJS-006', 4);
|
||||
binds = a2;
|
||||
executeOpts = a3;
|
||||
executeCb = a4;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -108,36 +159,28 @@ function execute(a1, a2, a3, a4) {
|
|||
// Need to extend resultsets which may come from either the query results
|
||||
// or outBinds.
|
||||
if (result.resultSet) {
|
||||
resultset.extend(result.resultSet, self._oracledb, executeOpts);
|
||||
} else if (result.outBinds) {
|
||||
outBindsKeys = Object.keys(result.outBinds);
|
||||
if (executeOpts.resultSet) {
|
||||
resultset.extend(result.resultSet, self._oracledb, executeOpts);
|
||||
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) {
|
||||
if (result.outBinds[outBindsKeys[outBindsIdx]] instanceof self._oracledb.ResultSet) {
|
||||
resultset.extend(result.outBinds[outBindsKeys[outBindsIdx]], self._oracledb, executeOpts);
|
||||
for (outBindsIdx = 0; outBindsIdx < outBindsKeys.length; outBindsIdx += 1) {
|
||||
if (result.outBinds[outBindsKeys[outBindsIdx]] instanceof self._oracledb.ResultSet) {
|
||||
resultset.extend(result.outBinds[outBindsKeys[outBindsIdx]], self._oracledb, executeOpts);
|
||||
}
|
||||
}
|
||||
}
|
||||
executeCb(null, result);
|
||||
}
|
||||
|
||||
executeCb(null, result);
|
||||
};
|
||||
|
||||
switch (arguments.length) {
|
||||
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;
|
||||
}
|
||||
self._execute.call(self, sql, binds, executeOpts, custExecuteCb);
|
||||
}
|
||||
|
||||
executePromisified = nodbUtil.promisify(execute);
|
||||
|
|
|
@ -181,7 +181,6 @@ njsBaton::~njsBaton()
|
|||
jsCallingObj.Reset();
|
||||
jsOracledb.Reset();
|
||||
jsBuffer.Reset();
|
||||
jsRows.Reset();
|
||||
ClearAsyncData();
|
||||
}
|
||||
|
||||
|
@ -236,27 +235,25 @@ void njsBaton::ClearAsyncData()
|
|||
delete protoILob;
|
||||
protoILob = NULL;
|
||||
}
|
||||
if (!keepQueryInfo) {
|
||||
if (queryVars) {
|
||||
delete [] queryVars;
|
||||
queryVars = NULL;
|
||||
numQueryVars = 0;
|
||||
}
|
||||
if (fetchInfo) {
|
||||
delete [] fetchInfo;
|
||||
fetchInfo = NULL;
|
||||
numFetchInfo = 0;
|
||||
}
|
||||
if (fetchAsStringTypes) {
|
||||
delete [] fetchAsStringTypes;
|
||||
fetchAsStringTypes = NULL;
|
||||
numFetchAsStringTypes = 0;
|
||||
}
|
||||
if (fetchAsBufferTypes) {
|
||||
delete [] fetchAsBufferTypes;
|
||||
fetchAsBufferTypes = NULL;
|
||||
numFetchAsBufferTypes = 0;
|
||||
}
|
||||
if (queryVars) {
|
||||
delete [] queryVars;
|
||||
queryVars = NULL;
|
||||
numQueryVars = 0;
|
||||
}
|
||||
if (fetchInfo) {
|
||||
delete [] fetchInfo;
|
||||
fetchInfo = NULL;
|
||||
numFetchInfo = 0;
|
||||
}
|
||||
if (fetchAsStringTypes) {
|
||||
delete [] fetchAsStringTypes;
|
||||
fetchAsStringTypes = NULL;
|
||||
numFetchAsStringTypes = 0;
|
||||
}
|
||||
if (fetchAsBufferTypes) {
|
||||
delete [] fetchAsBufferTypes;
|
||||
fetchAsBufferTypes = NULL;
|
||||
numFetchAsBufferTypes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,26 +320,18 @@ void njsBaton::AsyncAfterWorkCallback(uv_work_t *req, int status)
|
|||
callbackArgs[i] = Nan::Undefined();
|
||||
}
|
||||
|
||||
// if no JS callback available, just delete the baton
|
||||
if (baton->jsCallback.IsEmpty())
|
||||
delete baton;
|
||||
// if this baton is considered the active baton, clear it
|
||||
if (baton->callingObj && baton == baton->callingObj->activeBaton)
|
||||
baton->callingObj->activeBaton = NULL;
|
||||
|
||||
// otherwise, call the JS callback
|
||||
else {
|
||||
Local<Function> callback = Nan::New<Function>(baton->jsCallback);
|
||||
// delete the baton before the callback is made so any unnecessary
|
||||
// ODPI-C handles are released as soon as possible
|
||||
Local<Function> callback = Nan::New<Function>(baton->jsCallback);
|
||||
delete baton;
|
||||
|
||||
// if this baton is considered the active baton, clear it
|
||||
if (baton->callingObj && baton == baton->callingObj->activeBaton)
|
||||
baton->callingObj->activeBaton = NULL;
|
||||
|
||||
// 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);
|
||||
}
|
||||
// make JS callback
|
||||
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), callback,
|
||||
numCallbackArgs, callbackArgs);
|
||||
|
||||
// we no longer need the callback args
|
||||
delete [] callbackArgs;
|
||||
|
|
|
@ -282,7 +282,6 @@ public:
|
|||
bool getRS;
|
||||
bool autoCommit;
|
||||
bool extendedMetaData;
|
||||
bool keepQueryInfo;
|
||||
bool isReturning;
|
||||
bool isPLSQL;
|
||||
uint64_t bufferSize;
|
||||
|
@ -293,7 +292,6 @@ public:
|
|||
Nan::Persistent<Object> jsCallingObj;
|
||||
Nan::Persistent<Object> jsOracledb;
|
||||
Nan::Persistent<Object> jsBuffer;
|
||||
Nan::Persistent<Object> jsRows;
|
||||
Nan::Persistent<Function> jsCallback;
|
||||
|
||||
njsBaton(Local<Function> callback, Local<Object> callingObj) :
|
||||
|
@ -307,8 +305,8 @@ public:
|
|||
fetchAsStringTypes(NULL), numFetchAsBufferTypes(0),
|
||||
fetchAsBufferTypes(NULL), protoILob(NULL), externalAuth(false),
|
||||
getRS(false), autoCommit(false), extendedMetaData(false),
|
||||
keepQueryInfo(false), isReturning(false), isPLSQL(false),
|
||||
bufferSize(0), bufferPtr(NULL), lobOffset(0), lobAmount(0) {
|
||||
isReturning(false), isPLSQL(false), bufferSize(0), bufferPtr(NULL),
|
||||
lobOffset(0), lobAmount(0) {
|
||||
this->jsCallback.Reset(callback);
|
||||
this->jsCallingObj.Reset(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()
|
||||
// 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()
|
||||
// Process binds passed through to execute call. A variable is created for
|
||||
|
@ -1450,7 +1327,7 @@ NAN_METHOD(njsConnection::Execute)
|
|||
std::string sql;
|
||||
njsBaton *baton;
|
||||
|
||||
connection = (njsConnection*) ValidateArgs(info, 2, 4);
|
||||
connection = (njsConnection*) ValidateArgs(info, 4, 4);
|
||||
if (!connection)
|
||||
return;
|
||||
if (!connection->GetStringArg(info, 0, sql))
|
||||
|
@ -1472,11 +1349,10 @@ NAN_METHOD(njsConnection::Execute)
|
|||
baton->outFormat = oracledb->getOutFormat();
|
||||
baton->autoCommit = oracledb->getAutoCommit();
|
||||
baton->extendedMetaData = oracledb->getExtendedMetaData();
|
||||
baton->getRS = false;
|
||||
}
|
||||
if (ok && info.Length() > 2)
|
||||
if (ok)
|
||||
ok = ProcessBinds(info, 1, baton);
|
||||
if (ok && info.Length() > 3)
|
||||
if (ok)
|
||||
ProcessOptions(info, 2, baton);
|
||||
baton->CheckJSException(&tryCatch);
|
||||
baton->QueueWork("Execute", Async_Execute, Async_AfterExecute, 2);
|
||||
|
@ -1504,7 +1380,7 @@ void njsConnection::Async_Execute(njsBaton *baton)
|
|||
return;
|
||||
}
|
||||
|
||||
// for queries, perform defines and ensure that rows have been fetched
|
||||
// for queries, perform defines
|
||||
if (baton->numQueryVars > 0) {
|
||||
|
||||
// perform defines
|
||||
|
@ -1513,12 +1389,6 @@ void njsConnection::Async_Execute(njsBaton *baton)
|
|||
baton->numQueryVars))
|
||||
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
|
||||
// and process any LOBS for out binds, as needed
|
||||
} else {
|
||||
|
@ -1531,14 +1401,6 @@ void njsConnection::Async_Execute(njsBaton *baton)
|
|||
if (!ProcessVars(baton, baton->bindVars, baton->numBindVars, 1))
|
||||
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> callingObj, rows;
|
||||
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
|
||||
if (baton->queryVars) {
|
||||
|
@ -1598,22 +1429,10 @@ void njsConnection::Async_AfterExecute(njsBaton *baton, Local<Value> argv[])
|
|||
GetMetaData(baton->queryVars, baton->numQueryVars,
|
||||
baton->extendedMetaData));
|
||||
|
||||
// return result set, if requested to do so
|
||||
if (baton->getRS) {
|
||||
Local<Object> resultSet = njsResultSet::CreateFromBaton(baton);
|
||||
Nan::Set(result, Nan::New<String>("rows").ToLocalChecked(),
|
||||
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());
|
||||
}
|
||||
// assign result set
|
||||
Local<Object> resultSet = njsResultSet::CreateFromBaton(baton);
|
||||
Nan::Set(result, Nan::New<String>("resultSet").ToLocalChecked(),
|
||||
resultSet);
|
||||
|
||||
} else {
|
||||
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()
|
||||
// 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<Value> GetMetaData(njsVariable *vars, uint32_t numVars,
|
||||
bool extendedMetaData);
|
||||
static bool GetRows(njsBaton* baton, Local<Object> &rows);
|
||||
static bool ProcessFetch(njsBaton* baton);
|
||||
static bool GetScalarValueFromVar(njsBaton *baton, njsVariable *var,
|
||||
uint32_t pos, Local<Value> &value);
|
||||
static bool ProcessQueryVars(njsBaton* baton, dpiStmt *dpiStmtHandle,
|
||||
njsVariable *vars, uint32_t numVars);
|
||||
static bool ProcessVars(njsBaton *baton, njsVariable *vars,
|
||||
uint32_t numVars, uint32_t numElements);
|
||||
static void Init(Handle<Object> target);
|
||||
bool IsValid() const { return (dpiConnHandle) ? true : false; }
|
||||
njsErrorType GetInvalidErrorType() const { return errInvalidConnection; }
|
||||
|
@ -95,7 +97,6 @@ private:
|
|||
static NAN_METHOD(Execute);
|
||||
static void Async_Execute(njsBaton *baton);
|
||||
static void Async_AfterExecute(njsBaton *baton, Local<Value> argv[]);
|
||||
static void Async_ExecuteGetMoreRows(njsBaton *baton);
|
||||
|
||||
// Release Method on Connection class
|
||||
static NAN_METHOD(Release);
|
||||
|
@ -138,8 +139,6 @@ private:
|
|||
Local<Value> value, uint32_t *bindType, uint32_t *maxSize,
|
||||
njsBaton *baton, bool scalarOnly = false);
|
||||
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,
|
||||
Local<Value> &value);
|
||||
static bool MapByName(njsBaton *baton, dpiQueryInfo *queryInfo,
|
||||
|
@ -157,8 +156,6 @@ private:
|
|||
njsBaton *baton);
|
||||
static bool ProcessScalarBindValue(Local<Value> bindValue,
|
||||
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,
|
||||
unsigned int index, njsBaton *baton);
|
||||
static void SetTextAttribute(Nan::NAN_SETTER_ARGS_TYPE args,
|
||||
|
|
|
@ -108,7 +108,7 @@ void njsResultSet::Init(Handle<Object> target)
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
// njsResultSet::CreateFromBaton()
|
||||
// Create a new result set from the baton (
|
||||
// Create a new result set from the baton.
|
||||
//-----------------------------------------------------------------------------
|
||||
Local<Object> njsResultSet::CreateFromBaton(njsBaton *baton)
|
||||
{
|
||||
|
@ -128,6 +128,10 @@ Local<Object> njsResultSet::CreateFromBaton(njsBaton *baton)
|
|||
resultSet->outFormat = baton->outFormat;
|
||||
resultSet->numQueryVars = baton->numQueryVars;
|
||||
resultSet->queryVars = baton->queryVars;
|
||||
if (!baton->getRS) {
|
||||
resultSet->autoClose = true;
|
||||
resultSet->maxRows = baton->maxRows;
|
||||
}
|
||||
baton->queryVars = NULL;
|
||||
resultSet->activeBaton = NULL;
|
||||
resultSet->jsConnection.Reset(baton->jsCallingObj);
|
||||
|
@ -163,6 +167,7 @@ bool njsResultSet::CreateFromRefCursor(njsBaton *baton, dpiStmt *dpiStmtHandle,
|
|||
resultSet->activeBaton = NULL;
|
||||
resultSet->queryVars = queryVars;
|
||||
resultSet->numQueryVars = numQueryVars;
|
||||
baton->queryVars = NULL;
|
||||
value = scope.Escape(obj);
|
||||
return true;
|
||||
}
|
||||
|
@ -257,17 +262,11 @@ NAN_METHOD(njsResultSet::GetRows)
|
|||
baton->error = njsMessages::Get(errBusyResultSet);
|
||||
else if (baton->error.empty()) {
|
||||
resultSet->activeBaton = baton;
|
||||
baton->SetDPIStmtHandle(resultSet->dpiStmtHandle);
|
||||
baton->SetDPIConnHandle(resultSet->dpiConnHandle);
|
||||
baton->outFormat = resultSet->outFormat;
|
||||
baton->queryVars = resultSet->queryVars;
|
||||
baton->numQueryVars = resultSet->numQueryVars;
|
||||
baton->keepQueryInfo = true;
|
||||
baton->jsOracledb.Reset(resultSet->jsOracledb);
|
||||
baton->maxRows = 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)
|
||||
{
|
||||
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[])
|
||||
{
|
||||
Nan::EscapableHandleScope scope;
|
||||
Local<Object> rowAsObj, rows;
|
||||
Local<Value> val, keyVal;
|
||||
Local<Array> rowAsArray;
|
||||
njsResultSet *resultSet;
|
||||
njsVariable *var;
|
||||
|
||||
Local<Object> rows;
|
||||
if (!njsConnection::GetRows(baton, rows))
|
||||
return;
|
||||
rows = Nan::New<Array>(baton->rowsFetched);
|
||||
resultSet = (njsResultSet*) baton->callingObj;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -354,7 +444,6 @@ void njsResultSet::Async_AfterClose(njsBaton *baton, Local<Value> argv[])
|
|||
|
||||
resultSet->jsConnection.Reset();
|
||||
resultSet->jsOracledb.Reset();
|
||||
baton->keepQueryInfo = false;
|
||||
baton->queryVars = resultSet->queryVars;
|
||||
baton->numQueryVars = resultSet->numQueryVars;
|
||||
resultSet->queryVars = NULL;
|
||||
|
|
|
@ -79,7 +79,8 @@ public:
|
|||
private:
|
||||
|
||||
njsResultSet() : dpiStmtHandle(NULL), dpiConnHandle(NULL), numQueryVars(0),
|
||||
queryVars(NULL), outFormat(0), extendedMetaData(false) {}
|
||||
queryVars(NULL), outFormat(0), maxRows(0),
|
||||
extendedMetaData(false), autoClose(false) {}
|
||||
~njsResultSet();
|
||||
|
||||
static NAN_METHOD(New);
|
||||
|
@ -105,7 +106,9 @@ private:
|
|||
uint32_t numQueryVars;
|
||||
njsVariable *queryVars;
|
||||
uint32_t outFormat;
|
||||
uint32_t maxRows;
|
||||
bool extendedMetaData;
|
||||
bool autoClose;
|
||||
Nan::Persistent<Object> jsOracledb;
|
||||
Nan::Persistent<Object> jsConnection;
|
||||
|
||||
|
|
Loading…
Reference in New Issue