Fix memory leaks with ResultSets

This commit is contained in:
Christopher Jones 2016-05-16 09:48:43 +10:00
parent 5f0cc2f142
commit fa5068f0e9
7 changed files with 197 additions and 107 deletions

View File

@ -38,6 +38,10 @@
# include <dpiConnImpl.h>
#endif
#ifndef DPIEXCEPTIONIMPL_ORACLE
# include <dpiExceptionImpl.h>
#endif
#ifndef DPIDATETIMEARRAYIMPL_ORACLE
#include <dpiDateTimeArrayImpl.h>
#endif
@ -478,57 +482,63 @@ unsigned int StmtImpl::rowsFetched () const
*/
const MetaData* StmtImpl::getMetaData ()
{
numCols();
if (!numCols_)
return NULL;
ub4 col = 0;
void *colDesc = (OCIParam *) 0;
meta_ = new MetaData[numCols_];
void *colName = NULL;
while (col < numCols_)
if ( !meta_ )
{
ociCall(OCIParamGet((void *)stmth_, OCI_HTYPE_STMT, errh_,
&colDesc, (ub4) (col+1)), errh_ );
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM, &colName,
(ub4 *) &(meta_[col].colNameLen),
(ub4) OCI_ATTR_NAME,errh_ ), errh_ );
meta_[col].colName = (unsigned char *) colName;
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].dbType),(ub4 *) 0,
(ub4) OCI_ATTR_DATA_TYPE,
errh_ ), errh_ );
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].dbSize),(ub4 *) 0,
(ub4) OCI_ATTR_DATA_SIZE,
errh_ ), errh_ );
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].isNullable),(ub4*) 0,
(ub4) OCI_ATTR_IS_NULL,
errh_ ), errh_ );
if (meta_[col].dbType == DpiNumber || meta_[col].dbType == DpiBinaryFloat
||meta_[col].dbType == DpiBinaryDouble )
if ( numCols () )
{
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].precision),(ub4* ) 0,
(ub4) OCI_ATTR_PRECISION,
errh_ ), errh_ );
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].scale),(ub4*) 0,
(ub4) OCI_ATTR_SCALE,
errh_ ), errh_ );
}
else
{ // avoid uninitialized variables
meta_[col].precision = 0;
meta_[col].scale = 0;
}
ub4 col = 0;
void *colDesc = (OCIParam *) 0;
void *colName = NULL;
OCIDescriptorFree( colDesc, OCI_DTYPE_PARAM);
col++;
meta_ = new MetaData[numCols_];
if ( !meta_ )
{
throw ExceptionImpl ( DpiErrMemAllocFail ) ;
}
while (col < numCols_)
{
ociCall(OCIParamGet((void *)stmth_, OCI_HTYPE_STMT, errh_,
&colDesc, (ub4) (col+1)), errh_ );
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM, &colName,
(ub4 *) &(meta_[col].colNameLen),
(ub4) OCI_ATTR_NAME,errh_ ), errh_ );
meta_[col].colName = (unsigned char *) colName;
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].dbType),(ub4 *) 0,
(ub4) OCI_ATTR_DATA_TYPE,
errh_ ), errh_ );
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].dbSize),(ub4 *) 0,
(ub4) OCI_ATTR_DATA_SIZE,
errh_ ), errh_ );
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].isNullable),(ub4*) 0,
(ub4) OCI_ATTR_IS_NULL,
errh_ ), errh_ );
if (meta_[col].dbType == DpiNumber || meta_[col].dbType == DpiBinaryFloat
||meta_[col].dbType == DpiBinaryDouble )
{
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].precision),(ub4* ) 0,
(ub4) OCI_ATTR_PRECISION,
errh_ ), errh_ );
ociCall(OCIAttrGet(colDesc, (ub4) OCI_DTYPE_PARAM,
(void*) &(meta_[col].scale),(ub4*) 0,
(ub4) OCI_ATTR_SCALE,
errh_ ), errh_ );
}
else
{ // avoid uninitialized variables
meta_[col].precision = 0;
meta_[col].scale = 0;
}
OCIDescriptorFree( colDesc, OCI_DTYPE_PARAM);
col++;
}
}
}
return meta_;

View File

