Handle<Value> ODBCResult::CloseSync(const Arguments& args) { DEBUG_PRINTF("ODBCResult::CloseSync\n"); HandleScope scope; OPT_INT_ARG(0, closeOption, SQL_DESTROY); ODBCResult* result = ObjectWrap::Unwrap<ODBCResult>(args.Holder()); DEBUG_PRINTF("ODBCResult::CloseSync closeOption=%i m_canFreeHandle=%i\n", closeOption, result->m_canFreeHandle); if (closeOption == SQL_DESTROY && result->m_canFreeHandle) { result->Free(); } else if (closeOption == SQL_DESTROY && !result->m_canFreeHandle) { //We technically can't free the handle so, we'll SQL_CLOSE uv_mutex_lock(&ODBC::g_odbcMutex); SQLFreeStmt(result->m_hSTMT, SQL_CLOSE); uv_mutex_unlock(&ODBC::g_odbcMutex); } else { uv_mutex_lock(&ODBC::g_odbcMutex); SQLFreeStmt(result->m_hSTMT, closeOption); uv_mutex_unlock(&ODBC::g_odbcMutex); } return scope.Close(True()); }
Handle<Value> ODBCResult::Fetch(const Arguments& args) { DEBUG_PRINTF("ODBCResult::Fetch\n"); HandleScope scope; ODBCResult* objODBCResult = ObjectWrap::Unwrap<ODBCResult>(args.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); fetch_work_data* data = (fetch_work_data *) calloc(1, sizeof(fetch_work_data)); Local<Function> cb; //set the fetch mode to the default of this instance data->fetchMode = objODBCResult->m_fetchMode; if (args.Length() == 1 && args[0]->IsFunction()) { cb = Local<Function>::Cast(args[0]); } else if (args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction()) { cb = Local<Function>::Cast(args[1]); Local<Object> obj = args[0]->ToObject(); if (obj->Has(OPTION_FETCH_MODE) && obj->Get(OPTION_FETCH_MODE)->IsInt32()) { data->fetchMode = obj->Get(OPTION_FETCH_MODE)->ToInt32()->Value(); } } else { return ThrowException(Exception::TypeError( String::New("ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function.") )); } data->cb = Persistent<Function>::New(cb); data->objResult = objODBCResult; work_req->data = data; uv_queue_work( uv_default_loop(), work_req, UV_Fetch, (uv_after_work_cb)UV_AfterFetch); objODBCResult->Ref(); return scope.Close(Undefined()); }
Handle<Value> ODBCResult::New(const Arguments& args) { DEBUG_PRINTF("ODBCResult::New\n"); HandleScope scope; REQ_EXT_ARG(0, js_henv); REQ_EXT_ARG(1, js_hdbc); REQ_EXT_ARG(2, js_hstmt); REQ_EXT_ARG(3, js_canFreeHandle); HENV hENV = static_cast<HENV>(js_henv->Value()); HDBC hDBC = static_cast<HDBC>(js_hdbc->Value()); HSTMT hSTMT = static_cast<HSTMT>(js_hstmt->Value()); bool* canFreeHandle = static_cast<bool *>(js_canFreeHandle->Value()); //create a new OBCResult object ODBCResult* objODBCResult = new ODBCResult(hENV, hDBC, hSTMT, *canFreeHandle); DEBUG_PRINTF("ODBCResult::New m_hDBC=%X m_hDBC=%X m_hSTMT=%X canFreeHandle=%X\n", objODBCResult->m_hENV, objODBCResult->m_hDBC, objODBCResult->m_hSTMT, objODBCResult->m_canFreeHandle ); //free the pointer to canFreeHandle delete canFreeHandle; //specify the buffer length objODBCResult->bufferLength = MAX_VALUE_SIZE - 1; //initialze a buffer for this object objODBCResult->buffer = (uint16_t *) malloc(objODBCResult->bufferLength + 1); //TODO: make sure the malloc succeeded //set the initial colCount to 0 objODBCResult->colCount = 0; //default fetchMode to FETCH_OBJECT objODBCResult->m_fetchMode = FETCH_OBJECT; objODBCResult->Wrap(args.Holder()); return scope.Close(args.Holder()); }
void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll\n"); Nan::HandleScope scope; fetch_work_data* data = (fetch_work_data *)(work_req->data); ODBCResult* self = data->objResult->self(); bool doMoreWork = true; if (self->colCount == 0) { self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); } //check to see if the result set has columns if (self->colCount == 0) { //this most likely means that the query was something like //'insert into ....' doMoreWork = false; } //check to see if there was an error else if (data->result == SQL_ERROR) { data->errorCount++; data->objError.Reset(ODBC::GetSQLError( SQL_HANDLE_STMT, self->m_hSTMT, (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll" )); doMoreWork = false; } //check to see if we are at the end of the recordset else if (data->result == SQL_NO_DATA) { doMoreWork = false; } else { //TODO: !important: persistent forces us to set this to a local handle, but do we need to recopy it back to persistent handle? Local<Array> rows = Nan::New(data->rows); if (data->fetchMode == FETCH_ARRAY) { rows->Set( Nan::New(data->count), ODBC::GetRecordArray( self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength) ); } else { rows->Set( Nan::New(data->count), ODBC::GetRecordTuple( self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength) ); } data->count++; } if (doMoreWork) { //Go back to the thread pool and fetch more data! uv_queue_work( uv_default_loop(), work_req, UV_FetchAll, (uv_after_work_cb)UV_AfterFetchAll); } else { ODBC::FreeColumns(self->columns, &self->colCount); Local<Value> info[2]; if (data->errorCount > 0) { info[0] = Nan::New(data->objError); } else { info[0] = Nan::Null(); } info[1] = Nan::New(data->rows); Nan::TryCatch try_catch; data->cb->Call(2, info); delete data->cb; data->rows.Reset(); data->objError.Reset(); if (try_catch.HasCaught()) { FatalException(try_catch); } //TODO: Do we need to free self->rows somehow? free(data); free(work_req); self->Unref(); } }
void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll - Entry\n"); Nan::HandleScope scope; fetch_work_data* data = (fetch_work_data *)(work_req->data); ODBCResult* self = data->objResult->self(); bool doMoreWork = true; /* Check : to see if there was an ERROR on SQLFetch() call. * So before GetColums call we should store the error. * Reason : GetColumns internally calls SQLGetDiagField method, * and SQLGetDiagField() method retrieves only the diagnostic information of * most recent CLI function call, any diagnostic information from a previous call * with the same handle will be lost. - issue253 */ if (data->result == SQL_ERROR) { data->errorCount++; data->objError.Reset(ODBC::GetSQLError( SQL_HANDLE_STMT, self->m_hSTMT, (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll" )); } if (self->colCount == 0) { self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll, colcount = %d, columns = %d, stmt = %X\n", self->colCount, self->columns, data->objResult->m_hSTMT); } /* Check : to see if the result set has columns. * Queries like insert into... (which has no actual fetch data), * will also return error after SQLFetch call, which is expected here * (as we are calling SQLFetch for every SQL query) but not true, * hence we should ignore these error. */ bool noDataFetchQuery = false; if (self->colCount == 0) { noDataFetchQuery = true; doMoreWork = false; } //check to see if we are at the end of the recordset else if (data->result == SQL_NO_DATA) { doMoreWork = false; } //check to see if there was an error else if (data->result == SQL_ERROR) { doMoreWork = false; } else { Local<Array> rows = Nan::New(data->rows); if (data->fetchMode == FETCH_ARRAY) { rows->Set( Nan::New(data->count), ODBC::GetRecordArray( self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength) ); } else { rows->Set( Nan::New(data->count), ODBC::GetRecordTuple( self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength) ); } data->count++; } if (doMoreWork) { //Go back to the thread pool and fetch more data! uv_queue_work( uv_default_loop(), work_req, UV_FetchAll, (uv_after_work_cb)UV_AfterFetchAll); } else { DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll Done for stmt %X\n", data->objResult->m_hSTMT); Local<Value> info[3]; if (data->errorCount > 0) { if(noDataFetchQuery) { info[0] = Nan::Null(); noDataFetchQuery = false; } else { info[0] = Nan::New(data->objError); } } else { info[0] = Nan::Null(); } info[1] = Nan::New(data->rows); info[2] = Nan::New(self->colCount); Nan::TryCatch try_catch; data->cb->Call(3, info); ODBC::FreeColumns(self->columns, &self->colCount); delete data->cb; data->rows.Reset(); data->objError.Reset(); if (try_catch.HasCaught()) { FatalException(try_catch); } //TODO: Do we need to free self->rows somehow? free(data); free(work_req); self->Unref(); } DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll - Exit\n"); }
void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll\n"); HandleScope scope; fetch_work_data* data = (fetch_work_data *)(work_req->data); ODBCResult* self = data->objResult->self(); bool doMoreWork = true; if (self->colCount == 0) { self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); } //check to see if the result set has columns if (self->colCount == 0) { //this most likely means that the query was something like //'insert into ....' doMoreWork = false; } //check to see if there was an error else if (data->result == SQL_ERROR) { data->errorCount++; data->objError = Persistent<Object>::New(ODBC::GetSQLError( SQL_HANDLE_STMT, self->m_hSTMT, (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll" )); doMoreWork = false; } //check to see if we are at the end of the recordset else if (data->result == SQL_NO_DATA) { doMoreWork = false; } else { if (data->fetchMode == FETCH_ARRAY) { data->rows->Set( Integer::New(data->count), ODBC::GetRecordArray( self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength) ); } else { data->rows->Set( Integer::New(data->count), ODBC::GetRecordTuple( self->m_hSTMT, self->columns, &self->colCount, self->buffer, self->bufferLength) ); } data->count++; } if (doMoreWork) { //Go back to the thread pool and fetch more data! uv_queue_work( uv_default_loop(), work_req, UV_FetchAll, (uv_after_work_cb)UV_AfterFetchAll); } else { ODBC::FreeColumns(self->columns, &self->colCount); Handle<Value> args[2]; if (data->errorCount > 0) { args[0] = Local<Object>::New(data->objError); } else { args[0] = Null(); } args[1] = Local<Array>::New(data->rows); TryCatch try_catch; data->cb->Call(Context::GetCurrent()->Global(), 2, args); data->cb.Dispose(); data->rows.Dispose(); data->objError.Dispose(); if (try_catch.HasCaught()) { FatalException(try_catch); } //TODO: Do we need to free self->rows somehow? free(data); free(work_req); self->Unref(); } scope.Close(Undefined()); }