Add REF CURSOR support. Result Set tidy ups. Renumber constants.

This commit is contained in:
Christopher Jones 2015-07-20 17:45:32 +10:00
parent 322e447324
commit bc6b88eefe
11 changed files with 241 additions and 124 deletions

View File

@ -31,15 +31,16 @@ try {
var oracledb_ins = new oracledb.Oracledb();
oracledb_ins.STRING = 1;
oracledb_ins.NUMBER = 2;
oracledb_ins.DATE = 3;
oracledb_ins.STRING = 2001;
oracledb_ins.NUMBER = 2002;
oracledb_ins.DATE = 2003;
oracledb_ins.CURSOR = 2004;
oracledb_ins.ARRAY = 1;
oracledb_ins.OBJECT = 2;
oracledb_ins.BIND_IN = 3001;
oracledb_ins.BIND_INOUT = 3002;
oracledb_ins.BIND_OUT = 3003;
oracledb_ins.BIND_IN = 1;
oracledb_ins.BIND_INOUT = 2;
oracledb_ins.BIND_OUT = 3;
oracledb_ins.ARRAY = 4001;
oracledb_ins.OBJECT = 4002;
module.exports = oracledb_ins;

View File

@ -67,7 +67,7 @@ public:
virtual void action(const string &action) = 0;
// methods
virtual Stmt* getStmt (const string &sql) = 0;
virtual Stmt* getStmt (const string &sql="") = 0;
virtual void commit() = 0;

View File

@ -86,6 +86,7 @@ typedef enum
DpiClob = 112,
DpiBlob = 113,
DpiBfile = 114,
DpiRSet = 116,
DpiYearMonth = 182, /* internal only */
DpiDaySecond = 183, /* internal only */
DpiTimestamp = 187, /* internal only */

View File

@ -414,7 +414,6 @@ Stmt* ConnImpl::getStmt (const string &sql)
}
/*****************************************************************************/
/*
DESCRIPTION

View File

@ -88,17 +88,27 @@ StmtImpl::StmtImpl (EnvImpl *env, OCIEnv *envh, ConnImpl *conn,
try : conn_(conn), errh_(NULL), svch_(svch),
stmth_(NULL), numCols_ (0),meta_(NULL), stmtType_ (DpiStmtUnknown),
isReturning_(false), isReturningSet_(false)
isReturning_(false), isReturningSet_(false), refCursor_(false)
{
// create an OCIError object for this execution
ociCallEnv (OCIHandleAlloc ((void *)envh, (dvoid **)&errh_,
OCI_HTYPE_ERROR, 0, (dvoid **)0), envh);
// Prepare OCIStmt object with given sql statement.
ociCall (OCIStmtPrepare2 (svch_, &stmth_, errh_, (oratext *)sql.data(),
(ub4)sql.length(), NULL, 0, OCI_NTV_SYNTAX,
OCI_DEFAULT),
errh_);
if(!sql.empty())
{
// Prepare OCIStmt object with given sql statement.
ociCall (OCIStmtPrepare2 (svch_, &stmth_, errh_, (oratext *)sql.data(),
(ub4)sql.length(), NULL, 0, OCI_NTV_SYNTAX,
OCI_DEFAULT),
errh_);
}
else
{
// to build empty stmt object used for ref cursors.
ociCall (OCIHandleAlloc ((void *)envh, (dvoid **)&stmth_,
OCI_HTYPE_STMT,0, (dvoid **)0), errh_);
refCursor_ = true;
}
}
catch (...)
{
@ -240,7 +250,9 @@ void StmtImpl::bind (unsigned int pos, unsigned short type, void *buf,
OCIBind *b = (OCIBind *)0;
ociCall (DPIBINDBYPOS (stmth_, &b, errh_, pos,
(cb ? NULL : buf), bufSize, type,
(cb ? NULL : (type==DpiRSet) ?
(void *)&(((StmtImpl*)buf)->stmth_) : buf),
(type == DpiRSet) ? 0 : bufSize, type,
(cb ? NULL : ind),
(cb ? NULL : bufLen),
NULL, 0, NULL,
@ -285,7 +297,9 @@ void StmtImpl::bind (const unsigned char *name, int nameLen,
OCIBind *b = (OCIBind *)0;
ociCall (DPIBINDBYNAME (stmth_, &b, errh_, name, nameLen,
(cb ? NULL : buf), bufSize, type,
(cb ? NULL : (type == DpiRSet) ?
(void *)&((StmtImpl*)buf)->stmth_: buf),
(type == DpiRSet) ? 0 : bufSize, type,
(cb ? NULL : ind),
(cb ? NULL : bufLen),
NULL, 0, NULL,
@ -518,7 +532,12 @@ void StmtImpl::cleanup ()
}
if ( stmth_)
{
ociCall ( OCIStmtRelease (stmth_, errh_, NULL, 0, OCI_DEFAULT), errh_ );
// Release not called for ref cursor.
if ( refCursor_ )
OCIHandleFree ( stmth_, OCI_HTYPE_STMT );
else
ociCall ( OCIStmtRelease (stmth_, errh_, NULL, 0, OCI_DEFAULT), errh_ );
stmth_ = NULL;
}
if ( errh_)

View File

@ -112,7 +112,6 @@ public:
dvoid **bufpp, ub4 **alenp, ub1 *piecep,
dvoid **indpp, ub2 **rcodepp );
private:
void cleanup ();
@ -131,6 +130,7 @@ private:
DpiStmtType stmtType_; // Statement Type (Query, DML, ... )
bool isReturning_; // Does the stmt has RETURNING INTO clause?
bool isReturningSet_; // Has isReturning_ flag queried & set.
bool refCursor_; // refCursor or not.
};

View File

@ -59,6 +59,7 @@ Persistent<FunctionTemplate> Connection::connectionTemplate_s;
#define NJS_MAX_OUT_BIND_SIZE 200
// # of milliseconds in a day. Used to convert from/to v8::Date to Oracle/Date
#define NJS_DAY2MS (24.0 * 60.0 * 60.0 * 1000.0 )
#define NJS_PREFETCH_NON_RESULTSET 2
/*****************************************************************************/
@ -574,16 +575,20 @@ void Connection::GetOutBindParams (unsigned short dataType, Bind* bind,
switch(dataType)
{
case DATA_STR :
bind->type = dpi::DpiVarChar;
bind->type = dpi::DpiVarChar;
break;
case DATA_NUM :
bind->type = dpi::DpiDouble;
bind->maxSize = sizeof(double);
bind->type = dpi::DpiDouble;
bind->maxSize = sizeof(double);
break;
case DATA_DATE :
bind->extvalue = (long double *) malloc ( sizeof ( long double ) );
bind->type = dpi::DpiTimestampLTZ;
bind->maxSize = 0;
bind->type = dpi::DpiTimestampLTZ;
bind->maxSize = 0;
break;
case DATA_CURSOR :
bind->type = dpi::DpiRSet;
bind->maxSize = 0;
break;
default :
executeBaton->error= NJSMessages::getErrorMsg(errInvalidBindDataType,2);
@ -737,6 +742,20 @@ void Connection::Async_Execute (uv_work_t *req)
if (executeBaton->st == DpiStmtSelect)
{
// set prefetch
if(executeBaton->getRS)
{
// prefetchRows either default or value provided by user
executeBaton->dpistmt->prefetchRows(executeBaton->prefetchRows);
}
else
{
// OCI default prefetchRows value is 1.
// Set default prefetchRows to 2 to cut down on additional
// fetch round-trip for single row non-resultset cases
executeBaton->dpistmt->prefetchRows(NJS_PREFETCH_NON_RESULTSET);
}
executeBaton->dpistmt->execute(0, executeBaton->autoCommit);
const dpi::MetaData* meta = executeBaton->dpistmt->getMetaData();
executeBaton->numCols = executeBaton->dpistmt->numCols();
@ -748,6 +767,10 @@ void Connection::Async_Execute (uv_work_t *req)
goto exitAsyncExecute;
Connection::DoDefines(executeBaton, meta, executeBaton->numCols);
/* If any errors while creating define structures, bail out */
if ( !executeBaton->error.empty() )
goto exitAsyncExecute;
Connection::DoFetch(executeBaton);
}
else
@ -849,9 +872,6 @@ void Connection::PrepareAndBind (eBaton* executeBaton)
executeBaton->st = executeBaton->dpistmt->stmtType ();
executeBaton->stmtIsReturning = executeBaton->dpistmt->isReturning ();
// set prefetch
executeBaton->dpistmt->prefetchRows(executeBaton->prefetchRows);
if(!executeBaton->binds.empty())
{
if(!executeBaton->binds[0]->key.empty())
@ -881,6 +901,12 @@ void Connection::PrepareAndBind (eBaton* executeBaton)
false, 1 );
}
// Allocate handle for Ref Cursor
if ( executeBaton->binds[index]->type == DpiRSet )
{
executeBaton->binds[index]->value = executeBaton->dpiconn->
getStmt();
}
// Convert v8::Date to Oracle DB Type
if ( executeBaton->binds[index]->type == DpiTimestampLTZ )
@ -923,6 +949,13 @@ void Connection::PrepareAndBind (eBaton* executeBaton)
return;
}
// Allocate handle for Ref Cursor
if ( executeBaton->binds[index]->type == DpiRSet )
{
executeBaton->binds[index]->value = executeBaton->dpiconn->
getStmt();
}
// Allocate for OUT Binds
// For DML Returning, allocation happens through callback
if ( executeBaton->binds[index]->isOut &&
@ -1237,15 +1270,7 @@ v8::Handle<v8::Value> Connection::GetRows (eBaton* executeBaton)
Local<Array> row = NanNew<v8::Array>(executeBaton->numCols);
for(unsigned int j = 0; j < executeBaton->numCols; j++)
{
long double *dblArr = (long double *)executeBaton->defines[j].buf;
row->Set(j, Connection::GetValue(
executeBaton->defines[j].ind[i],
executeBaton->defines[j].fetchType,
(executeBaton->defines[j].fetchType == DpiTimestampLTZ ) ?
(void *) &dblArr[i] :
(void *) ((char *)(executeBaton->defines[j].buf) +
( i * (executeBaton->defines[j].maxSize ))),
executeBaton->defines[j].len[i]));
row->Set(j, Connection::GetValue(executeBaton, true, j, i));
}
rowsArray->Set(i, row);
}
@ -1258,24 +1283,17 @@ v8::Handle<v8::Value> Connection::GetRows (eBaton* executeBaton)
for(unsigned int j = 0; j < executeBaton->numCols; j++)
{
long double *dblArr = (long double * )executeBaton->defines[j].buf;
row->Set(NanNew<v8::String>(executeBaton->columnNames[j].c_str(),
(int) executeBaton->columnNames[j].length()),
Connection::GetValue(
executeBaton->defines[j].ind[i],
executeBaton->defines[j].fetchType,
(executeBaton->defines[j].fetchType == DpiTimestampLTZ ) ?
(void *) &dblArr[i] :
(void *) ((char *)(executeBaton->defines[j].buf) +
( i * (executeBaton->defines[j].maxSize ))),
executeBaton->defines[j].len[i]));
Connection::GetValue(executeBaton, true, j, i));
}
rowsArray->Set(i, row);
}
break;
default :
executeBaton->error = NJSMessages::getErrorMsg(errInvalidPropertyValue, "outFormat");
executeBaton->error = NJSMessages::getErrorMsg(errInvalidPropertyValue,
"outFormat");
goto exitGetRows;
break;
}
@ -1288,6 +1306,111 @@ v8::Handle<v8::Value> Connection::GetRows (eBaton* executeBaton)
DESCRIPTION
Method to create handle from C++ value
PARAMETERS:
executeBaton - eBaton struct
isQuery - true if define struct is to be processed
false if bind struct is to be processed
index - column index in define array /
index in binds vector
row - row index in define->buf /
always 0 for bind->value
RETURNS:
Handle
*/
Handle<Value> Connection::GetValue ( eBaton *executeBaton,
bool isQuery,
unsigned int index,
unsigned int row )
{
NanEscapableScope();
if(isQuery)
{
// SELECT queries
Define *define = &(executeBaton->defines[index]);
long double *dblArr = (long double *)define->buf;
return NanEscapeScope( Connection::GetValueCommon(
define->ind[row],
define->fetchType,
(define->fetchType == DpiTimestampLTZ ) ?
(void *) &dblArr[row] :
(void *) ((char *)(define->buf) +
( row * (define->maxSize ))),
define->len[row] ));
}
else
{
// DML, PL/SQL execution
Bind *bind = executeBaton->binds[index];
if(executeBaton->stmtIsReturning)
{
return NanEscapeScope(Connection::GetArrayValue (
executeBaton->binds[index],
(unsigned long)executeBaton->rowsAffected ) );
}
else if(bind->type == DpiRSet)
{
return NanEscapeScope ( Connection::GetValueRefCursor (
executeBaton, bind ));
}
else
{
return NanEscapeScope ( Connection::GetValueCommon (
bind->ind[row],
bind->type,
(bind->type == DpiTimestampLTZ ) ?
bind->extvalue : bind->value,
bind->len[row] ));
}
}
}
/*****************************************************************************/
/*
DESCRIPTION
Method to create handle for refcursor
PARAMETERS:
executeBaton - struct eBaton
bind - struct bind
RETURNS:
Handle
*/
Handle<Value> Connection::GetValueRefCursor ( eBaton *executeBaton,
Bind *bind )
{
NanEscapableScope();
Handle<Object> resultSet;
Handle<Value> value;
if(bind->ind[0] != -1)
{
resultSet = NanNew(ResultSet::resultSetTemplate_s)->
GetFunction() ->NewInstance();
(ObjectWrap::Unwrap<ResultSet> (resultSet))->
setResultSet( (dpi::Stmt*)(bind->value),
executeBaton->dpienv,
executeBaton->njsconn,
executeBaton->outFormat );
// set the prefetch on the cursor object
((dpi::Stmt*)(bind->value))->prefetchRows(executeBaton->prefetchRows);
value = resultSet;
}
else
{
value = NanNull();
}
return NanEscapeScope(value);
}
/*****************************************************************************/
/*
DESCRIPTION
Method to create handle from C++ value for primitive types
PARAMETERS:
ind - to validate the data,
type - data type of the value,
@ -1297,9 +1420,9 @@ v8::Handle<v8::Value> Connection::GetRows (eBaton* executeBaton)
RETURNS:
Handle
*/
v8::Handle<v8::Value> Connection::GetValue ( short ind, unsigned short type,
void* val, DPI_BUFLEN_TYPE len,
DPI_SZ_TYPE maxSize)
Handle<Value> Connection::GetValueCommon ( short ind,
unsigned short type,
void* val, DPI_BUFLEN_TYPE len )
{
NanEscapableScope();
Handle<Value> value;
@ -1422,17 +1545,12 @@ v8::Handle<v8::Value> Connection::GetOutBinds (eBaton* executeBaton)
if( executeBaton->binds[0]->key.empty() )
{
// Binds as JS array
return NanEscapeScope(GetOutBindArray( executeBaton->binds,
executeBaton->numOutBinds,
executeBaton->stmtIsReturning,
(unsigned long)executeBaton->rowsAffected ));
return NanEscapeScope(GetOutBindArray( executeBaton ));
}
else
{
// Binds as JS object
return NanEscapeScope(GetOutBindObject( executeBaton->binds,
executeBaton->stmtIsReturning,
(unsigned long)executeBaton->rowsAffected ));
return NanEscapeScope(GetOutBindObject( executeBaton ));
}
}
return NanUndefined();
@ -1449,14 +1567,13 @@ v8::Handle<v8::Value> Connection::GetOutBinds (eBaton* executeBaton)
RETURNS:
Outbinds array
*/
v8::Handle<v8::Value> Connection::GetOutBindArray ( std::vector<Bind*> &binds,
unsigned int outCount,
bool isDMLReturning,
unsigned long rowcount )
v8::Handle<v8::Value> Connection::GetOutBindArray ( eBaton *executeBaton )
{
NanEscapableScope();
Local<Array> arrayBinds = NanNew<v8::Array>( outCount );
std::vector<Bind*>binds = executeBaton->binds;
Local<Array> arrayBinds = NanNew<v8::Array>( executeBaton->numOutBinds );
unsigned int it = 0;
for(unsigned int index = 0; index < binds.size(); index++)
@ -1464,20 +1581,7 @@ v8::Handle<v8::Value> Connection::GetOutBindArray ( std::vector<Bind*> &binds,
if(binds[index]->isOut)
{
Handle<Value> val ;
if ( !isDMLReturning )
{
val = Connection::GetValue (
binds[index]->ind[0],
binds[index]->type,
(binds[index]->type == DpiTimestampLTZ ) ?
binds[index]->extvalue :
binds[index]->value,
binds[index]->len[0] ) ;
}
else
{
val = Connection::GetArrayValue ( binds[index], rowcount );
}
val = Connection::GetValue ( executeBaton, false, index );
arrayBinds->Set( it, val );
it ++;
}
@ -1496,10 +1600,10 @@ v8::Handle<v8::Value> Connection::GetOutBindArray ( std::vector<Bind*> &binds,
RETURNS:
Outbinds object
*/
v8::Handle<v8::Value> Connection::GetOutBindObject ( std::vector<Bind*> &binds,
bool isDMLReturning,
unsigned long rowcount )
v8::Handle<v8::Value> Connection::GetOutBindObject ( eBaton *executeBaton )
{
std::vector<Bind*>binds = executeBaton->binds;
NanEscapableScope();
Local<Object> objectBinds = NanNew<v8::Object>();
for(unsigned int index = 0; index < binds.size(); index++)
@ -1510,21 +1614,7 @@ v8::Handle<v8::Value> Connection::GetOutBindObject ( std::vector<Bind*> &binds,
binds[index]->key.erase(binds[index]->key.begin());
if ( !isDMLReturning )
{
val = Connection::GetValue (
binds[index]->ind[0],
binds[index]->type,
(binds[index]->type == DpiTimestampLTZ ) ?
binds[index]->extvalue : binds[index]->value,
binds[index]->len[0],
binds[index]->maxSize );
}
else
{
val = Connection::GetArrayValue ( binds[index], rowcount ) ;
}
val = Connection::GetValue ( executeBaton, false, index );
objectBinds->Set( NanNew<v8::String> ( binds[index]->key.c_str(),
(int) binds[index]->key.length() ),
val );

View File

@ -151,13 +151,14 @@ typedef struct eBaton
// donot free date value here, it is done in DateTimeArray functions
if(binds[index]->type != DpiTimestampLTZ )
{
if( binds[index]->value )
// do not free refcursor type.
if( binds[index]->value && binds[index]->type != DpiRSet )
{
free(binds[index]->value);
}
if ( binds[index]->extvalue )
{
free ( binds[index]->value );
free ( binds[index]->extvalue );
}
if ( binds[index]->ind )
{
@ -208,6 +209,7 @@ public:
static void Descr2Double ( Define* defines, unsigned int numCols,
unsigned int rowsFetched, bool getRS );
bool isValid() { return isValid_; }
Oracledb* oracledb_;
private:
static NAN_METHOD(New);
@ -274,17 +276,22 @@ private:
static void GetOutBindParams (unsigned short dataType, Bind* bind,
eBaton* executeBaton);
static v8::Handle<v8::Value> GetOutBinds (eBaton* executeBaton);
static v8::Handle<v8::Value> GetOutBindArray ( std::vector<Bind*> &binds,
unsigned int outCount,
bool bDMLReturn = false,
unsigned long rowcount = 1);
static v8::Handle<v8::Value> GetOutBindObject (std::vector<Bind*> &binds,
bool bDMLReturn = false,
unsigned long rowcount = 1);
static v8::Handle<v8::Value> GetOutBindArray (eBaton* executeBaton);
static v8::Handle<v8::Value> GetOutBindObject (eBaton* executeBaton);
static v8::Handle<v8::Value> GetArrayValue (Bind *bind, unsigned long count);
static v8::Handle<v8::Value> GetValue (short ind, unsigned short type, void* val,
DPI_BUFLEN_TYPE len,
DPI_SZ_TYPE maxSize = -1);
// to convert DB value to v8::Value
static v8::Handle<v8::Value> GetValue (eBaton *executeBaton,
bool isQuery,
unsigned int index,
unsigned int row = 0);
// for primitive types (Number, String and Date)
static v8::Handle<v8::Value> GetValueCommon (short ind,
unsigned short type,
void* val, DPI_BUFLEN_TYPE len);
// for refcursor
static v8::Handle<v8::Value> GetValueRefCursor (eBaton *executeBaton,
Bind *bind);
static void UpdateDateValue ( eBaton *executeBaton );
static void v8Date2OraDate ( v8::Handle<v8::Value>, Bind *bind);
@ -302,7 +309,6 @@ private:
dpi::Conn* dpiconn_;
bool isValid_;
Oracledb* oracledb_;
};

View File

@ -59,7 +59,6 @@ using namespace node;
using namespace v8;
//peristent ResultSet class handle
Persistent<FunctionTemplate> ResultSet::resultSetTemplate_s;
/*****************************************************************************/
/*
DESCRIPTION
@ -71,14 +70,14 @@ Persistent<FunctionTemplate> ResultSet::resultSetTemplate_s;
conn - njs connection
outFormat - outFormat of the result set
*/
void ResultSet::setResultSet ( dpi::Stmt *stmt, dpi::Env *env,
void ResultSet::setResultSet ( dpi::Stmt *stmt, dpi::Env *dpienv,
Connection *conn, unsigned int outFormat )
{
this->dpistmt_ = stmt;
this->dpienv_ = env;
this->dpienv_ = dpienv;
this->njsconn_ = conn;
this->meta_ = stmt->getMetaData();
this->numCols_ = stmt->numCols();
this->numCols_ = this->dpistmt_->numCols();
this->state_ = INACTIVE;
this->outFormat_ = outFormat;
this->fetchRowCount_ = 0;
@ -282,8 +281,8 @@ void ResultSet::GetRowsCommon(rsBaton *getRowsBaton)
ebaton->columnNames = new std::string[njsRS->numCols_];
ebaton->maxRows = getRowsBaton->numRows;
ebaton->dpistmt = njsRS->dpistmt_;
ebaton->dpienv = njsRS->dpienv_;
ebaton->getRS = true;
ebaton->dpienv = njsRS->njsconn_->oracledb_->getDpiEnv();
ebaton->outFormat = njsRS->outFormat_;
exitGetRowsCommon:

View File

@ -101,8 +101,9 @@ public:
static void Init(Handle<Object> target);
void setResultSet ( dpi::Stmt *dpistmt, dpi::Env *env,
void setResultSet ( dpi::Stmt *dpistmt, dpi::Env *dpienv,
Connection* conn, unsigned int outFormat );
// Define ResultSet Constructor
static Persistent<FunctionTemplate> resultSetTemplate_s ;

View File

@ -61,26 +61,27 @@
typedef enum
{
DATA_UNKNOWN = -1,
DATA_STR = 1,
DATA_NUM = 2,
DATA_DATE = 3
DATA_STR = 2001,
DATA_NUM = 2002,
DATA_DATE = 2003,
DATA_CURSOR = 2004
}DataType;
// User specified bind types.
typedef enum
{
BIND_UNKNOWN = -1,
BIND_IN = 1,
BIND_INOUT = 2,
BIND_OUT = 3
BIND_IN = 3001,
BIND_INOUT = 3002,
BIND_OUT = 3003
}BindType;
// outFormat types.
typedef enum
{
ROWS_UNKNOWN = -1,
ROWS_ARRAY = 1,
ROWS_OBJECT = 2
ROWS_ARRAY = 4001,
ROWS_OBJECT = 4002
}RowsType;
// states