@ -1505,15 +1505,18 @@ void Connection::Async_Execute (uv_work_t *req)
}
executeBaton->dpistmt->execute(0, executeBaton->autoCommit);
if ( executeBaton->getRS )
{
goto exitAsyncExecute;
}
const dpi::MetaData* meta = executeBaton->dpistmt->getMetaData();
executeBaton->numCols = executeBaton->dpistmt->numCols();
executeBaton->columnNames = new std::string[executeBaton->numCols];
Connection::CopyMetaData( executeBaton->columnNames, meta,
executeBaton->numCols );
if ( executeBaton->getRS )
goto exitAsyncExecute;
Connection::DoDefines(executeBaton, meta, executeBaton->numCols);
/* If any errors while creating define structures, bail out */
if ( !executeBaton->error.empty() )
@ -2300,15 +2303,23 @@ void Connection::DoDefines ( eBaton* executeBaton, const dpi::MetaData* meta,
*/
void Connection::DoFetch (eBaton* executeBaton)
{
NJSErrorType errNum = errSuccess;
executeBaton->dpistmt->fetch ( executeBaton->maxRows );
executeBaton->rowsFetched = executeBaton->dpistmt->rowsFetched();
Connection::Descr2Double ( executeBaton->defines,
executeBaton->numCols,
executeBaton->rowsFetched,
executeBaton->getRS );
Connection::Descr2protoILob ( executeBaton,
executeBaton->numCols,
executeBaton->rowsFetched );
errNum = Connection::Descr2Double ( executeBaton->defines,
executeBaton->numCols,
executeBaton->rowsFetched,
executeBaton->getRS );
if ( !errNum )
{
Connection::Descr2protoILob ( executeBaton,
executeBaton->numCols,
executeBaton->rowsFetched );
}
else
{
executeBaton->error = NJSMessages::getErrorMsg ( errNum );
}
}
/*****************************************************************************/
@ -2322,35 +2333,53 @@ void Connection::DoFetch (eBaton* executeBaton)
rowsFetched - rows fetched
getRS - boolean set for resultset
*/
void Connection::Descr2Double( Define* defines, unsigned int numCols,
NJSErrorType Connection::Descr2Double( Define* defines, unsigned int numCols,
unsigned int rowsFetched, bool getRS )
{
/* Special processing for certain data types */
for (unsigned int col = 0; col < numCols; col ++ )
NJSErrorType errNum = errSuccess;
/* Special processing for certain data types */
for (unsigned int col = 0; !errNum && ( col < numCols ); col ++ )
{
/* Special processing for datetime, as it is obtained as descriptors */
if ( defines[col].dttmarr )
{
long double *dblArr = NULL;
defines[col].buf =
dblArr = (long double *)malloc ( sizeof ( long double ) *
rowsFetched );
for ( int row = 0; row < (int) rowsFetched; row ++ )
if ( !defines[col].buf )
{
dblArr[row] = defines[col].dttmarr->getDateTime (row);
// size_t overflow check not required here as rowsFetched(unsigned int)
// multiplied by sizeof(long double) never cause size_t overflow
defines[col].buf =
dblArr =
(long double *)malloc ( sizeof ( long double ) * rowsFetched );
if( !defines[col].buf )
{
errNum = errInsufficientMemory;
}
}
defines[col].buf = (void *) dblArr;
if ( !getRS )
else
{
defines[col].dttmarr->release ();
defines[col].extbuf = NULL;
dblArr = (long double *) defines[col].buf;
}
if ( !errNum )
{
for ( int row = 0; row < (int) rowsFetched; row ++ )
{
dblArr[row] = defines[col].dttmarr->getDateTime (row);
}
if ( !getRS )
{
defines[col].dttmarr->release ();
defines[col].extbuf = NULL;
}
}
}
}
return errNum;
}
/*****************************************************************************/
@ -2511,13 +2540,6 @@ void Connection::Async_AfterExecute(uv_work_t *req)
switch(executeBaton->st)
{
case DpiStmtSelect :
rowArray = Connection::GetRows(executeBaton);
if(!(executeBaton->error).empty())
{
argv[0] = v8::Exception::Error(Nan::New<v8::String>((executeBaton->error).c_str()).ToLocalChecked());
argv[1] = Nan::Undefined();
goto exitAsyncAfterExecute;
}
if( executeBaton->getRS )
{
Local<Object> resultSet = Nan::New<FunctionTemplate>(
@ -2542,6 +2564,13 @@ void Connection::Async_AfterExecute(uv_work_t *req)
}
else
{
rowArray = Connection::GetRows(executeBaton);
if(!(executeBaton->error).empty())
{
argv[0] = v8::Exception::Error(Nan::New<v8::String>((executeBaton->error).c_str()).ToLocalChecked());
argv[1] = Nan::Undefined();
goto exitAsyncAfterExecute;
}
Nan::Set(result, Nan::New<v8::String>("rows").ToLocalChecked(), rowArray);
Nan::Set(result, Nan::New<v8::String>("resultSet").ToLocalChecked(), Nan::Undefined());
}
@ -2595,6 +2624,7 @@ void Connection::Async_AfterExecute(uv_work_t *req)
}
exitAsyncAfterExecute:
Local<Function> callback = Nan::New<Function>(executeBaton->cb);
executeBaton->getRS = false; // To cleanup in case of parent SQL execution
delete executeBaton;
Nan::MakeCallback( Nan::GetCurrentContext()->Global(), callback, 2, argv );
if(tc.HasCaught())

View File

@ -362,8 +362,8 @@ private:
static void GetOutBindParams (unsigned short dataType, Bind* bind,
eBaton* executeBaton);
static void Descr2Double ( Define* defines, unsigned int numCols,
unsigned int rowsFetched, bool getRS );
static NJSErrorType Descr2Double ( Define* defines, unsigned int numCols,
unsigned int rowsFetched, bool getRS );
static void Descr2protoILob ( eBaton *executeBaton, unsigned int numCols,
unsigned int rowsFetched );
static v8::Local<v8::Value> GetOutBinds (eBaton* executeBaton);

View File

@ -36,6 +36,7 @@ using namespace std;
static const char *errMsg[] =
{
"NJS-000: success", // errSuccess
"NJS-001: expected callback as last parameter", // errMissingCallback
"NJS-002: invalid pool", // errInvalidPool
"NJS-003: invalid connection", // errInvalidConnection
@ -92,7 +93,7 @@ string NJSMessages::getErrorMsg ( NJSErrorType err, ... )
{
// print all specified arguments
va_start (vlist, err);
if ( vsnprintf (msg, MAX_ERROR_MSG_LEN, errMsg[err-1], vlist) <= 0)
if ( vsnprintf (msg, MAX_ERROR_MSG_LEN, errMsg[err], vlist) <= 0)
{
msg[0] = 0;
}

View File

@ -35,7 +35,8 @@ using namespace std;
typedef enum
{
errMissingCallback = 1,
errSuccess = 0,
errMissingCallback,
errInvalidPool,
errInvalidConnection,
errInvalidPropertyValue,

View File

@ -73,22 +73,14 @@ void ResultSet::setResultSet ( dpi::Stmt *stmt, eBaton *executeBaton )
this->dpistmt_ = stmt;
this->dpienv_ = executeBaton->dpienv;
this->njsconn_ = executeBaton->njsconn;
if ( stmt )
{
this->meta_ = stmt->getMetaData();
this->numCols_ = this->dpistmt_->numCols();
this->state_ = NJS_INACTIVE;
}
else
{
/*
* This could happen in REFCURSOR case, when the stored procedure
* did not return a valid handle
*/
this->numCols_ = 0;
this->meta_ = NULL;
this->state_ = NJS_INVALID;
}
this->numCols_ = 0; // numCols_ and meta_ are initialized as part
this->meta_ = NULL; // of the first call on RS
/*
* stmt can be NULL in REFCURSOR case, when the stored procedure
* did not return a valid stmt handle
*/
this->state_ = ( stmt ) ? NJS_INACTIVE : NJS_INVALID;
this->outFormat_ = executeBaton->outFormat;
this->fetchRowCount_ = 0;
@ -228,12 +220,30 @@ NAN_GETTER(ResultSet::GetMetaData)
info.GetReturnValue().SetUndefined();
return;
}
if ( !njsResultSet->meta_ )
{
try
{
njsResultSet->meta_ = njsResultSet->dpistmt_->getMetaData();
njsResultSet->numCols_ = njsResultSet->dpistmt_->numCols();
}
catch(dpi::Exception &e)
{
NJS_SET_CONN_ERR_STATUS ( e.errnum(), NULL );
NJS_SET_EXCEPTION(e.what(), (int) strlen(e.what()));
info.GetReturnValue().SetUndefined();
return;
}
}
std::string *columnNames = new std::string[njsResultSet->numCols_];
Connection::CopyMetaData ( columnNames, njsResultSet->meta_,
njsResultSet->numCols_ );
Local<Value> meta;
meta = Connection::GetMetaData( columnNames,
njsResultSet->numCols_ );
delete [] columnNames;
columnNames = NULL;
info.GetReturnValue().Set(meta);
}
@ -383,7 +393,6 @@ void ResultSet::GetRowsCommon(rsBaton *getRowsBaton)
ebaton = getRowsBaton->ebaton;
njsRS = getRowsBaton->njsRS;
ebaton->columnNames = new std::string[njsRS->numCols_];
ebaton->maxRows = getRowsBaton->numRows;
ebaton->dpistmt = njsRS->dpistmt_;
ebaton->getRS = true;
@ -458,15 +467,24 @@ void ResultSet::Async_GetRows(uv_work_t *req)
try
{
if ( !njsRS->meta_ )
{
njsRS->meta_ = njsRS->dpistmt_->getMetaData();
njsRS->numCols_ = njsRS->dpistmt_->numCols();
}
ebaton->columnNames = new std::string[njsRS->numCols_];
Connection::CopyMetaData ( ebaton->columnNames, njsRS->meta_,
njsRS->numCols_ );
ebaton->numCols = njsRS->numCols_;
// Allocate if not already done, or need more buffer
if( !njsRS->defineBuffers_ ||
njsRS->fetchRowCount_ < getRowsBaton->numRows )
{
if( njsRS->defineBuffers_ )
{
ResultSet::clearFetchBuffer(njsRS->defineBuffers_, njsRS->numCols_);
ResultSet::clearFetchBuffer(njsRS->defineBuffers_, njsRS->numCols_,
njsRS->fetchRowCount_);
getRowsBaton-> njsRS-> defineBuffers_ = NULL;
}
Connection::DoDefines(ebaton, njsRS->meta_, njsRS->numCols_);
@ -480,8 +498,11 @@ void ResultSet::Async_GetRows(uv_work_t *req)
}
else
{
// Buffers are reused except for LOB columns
for (unsigned int col = 0; col < njsRS->numCols_; col++)
{
// In case of LOB column, descriptor would have been wrapped by
// ProtoILob & njsIntLob and set the element to NULL, so reallocate
switch(njsRS->meta_[col].dbType)
{
case dpi::DpiClob:
@ -489,15 +510,26 @@ void ResultSet::Async_GetRows(uv_work_t *req)
case dpi::DpiBfile:
for (unsigned int j = 0; j < ebaton->maxRows; j++)
{
((Descriptor **)(njsRS->defineBuffers_[col].buf))[j] =
ebaton->dpienv->allocDescriptor(LobDescriptorType);
if ( !( ((Descriptor **)(njsRS->defineBuffers_[col].buf))[j] ) )
{
((Descriptor **)(njsRS->defineBuffers_[col].buf))[j] =
ebaton->dpienv->allocDescriptor(LobDescriptorType);
}
}
break;
default:
break;
}
}
}
ebaton->defines = njsRS->defineBuffers_;
Connection::DoFetch(ebaton);
if ( !ebaton->error.empty () )
{
getRowsBaton->error = ebaton->error;
goto exitAsyncGetRows;
}
if(ebaton->rowsFetched != getRowsBaton->numRows)
njsRS->rsEmpty_ = true;
@ -661,7 +693,8 @@ void ResultSet::Async_Close(uv_work_t *req)
unsigned int numCols = closeBaton-> njsRS-> numCols_;
if(defineBuffers)
{
ResultSet::clearFetchBuffer(defineBuffers, numCols);
ResultSet::clearFetchBuffer(defineBuffers, numCols,
closeBaton-> njsRS-> fetchRowCount_);
closeBaton-> njsRS-> defineBuffers_ = NULL;
}
@ -735,7 +768,8 @@ void ResultSet::Async_AfterClose(uv_work_t *req)
defineBuffers - Define bufferes from njsResultSet,
numCols - # of columns
*/
void ResultSet::clearFetchBuffer( Define* defineBuffers, unsigned int numCols)
void ResultSet::clearFetchBuffer( Define* defineBuffers, unsigned int numCols,
unsigned int numRows )
{
for( unsigned int i=0; i<numCols; i++ )
{
@ -744,6 +778,20 @@ void ResultSet::clearFetchBuffer( Define* defineBuffers, unsigned int numCols)
defineBuffers[i].dttmarr->release ();
defineBuffers[i].extbuf = NULL;
}
else if ( ( defineBuffers[i].fetchType == DpiClob ) ||
( defineBuffers[i].fetchType == DpiBlob ) ||
( defineBuffers[i].fetchType == DpiBfile ) )
{
for (unsigned int j = 0; j < numRows; j++)
{
if (((Descriptor **)(defineBuffers[i].buf))[j])
{
Env::freeDescriptor(((Descriptor **)(defineBuffers[i].buf))[j],
LobDescriptorType);
}
}
}
free(defineBuffers[i].buf);
free(defineBuffers[i].len);
free(defineBuffers[i].ind);

View File

@ -132,7 +132,7 @@ private:
static NAN_SETTER(SetMetaData);
static void clearFetchBuffer( Define* defineBuffers,
unsigned int numCols );
unsigned int numCols, unsigned int numRows );
dpi::Stmt *dpistmt_;