Skip to content

Commit 1121e5d

Browse files
committed
Work on REF CURSOR crashes and errors
1 parent 3850b65 commit 1121e5d

File tree

8 files changed

+152
-24
lines changed

8 files changed

+152
-24
lines changed

doc/api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2892,7 +2892,8 @@ Oracle REF CURSORS can be fetched in node-oracledb by binding a
28922892
[`getRow()`](#getrow) or [`getRows()`](getrows). When all rows have
28932893
been fetched, or the application does not want to continue getting
28942894
more rows, then the result set must be freed using
2895-
[`close()`](#close).
2895+
[`close()`](#close). If the cursor is not initialized by the procedure, the
2896+
[`ResultSet`](#resultsetclass) object still requires to be closed.
28962897
28972898
When using Oracle Database 11gR2 or greater, then
28982899
[`prefetchRows`](#propdbprefetchrows) can be used to tune the

src/dpi/include/dpiStmt.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,20 @@ typedef enum
9999
DpiIntervalArray /* external only */
100100
} DpiDataType;
101101

102+
103+
/* OCI Stmt Handle state
104+
* For REFCURSORS state should be DpiStmtStateExecuted
105+
*/
106+
typedef enum
107+
{
108+
DpiStmtStateUndefined = -1, /* Undefined */
109+
DpiStmtStateInitialized = 0x001, /* Initialized */
110+
DpiStmtStateExecuted, /* Executed */
111+
DpiStmtStateEndOfFetch /* End-Of-Fetch */
112+
} DpiStmtState;
113+
114+
115+
102116
/*
103117
* For 11g/12c Compatability BIND/DEFINE calls expect ub8 in 12c & ub4 in 11g
104118
* Using this type makes is compile-time selction of 11g or 12c.
@@ -187,6 +201,7 @@ class Stmt
187201

188202
virtual OCIError *getError () = 0;
189203

204+
virtual DpiStmtState getState () = 0;
190205

191206
virtual ~Stmt(){};
192207

src/dpi/src/dpiStmtImpl.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ StmtImpl::StmtImpl (EnvImpl *env, OCIEnv *envh, ConnImpl *conn,
8888

8989
try : conn_(conn), errh_(NULL), svch_(svch),
9090
stmth_(NULL), numCols_ (0),meta_(NULL), stmtType_ (DpiStmtUnknown),
91-
isReturning_(false), isReturningSet_(false), refCursor_(false)
91+
isReturning_(false), isReturningSet_(false), refCursor_(false),
92+
state_(0)
9293
{
9394
// create an OCIError object for this execution
9495
ociCallEnv (OCIHandleAlloc ((void *)envh, (dvoid **)&errh_,
@@ -716,5 +717,40 @@ bool StmtImpl::isReturning ()
716717
return isReturning_;
717718
}
718719

720+
/*****************************************************************************/
721+
/*
722+
DESCRIPTION
723+
To obtain the OCI statement handle state
724+
725+
PARAMETERS
726+
-None-
727+
728+
RETURNS
729+
One of the possible values DpiStmtState
730+
(DpiStmtStateInitialized, DpiStmtStateExecute, DpiStmtEndOfFetch)
731+
*/
732+
DpiStmtState StmtImpl::getState ()
733+
{
734+
DpiStmtState ret = DpiStmtStateUndefined ;
735+
736+
if ( !state_ )
737+
{
738+
ociCall (OCIAttrGet (stmth_, OCI_HTYPE_STMT, (ub4*)&state_, NULL,
739+
OCI_ATTR_STMT_STATE, errh_ ), errh_ );
740+
}
741+
742+
if ( ( state_ & OCI_STMT_STATE_INITIALIZED ) == OCI_STMT_STATE_INITIALIZED )
743+
ret = DpiStmtStateInitialized;
744+
745+
if ( ( state_ & OCI_STMT_STATE_EXECUTED ) == OCI_STMT_STATE_EXECUTED )
746+
ret = DpiStmtStateExecuted;
747+
748+
if ( ( state_ & OCI_STMT_STATE_END_OF_FETCH ) ==
749+
OCI_STMT_STATE_END_OF_FETCH )
750+
ret = DpiStmtStateEndOfFetch;
751+
752+
return ret;
753+
}
754+
719755

720756
/* end of file dpiStmtImpl.cpp */

src/dpi/src/dpiStmtImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ class StmtImpl : public Stmt
9494

9595
virtual OCIError * getError () { return errh_; }
9696

97+
virtual DpiStmtState getState ();
98+
99+
97100
// Is the SQL statement DML or not ?
98101
virtual inline bool isDML () const
99102
{
@@ -134,6 +137,7 @@ class StmtImpl : public Stmt
134137
bool isReturning_; // Does the stmt has RETURNING INTO clause?
135138
bool isReturningSet_; // Has isReturning_ flag queried & set.
136139
bool refCursor_; // refCursor or not.
140+
unsigned long state_; // OCI Stmt State
137141
};
138142

139143

src/njs/src/njsConnection.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,16 @@ void Connection::GetBindUnit (Handle<Value> val, Bind* bind,
593593
goto exitGetBindUnit;
594594
}
595595

596+
/* REFCURSOR(s) are supported only as OUT Binds now */
597+
if ( bind->type == DATA_CURSOR && dir != BIND_OUT )
598+
{
599+
executeBaton->error = NJSMessages::getErrorMsg (
600+
errInvalidPropertyValueInParam,
601+
"type", 2 ) ;
602+
goto exitGetBindUnit;
603+
}
604+
605+
596606
Local<Value> element = bind_unit->Get(NanNew<v8::String>("val"));
597607
switch(dir)
598608
{
@@ -919,6 +929,20 @@ void Connection::Async_Execute (uv_work_t *req)
919929
bind->dttmarr = NULL;
920930
}
921931
}
932+
933+
/* For each OUT Binds of CURSOR type, get the number of columns */
934+
for ( unsigned int b = 0; b < executeBaton->binds.size (); b ++ )
935+
{
936+
Bind *bind = executeBaton->binds[b];
937+
938+
if ( bind->isOut && bind->type == dpi::DpiRSet )
939+
{
940+
bind->flags =
941+
(((Stmt*)bind->value)->getState () == DpiStmtStateExecuted ) ?
942+
BIND_FLAGS_STMT_READY : BIND_FLAGS_STMT_NOT_READY;
943+
}
944+
}
945+
922946
// process any lob descriptor out binds
923947
Connection::Descr2protoILob ( executeBaton, 0, 0);
924948
}
@@ -969,6 +993,14 @@ void Connection::PrepareAndBind (eBaton* executeBaton)
969993
for(unsigned int index = 0 ;index < executeBaton->binds.size();
970994
index++)
971995
{
996+
if ( executeBaton->binds[index]->isOut &&
997+
executeBaton->stmtIsReturning &&
998+
executeBaton->binds[index]->type == dpi::DpiRSet )
999+
{
1000+
executeBaton->error = NJSMessages::getErrorMsg (
1001+
errInvalidResultSet ) ;
1002+
}
1003+
9721004
// Allocate for OUT Binds
9731005
// For DML Returning, allocation happens through callback.
9741006
if ( executeBaton->binds[index]->isOut &&
@@ -1775,9 +1807,12 @@ void Connection::Async_AfterExecute(uv_work_t *req)
17751807
result->Set(NanNew<v8::String>("rows"), NanUndefined());
17761808
Handle<Object> resultSet = NanNew(ResultSet::resultSetTemplate_s)->
17771809
GetFunction() ->NewInstance();
1810+
1811+
/* ResultSet case, the statement object is ready for fetching */
17781812
(ObjectWrap::Unwrap<ResultSet> (resultSet))->
17791813
setResultSet( executeBaton->dpistmt,
1780-
executeBaton );
1814+
executeBaton,
1815+
BIND_FLAGS_STMT_READY );
17811816
result->Set(NanNew<v8::String>("resultSet"), resultSet );
17821817
}
17831818
else
@@ -2010,7 +2045,8 @@ Handle<Value> Connection::GetValueRefCursor ( eBaton *executeBaton,
20102045
GetFunction() ->NewInstance();
20112046
(ObjectWrap::Unwrap<ResultSet> (resultSet))->
20122047
setResultSet( (dpi::Stmt*)(bind->value),
2013-
executeBaton );
2048+
executeBaton,
2049+
bind->flags );
20142050

20152051
// set the prefetch on the cursor object
20162052
((dpi::Stmt*)(bind->value))->prefetchRows(executeBaton->prefetchRows);

src/njs/src/njsConnection.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ using namespace dpi;
6666
class Connection;
6767
class ProtoILob;
6868

69+
/* Flags Constants */
70+
#define BIND_FLAGS_STMT_NOT_READY 0 /* REFCURSOR stmt not-fetch-able */
71+
#define BIND_FLAGS_STMT_READY 1 /* REFCURSOR stmt fetch-able */
6972

7073
/**
7174
* Structure used for binds
@@ -84,11 +87,12 @@ typedef struct Bind
8487
bool isInOut; // Date/Timestamp needs this info
8588
unsigned int rowsReturned; /* number rows returned for
8689
the bind (DML RETURNING) */
90+
unsigned long flags; // Flags applicable for this BIND
8791
dpi::DateTimeArray* dttmarr;
8892

8993
Bind () : key(""), value(NULL), extvalue (NULL), len(NULL), len2(NULL),
9094
maxSize(0), type(0), ind(NULL), isOut(false), isInOut(false),
91-
rowsReturned(0), dttmarr ( NULL )
95+
rowsReturned(0), flags(BIND_FLAGS_STMT_NOT_READY), dttmarr ( NULL )
9296
{}
9397
}Bind;
9498

src/njs/src/njsResultSet.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,27 @@ Persistent<FunctionTemplate> ResultSet::resultSetTemplate_s;
6868
stmt - dpi statement
6969
executeBaton - eBaton structure
7070
*/
71-
void ResultSet::setResultSet ( dpi::Stmt *stmt, eBaton *executeBaton )
71+
void ResultSet::setResultSet ( dpi::Stmt *stmt, eBaton *executeBaton,
72+
unsigned long flags )
7273
{
7374
this->dpistmt_ = stmt;
7475
this->dpienv_ = executeBaton->dpienv;
7576
this->njsconn_ = executeBaton->njsconn;
76-
this->meta_ = stmt->getMetaData();
77-
this->numCols_ = this->dpistmt_->numCols();
77+
if ( ( flags & BIND_FLAGS_STMT_READY ) == BIND_FLAGS_STMT_READY )
78+
{
79+
this->meta_ = stmt->getMetaData();
80+
this->numCols_ = this->dpistmt_->numCols();
81+
}
82+
else if ( ( flags & BIND_FLAGS_STMT_READY ) == BIND_FLAGS_STMT_NOT_READY )
83+
{
84+
/*
85+
* This could happen in REFCURSOR case, when the stored procedure
86+
* did not return a valid handle
87+
*/
88+
this->numCols_ = 0;
89+
this->meta_ = NULL;
90+
}
91+
7892
this->state_ = INACTIVE;
7993
this->outFormat_ = executeBaton->outFormat;
8094
this->fetchRowCount_ = 0;
@@ -173,6 +187,11 @@ NAN_PROPERTY_GETTER(ResultSet::GetMetaData)
173187
ResultSet* njsResultSet = ObjectWrap::Unwrap<ResultSet>(args.Holder());
174188
string msg;
175189

190+
if ( njsResultSet->numCols_ == 0 )
191+
{
192+
njsResultSet->state_ = INVALID;
193+
}
194+
176195
if(!njsResultSet->njsconn_->isValid())
177196
{
178197
msg = NJSMessages::getErrorMsg ( errInvalidConnection );
@@ -234,6 +253,11 @@ NAN_METHOD(ResultSet::GetRow)
234253
getRowsBaton->njsRS = njsResultSet;
235254
NanAssignPersistent(getRowsBaton->cb, callback );
236255

256+
if ( njsResultSet->numCols_ == 0 )
257+
{
258+
njsResultSet->state_ = INVALID ;
259+
}
260+
237261
if(njsResultSet->state_ == INVALID)
238262
{
239263
getRowsBaton->error = NJSMessages::getErrorMsg ( errInvalidResultSet );
@@ -279,6 +303,11 @@ NAN_METHOD(ResultSet::GetRows)
279303
getRowsBaton->njsRS = njsResultSet;
280304
NanAssignPersistent(getRowsBaton->cb, callback );
281305

306+
if ( njsResultSet->numCols_ == 0 )
307+
{
308+
njsResultSet->state_ = INVALID;
309+
}
310+
282311
if(njsResultSet->state_ == INVALID)
283312
{
284313
getRowsBaton->error = NJSMessages::getErrorMsg ( errInvalidResultSet );

src/njs/src/njsResultSet.h

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ class ResultSet: public ObjectWrap {
104104

105105
static void Init(Handle<Object> target);
106106

107-
void setResultSet ( dpi::Stmt *dpistmt, eBaton *executebaton );
107+
void setResultSet ( dpi::Stmt *dpistmt, eBaton *executebaton,
108+
unsigned long flags );
108109

109110

110111
// Define ResultSet Constructor
@@ -135,21 +136,23 @@ class ResultSet: public ObjectWrap {
135136
static void clearFetchBuffer( Define* defineBuffers,
136137
unsigned int numCols );
137138

138-
139-
dpi::Stmt *dpistmt_;
140-
dpi::Env *dpienv_;
141-
Connection *njsconn_;
142-
State state_;
143-
bool rsEmpty_;
144-
Define *defineBuffers_;
145-
unsigned int numCols_;
146-
unsigned int fetchRowCount_;
147-
unsigned int outFormat_;
148-
const dpi::MetaData *meta_;
149-
DataType *fetchAsStringTypes_;
150-
unsigned int fetchAsStringTypesCount_;
151-
FetchInfo *fetchInfo_;
152-
unsigned int fetchInfoCount_;
139+
static void Init ();
140+
141+
142+
dpi::Stmt *dpistmt_;
143+
dpi::Env *dpienv_;
144+
Connection *njsconn_;
145+
State state_;
146+
bool rsEmpty_;
147+
Define *defineBuffers_;
148+
unsigned int numCols_;
149+
unsigned int fetchRowCount_;
150+
unsigned int outFormat_;
151+
const dpi::MetaData *meta_;
152+
DataType *fetchAsStringTypes_;
153+
unsigned int fetchAsStringTypesCount_;
154+
FetchInfo *fetchInfo_;
155+
unsigned int fetchInfoCount_;
153156
};
154157

155158

0 commit comments

Comments
 (0)