// *************************************************************************** // Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved. // *************************************************************************** #include "nodever_cover.h" #include "sqlany_utils.h" #include "nan.h" using namespace v8; using namespace node; int getError( a_sqlany_connection *conn, char *str, size_t len ) /**************************************************************/ { int sqlcode; sqlcode = api.sqlany_error( conn, str, len ); return sqlcode; } void getErrorMsg( int code, std::string &str ) /********************************************/ { std::ostringstream message; message << "Code: "; message << code; message << " Msg: "; switch (code) { case JS_ERR_INVALID_OBJECT: message << "Invalid Object"; break; case JS_ERR_INVALID_ARGUMENTS: message << "Invalid Arguments"; break; case JS_ERR_CONNECTION_ALREADY_EXISTS: message << "Already Connected"; break; case JS_ERR_INITIALIZING_DBCAPI: message << "Can't initialize DBCAPI"; break; case JS_ERR_NOT_CONNECTED: message << "No Connection Available"; break; case JS_ERR_BINDING_PARAMETERS: message << "Can not bind parameter(s)"; break; case JS_ERR_GENERAL_ERROR: message << "An error occurred"; break; case JS_ERR_RESULTSET: message << "Error making result set Object"; break; case JS_ERR_NO_WIDE_STATEMENTS: message << "The DBCAPI library must be upgraded to support wide statements"; break; default: message << "Unknown Error"; } str = message.str(); } void getErrorMsg(a_sqlany_connection *conn, std::string &str) /*************************************************************/ { char buffer[SACAPI_ERROR_SIZE]; int sqlcode; sqlcode = getError( conn, buffer, sizeof(buffer) ); std::ostringstream message; message << "Code: "; message << sqlcode; message << " Msg: "; message << buffer; str = message.str(); } void throwError(a_sqlany_connection *conn) /******************************************/ { Isolate *isolate = Isolate::GetCurrent(); std::string message; getErrorMsg(conn, message); isolate->ThrowException( Exception::Error(String::NewFromUtf8(isolate, message.c_str()).ToLocalChecked())); } void throwError(int code) /*************************/ { Isolate *isolate = Isolate::GetCurrent(); std::string message; getErrorMsg(code, message); isolate->ThrowException( Exception::Error(String::NewFromUtf8(isolate, message.c_str()).ToLocalChecked())); } void callBack(std::string *str, Persistent &callback, Local &Result, bool callback_required) /*********************************************************/ { Isolate *isolate = Isolate::GetCurrent(); HandleScope scope(isolate); Local local_callback = Local::New(isolate, callback); // If string is NULL, then there is no error if (callback_required) { if (!local_callback->IsFunction()) { throwError(JS_ERR_INVALID_ARGUMENTS); return; } Local Err; if (str == NULL) { Err = Local::New(isolate, Undefined(isolate)); } else { Err = Exception::Error(String::NewFromUtf8(isolate, str->c_str()).ToLocalChecked()); } int argc = 2; Local argv[2] = {Err, Result}; TryCatch try_catch(isolate); Nan::Callback *cb = new Nan::Callback(local_callback); Nan::Call(*cb, argc, argv); if (try_catch.HasCaught()) { node::FatalException(isolate, try_catch); } } else { if (str != NULL) { isolate->ThrowException( Exception::Error(String::NewFromUtf8(isolate, str->c_str()).ToLocalChecked())); } } } void callBack(std::string *str, Persistent &callback, Persistent &Result, bool callback_required) /*********************************************************/ { Isolate *isolate = Isolate::GetCurrent(); HandleScope scope(isolate); Local local_result = Local::New(isolate, Result); callBack(str, callback, local_result, callback_required); } void callBack(std::string *str, const Local &arg, Local &Result, bool callback_required) /*********************************************************/ { Isolate *isolate = Isolate::GetCurrent(); HandleScope scope(isolate); // If string is NULL, then there is no error if (callback_required) { if (!arg->IsFunction()) { throwError(JS_ERR_INVALID_ARGUMENTS); return; } Local callback = Local::Cast(arg); Local Err; if (str == NULL) { Err = Local::New(isolate, Undefined(isolate)); } else { Err = Exception::Error(String::NewFromUtf8(isolate, str->c_str()).ToLocalChecked()); } int argc = 2; Local argv[2] = {Err, Result}; TryCatch try_catch(isolate); Nan::Callback *cb = new Nan::Callback(callback); Nan::Call(*cb, argc, argv); if (try_catch.HasCaught()) { node::FatalException(isolate, try_catch); } } else { if (str != NULL) { isolate->ThrowException( Exception::Error(String::NewFromUtf8(isolate, str->c_str()).ToLocalChecked())); } } } static bool getWideBindParameters(std::vector &execData, Isolate *isolate, Local arg, std::vector ¶ms, unsigned &num_rows) /*********************************************************************************/ { Local context = isolate->GetCurrentContext(); Local rows = Local::Cast(arg); num_rows = rows->Length(); Local row0 = Local::Cast(rows->Get(context, 0).ToLocalChecked()); unsigned num_cols = row0->Length(); unsigned c; if (num_cols == 0) { // if an empty array was passed in, we still need ExecuteData ExecuteData *ex = new ExecuteData; execData.push_back(ex); return true; } // Make sure that each array in the list has the same number and types // of values for (unsigned int r = 1; r < num_rows; r++) { Local row = Local::Cast(rows->Get(context, r).ToLocalChecked()); for (c = 0; c < num_cols; c++) { Local val0 = row0->Get(context, c).ToLocalChecked(); Local val = row->Get(context, c).ToLocalChecked(); if ((val0->IsInt32() || val0->IsNumber()) && (!val->IsInt32() && !val->IsNumber() && !val->IsNull())) { return false; } if (val0->IsString() && !val->IsString() && !val->IsNull()) { return false; } if (Buffer::HasInstance(val0) && !Buffer::HasInstance(val) && !val->IsNull()) { return false; } } } for (c = 0; c < num_cols; c++) { a_sqlany_bind_param param; memset(¶m, 0, sizeof(param)); ExecuteData *ex = new ExecuteData; execData.push_back(ex); double *param_double = new double[num_rows]; ex->addNum(param_double); char **char_arr = new char *[num_rows]; size_t *len = new size_t[num_rows]; ex->addStrings(char_arr, len); sacapi_bool *is_null = new sacapi_bool[num_rows]; ex->addNull(is_null); param.value.is_null = is_null; param.value.is_address = false; if (row0->Get(context, c).ToLocalChecked()->IsInt32() || row0->Get(context, c).ToLocalChecked()->IsNumber()) { param.value.type = A_DOUBLE; param.value.buffer = (char *)(param_double); } else if (row0->Get(context, c).ToLocalChecked()->IsString()) { param.value.type = A_STRING; param.value.buffer = (char *)char_arr; param.value.length = len; param.value.is_address = true; } else if (Buffer::HasInstance(row0->Get(context, c).ToLocalChecked())) { param.value.type = A_BINARY; param.value.buffer = (char *)char_arr; param.value.length = len; param.value.is_address = true; } else if (row0->Get(context, c).ToLocalChecked()->IsNull()) { } else { return false; } for (unsigned int r = 0; r < num_rows; r++) { Local bind_params = Local::Cast(rows->Get(context, r).ToLocalChecked()); is_null[r] = false; if (bind_params->Get(context, c).ToLocalChecked()->IsInt32() || bind_params->Get(context, c).ToLocalChecked()->IsNumber()) { param_double[r] = bind_params->Get(context, c).ToLocalChecked()->NumberValue(context).FromJust(); } else if (bind_params->Get(context, c).ToLocalChecked()->IsString()) { String::Utf8Value paramValue(isolate, (bind_params->Get(context, c).ToLocalChecked()->ToString(context)).ToLocalChecked()); const char *param_string = (*paramValue); len[r] = (size_t)paramValue.length(); char *param_char = new char[len[r] + 1]; char_arr[r] = param_char; memcpy(param_char, param_string, len[r] + 1); } else if (Buffer::HasInstance(bind_params->Get(context, c).ToLocalChecked())) { len[r] = Buffer::Length(bind_params->Get(context, c).ToLocalChecked()); char *param_char = new char[len[r]]; char_arr[r] = param_char; memcpy(param_char, Buffer::Data(bind_params->Get(context, c).ToLocalChecked()), len[r]); } else if (bind_params->Get(context, c).ToLocalChecked()->IsNull()) { is_null[r] = true; } } params.push_back(param); } return true; } bool getBindParameters(std::vector &execData, Isolate *isolate, Local arg, std::vector ¶ms, unsigned &num_rows) /*************************************************************************/ { Local context = isolate->GetCurrentContext(); Local bind_params = Local::Cast(arg); if (bind_params->Length() == 0) { // if an empty array was passed in, we still need ExecuteData ExecuteData *ex = new ExecuteData; execData.push_back(ex); return true; } if (bind_params->Get(context, 0).ToLocalChecked()->IsArray()) { return getWideBindParameters(execData, isolate, arg, params, num_rows); } num_rows = 1; ExecuteData *ex = new ExecuteData; execData.push_back(ex); for (unsigned int i = 0; i < bind_params->Length(); i++) { a_sqlany_bind_param param; memset(¶m, 0, sizeof(param)); if (bind_params->Get(context, i).ToLocalChecked()->IsInt32()) { int *param_int = new int; *param_int = bind_params->Get(context, i).ToLocalChecked()->Int32Value(context).FromJust(); ex->addInt(param_int); param.value.buffer = (char *)(param_int); param.value.type = A_VAL32; } else if (bind_params->Get(context, i).ToLocalChecked()->IsNumber()) { double *param_double = new double; *param_double = bind_params->Get(context, i).ToLocalChecked()->NumberValue(context).FromJust(); // Remove Round off Error ex->addNum(param_double); param.value.buffer = (char *)(param_double); param.value.type = A_DOUBLE; } else if (bind_params->Get(context, i).ToLocalChecked()->IsString()) { String::Utf8Value paramValue(isolate, (bind_params->Get(context, i).ToLocalChecked()->ToString(context)).ToLocalChecked()); const char *param_string = (*paramValue); size_t *len = new size_t; *len = (size_t)paramValue.length(); char **char_arr = new char *; char *param_char = new char[*len + 1]; *char_arr = param_char; memcpy(param_char, param_string, (*len) + 1); ex->addStrings(char_arr, len); param.value.type = A_STRING; param.value.buffer = param_char; param.value.length = len; param.value.buffer_size = *len + 1; } else if (Buffer::HasInstance(bind_params->Get(context, i).ToLocalChecked())) { size_t *len = new size_t; *len = Buffer::Length(bind_params->Get(context, i).ToLocalChecked()); char **char_arr = new char *; char *param_char = new char[*len]; *char_arr = param_char; memcpy(param_char, Buffer::Data(bind_params->Get(context, i).ToLocalChecked()), *len); ex->addStrings(char_arr, len); param.value.type = A_BINARY; param.value.buffer = param_char; param.value.length = len; param.value.buffer_size = sizeof(param_char); } else if (bind_params->Get(context, i).ToLocalChecked()->IsNull()) { param.value.type = A_STRING; sacapi_bool *is_null = new sacapi_bool; param.value.is_null = is_null; ex->addNull(is_null); is_null[0] = true; } else { return false; } params.push_back(param); } return true; } bool getResultSet(Persistent &Result, int &rows_affected, std::vector &colNames, ExecuteData *execData, std::vector &col_types) /*****************************************************************/ { Isolate *isolate = Isolate::GetCurrent(); Local context = isolate->GetCurrentContext(); HandleScope scope(isolate); int num_rows = 0; size_t num_cols = colNames.size(); if (rows_affected >= 0) { Result.Reset(isolate, Integer::New(isolate, rows_affected)); return true; } if (num_cols > 0) { size_t count = 0; size_t count_int = 0, count_num = 0, count_string = 0; Local ResultSet = Array::New(isolate); while (count_int < execData->intSize() || count_num < execData->numSize() || count_string < execData->stringSize()) { Local curr_row = Object::New(isolate); num_rows++; for (size_t i = 0; i < num_cols; i++) { switch (col_types[count]) { case A_INVALID_TYPE: curr_row->Set(context, String::NewFromUtf8(isolate, colNames[i]).ToLocalChecked(), Null(isolate)); break; case A_VAL32: case A_VAL16: case A_UVAL16: case A_VAL8: case A_UVAL8: if (execData->intIsNull(count_int)) { curr_row->Set(context, String::NewFromUtf8(isolate, colNames[i]).ToLocalChecked(), Null(isolate)); } else { curr_row->Set(context, String::NewFromUtf8(isolate, colNames[i]).ToLocalChecked(), Integer::New(isolate, execData->getInt(count_int))); } count_int++; break; case A_UVAL32: case A_UVAL64: case A_VAL64: case A_DOUBLE: if (execData->numIsNull(count_num)) { curr_row->Set(context, String::NewFromUtf8(isolate, colNames[i]).ToLocalChecked(), Null(isolate)); } else { curr_row->Set(context, String::NewFromUtf8(isolate, colNames[i]).ToLocalChecked(), Number::New(isolate, execData->getNum(count_num))); } count_num++; break; case A_BINARY: if (execData->stringIsNull(count_string)) { curr_row->Set(context, String::NewFromUtf8(isolate, colNames[i]).ToLocalChecked(), Null(isolate)); } else { MaybeLocal mbuf = node::Buffer::Copy( isolate, execData->getString(count_string), execData->getLen(count_string)); Local buf = mbuf.ToLocalChecked(); curr_row->Set(context, String::NewFromUtf8(isolate, colNames[i]).ToLocalChecked(), buf); } count_string++; break; case A_STRING: if (execData->stringIsNull(count_string)) { curr_row->Set(context, String::NewFromUtf8(isolate, colNames[i]).ToLocalChecked(), Null(isolate)); } else { curr_row->Set(context, String::NewFromUtf8(isolate, colNames[i]).ToLocalChecked(), String::NewFromUtf8(isolate, execData->getString(count_string), NewStringType::kNormal, (int)execData->getLen(count_string)) .ToLocalChecked()); } count_string++; break; default: return false; } count++; } ResultSet->Set(context, num_rows - 1, curr_row); } Result.Reset(isolate, ResultSet); } else { Result.Reset(isolate, Local::New(isolate, Undefined(isolate))); } return true; } bool fetchResultSet(a_sqlany_stmt *sqlany_stmt, int &rows_affected, std::vector &colNames, ExecuteData *execData, std::vector &col_types) /*****************************************************************/ { a_sqlany_data_value value; int num_cols = 0; rows_affected = api.sqlany_affected_rows(sqlany_stmt); num_cols = api.sqlany_num_cols(sqlany_stmt); if (rows_affected > 0 && num_cols < 1) { return true; } rows_affected = -1; if (num_cols > 0) { for (int i = 0; i < num_cols; i++) { a_sqlany_column_info info; api.sqlany_get_column_info(sqlany_stmt, i, &info); size_t size = strlen(info.name) + 1; char *name = new char[size]; memcpy(name, info.name, size); colNames.push_back(name); } int count_string = 0, count_num = 0, count_int = 0; while (api.sqlany_fetch_next(sqlany_stmt)) { for (int i = 0; i < num_cols; i++) { if (!api.sqlany_get_column(sqlany_stmt, i, &value)) { return false; break; } if (*(value.is_null)) { col_types.push_back(A_INVALID_TYPE); continue; } switch (value.type) { case A_BINARY: { size_t *size = new size_t; *size = *(value.length); char *val = new char[*size]; memcpy(val, value.buffer, *size); execData->addString(val, size); count_string++; break; } case A_STRING: { size_t *size = new size_t; *size = (size_t)((int)*(value.length)); char *val = new char[*size]; memcpy(val, (char *)value.buffer, *size); execData->addString(val, size); count_string++; break; } case A_VAL64: { double *val = new double; *val = (double)*(long long *)value.buffer; execData->addNum(val); count_num++; break; } case A_UVAL64: { double *val = new double; *val = (double)*(unsigned long long *)value.buffer; execData->addNum(val); count_num++; break; } case A_VAL32: { int *val = new int; *val = *(int *)value.buffer; execData->addInt(val); count_int++; break; } case A_UVAL32: { double *val = new double; *val = (double)*(unsigned int *)value.buffer; execData->addNum(val); count_num++; break; } case A_VAL16: { int *val = new int; *val = (int)*(short *)value.buffer; execData->addInt(val); count_int++; break; } case A_UVAL16: { int *val = new int; *val = (int)*(unsigned short *)value.buffer; execData->addInt(val); count_int++; break; } case A_VAL8: { int *val = new int; *val = (int)*(char *)value.buffer; execData->addInt(val); count_int++; break; } case A_UVAL8: { int *val = new int; *val = (int)*(unsigned char *)value.buffer; execData->addInt(val); count_int++; break; } case A_DOUBLE: { double *val = new double; *val = (double)*(double *)value.buffer; execData->addNum(val); count_num++; break; } default: return false; } col_types.push_back(value.type); } } } return true; } bool cleanAPI() /*************/ { if (openConnections == 0) { if (api.initialized) { api.sqlany_fini(); sqlany_finalize_interface(&api); return true; } } return false; } // Generic Baton and Callback (After) Function // Use this if the function does not have any return values and // Does not take any parameters. // Create custom Baton and Callback (After) functions otherwise void Connection::noParamAfter(uv_work_t *req, int status) /*********************************************/ { Isolate *isolate = Isolate::GetCurrent(); HandleScope scope(isolate); noParamBaton *baton = static_cast(req->data); Local undef = Local::New(isolate, Undefined(isolate)); if (baton->err) { callBack(&(baton->error_msg), baton->callback, undef, baton->callback_required); return; } callBack(NULL, baton->callback, undef, baton->callback_required); delete baton; delete req; } // Stmt Object Functions StmtObject::StmtObject() /**********************/ { connection = NULL; sqlany_stmt = NULL; } StmtObject::~StmtObject() /***********************/ { uv_mutex_t *mutex = NULL; if (connection != NULL) { mutex = &connection->conn_mutex; uv_mutex_lock(mutex); } cleanup(); removeConnection(); if (mutex != NULL) { uv_mutex_unlock(mutex); } } Persistent StmtObject::constructor; void StmtObject::Init(Isolate *isolate) /***************************************/ { HandleScope scope(isolate); // Prepare constructor template Local tpl = FunctionTemplate::New(isolate, New); tpl->SetClassName(String::NewFromUtf8(isolate, "StmtObject").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); // Prototype NODE_SET_PROTOTYPE_METHOD(tpl, "exec", exec); NODE_SET_PROTOTYPE_METHOD(tpl, "drop", drop); NODE_SET_PROTOTYPE_METHOD(tpl, "getMoreResults", getMoreResults); Local context = isolate->GetCurrentContext(); constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked()); } void StmtObject::New(const FunctionCallbackInfo &args) /*************************************************************/ { StmtObject *obj = new StmtObject(); obj->Wrap(args.This()); args.GetReturnValue().Set(args.This()); } void StmtObject::NewInstance(const FunctionCallbackInfo &args) /*********************************************************************/ { Isolate *isolate = Isolate::GetCurrent(); Persistent obj; CreateNewInstance(args, obj); args.GetReturnValue().Set(obj.Get(isolate)); } void StmtObject::CreateNewInstance(const FunctionCallbackInfo &args, Persistent &obj) /***************************************************************************/ { Isolate *isolate = args.GetIsolate(); HandleScope scope(isolate); const unsigned argc = 1; Local argv[argc] = {args[0]}; Local cons = Local::New(isolate, constructor); Local env = isolate->GetCurrentContext(); MaybeLocal mlObj = cons->NewInstance(env, argc, argv); Local instance = mlObj.ToLocalChecked(); obj.Reset(isolate, instance); } void StmtObject::cleanup(void) /******************************/ { if (sqlany_stmt != NULL) { api.sqlany_free_stmt(sqlany_stmt); sqlany_stmt = NULL; } } void StmtObject::removeConnection(void) /***************************************/ { if (connection != NULL) { connection->removeStmt(this); connection = NULL; } } // Connection Functions void HashToString(Isolate *isolate, Local obj, Persistent &ret) /*************************************************************/ { Local context = isolate->GetCurrentContext(); HandleScope scope(isolate); Local props = (obj->GetOwnPropertyNames(context)).ToLocalChecked(); int length = props->Length(); std::string params = ""; bool first = true; for (int i = 0; i < length; i++) { Local key = props->Get(context, i).ToLocalChecked().As(); Local val = obj->Get(context, key).ToLocalChecked().As(); String::Utf8Value key_utf8(isolate, key); String::Utf8Value val_utf8(isolate, val); if (!first) { params += ";"; } first = false; params += std::string(*key_utf8); params += "="; params += std::string(*val_utf8); } ret.Reset(isolate, String::NewFromUtf8(isolate, params.c_str()).ToLocalChecked()); } #if 0 // Handy function for determining what type an object is. static void CheckArgType( Local &obj ) /*******************************************/ { static const char *type = NULL; if( obj->IsArray() ) { type = "Array"; } else if( obj->IsBoolean() ) { type = "Boolean"; } else if( obj->IsBooleanObject() ) { type = "BooleanObject"; } else if( obj->IsDate() ) { type = "Date"; } else if( obj->IsExternal() ) { type = "External"; } else if( obj->IsFunction() ) { type = "Function"; } else if( obj->IsInt32() ) { type = "Int32"; } else if( obj->IsNativeError() ) { type = "NativeError"; } else if( obj->IsNull() ) { type = "Null"; } else if( obj->IsNumber() ) { type = "Number"; } else if( obj->IsNumberObject() ) { type = "NumberObject"; } else if( obj->IsObject() ) { type = "Object"; } else if( obj->IsRegExp() ) { type = "RegExp"; } else if( obj->IsString() ) { type = "String"; } else if( obj->IsStringObject() ) { type = "StringObject"; } else if( obj->IsUint32() ) { type = "Uint32"; } else if( obj->IsUndefined() ) { type = "Undefined"; } else { type = "Unknown"; } } #endif Connection::Connection(const FunctionCallbackInfo &args) /***************************************************************/ { Isolate *isolate = args.GetIsolate(); Local context = isolate->GetCurrentContext(); HandleScope scope(isolate); uv_mutex_init(&conn_mutex); conn = NULL; if (args.Length() > 1) { throwError(JS_ERR_INVALID_ARGUMENTS); return; } if (args.Length() == 1) { //CheckArgType( args[0] ); if (args[0]->IsString()) { MaybeLocal str = args[0]->ToString(context); int string_len = str.ToLocalChecked()->Utf8Length(isolate); char *buf = new char[string_len + 1]; str.ToLocalChecked()->WriteUtf8(isolate, buf); _arg.Reset(isolate, String::NewFromUtf8(isolate, buf).ToLocalChecked()); delete[] buf; } else if (args[0]->IsObject()) { HashToString(isolate, args[0]->ToObject(context).ToLocalChecked(), _arg); } else if (!args[0]->IsUndefined() && !args[0]->IsNull()) { throwError(JS_ERR_INVALID_ARGUMENTS); } else { _arg.Reset(isolate, String::NewFromUtf8(isolate, "").ToLocalChecked()); } } else { _arg.Reset(isolate, String::NewFromUtf8(isolate, "").ToLocalChecked()); } } Connection::~Connection() /***********************/ { scoped_lock api_lock(api_mutex); scoped_lock lock(conn_mutex); _arg.Reset(); cleanupStmts(); if (conn != NULL) { api.sqlany_disconnect(conn); api.sqlany_free_connection(conn); conn = NULL; openConnections--; } cleanAPI(); }; void Connection::cleanupStmts(void) /***********************************/ { std::vector::iterator findit; for (findit = statements.begin(); findit != statements.end(); findit++) { StmtObject *s = reinterpret_cast(*findit); s->cleanup(); } } void Connection::removeStmt(class StmtObject *stmt) /***************************************************/ { // caller must get mutex std::vector::iterator findit; for (findit = statements.begin(); findit != statements.end(); findit++) { if (*findit == reinterpret_cast(stmt)) { statements.erase(findit); return; } } } Persistent Connection::constructor; void Connection::Init(Isolate *isolate) /***************************************/ { HandleScope scope(isolate); // Prepare constructor template Local tpl = FunctionTemplate::New(isolate, New); tpl->SetClassName(String::NewFromUtf8(isolate, "Connection").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); // Prototype NODE_SET_PROTOTYPE_METHOD(tpl, "exec", exec); NODE_SET_PROTOTYPE_METHOD(tpl, "prepare", prepare); NODE_SET_PROTOTYPE_METHOD(tpl, "connect", connect); NODE_SET_PROTOTYPE_METHOD(tpl, "disconnect", disconnect); NODE_SET_PROTOTYPE_METHOD(tpl, "close", disconnect); NODE_SET_PROTOTYPE_METHOD(tpl, "commit", commit); NODE_SET_PROTOTYPE_METHOD(tpl, "rollback", rollback); NODE_SET_PROTOTYPE_METHOD(tpl, "connected", connected); Local context = isolate->GetCurrentContext(); constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked()); } void Connection::New(const FunctionCallbackInfo &args) /*************************************************************/ { Isolate *isolate = args.GetIsolate(); HandleScope scope(isolate); if (args.IsConstructCall()) { Connection *obj = new Connection(args); obj->Wrap(args.This()); args.GetReturnValue().Set(args.This()); } else { const int argc = 1; Local argv[argc] = {args[0]}; Local cons = Local::New(isolate, constructor); Local env = isolate->GetCurrentContext(); MaybeLocal mlObj = cons->NewInstance(env, argc, argv); const Local obj = mlObj.ToLocalChecked(); args.GetReturnValue().Set(obj); } } void Connection::NewInstance(const FunctionCallbackInfo &args) /*********************************************************************/ { Isolate *isolate = args.GetIsolate(); HandleScope scope(isolate); const unsigned argc = 1; Local argv[argc] = {args[0]}; Local cons = Local::New(isolate, constructor); Local env = isolate->GetCurrentContext(); MaybeLocal mlObj = cons->NewInstance(env, argc, argv); Local instance = mlObj.ToLocalChecked(); args.GetReturnValue().Set(instance); }