// ***************************************************************************
// Copyright (c) 2014 SAP AG or an SAP affiliate company. All rights reserved.
// ***************************************************************************
#include "utils.h"
using namespace v8;
using namespace node;
void getErrorMsg( int code, std::string &str )
{
std::ostringstream message;
message Get(i)->IsNumber() ) {
double *param_double = new double;
*param_double = bind_params->Get(i)->NumberValue(); // Remove Round off Error
num_params.push_back( param_double );
param.value.buffer = (char *)( param_double );
param.value.type = A_DOUBLE;
} else if( bind_params->Get(i)->IsString() ) {
String::Utf8Value paramValue( bind_params->Get(i)->ToString() );
const char* param_string = (*paramValue);
size_t *len = new size_t;
*len = (size_t)paramValue.length();
char *param_char = new char[*len + 1];
memcpy( param_char, param_string, ( *len ) + 1 );
string_params.push_back( param_char );
string_len.push_back( 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(i) ) ) {
size_t *len = new size_t;
*len = Buffer::Length( bind_params->Get(i) );
char *param_char = new char[*len];
memcpy( param_char, Buffer::Data( bind_params->Get(i) ), *len );
string_params.push_back( param_char );
string_len.push_back( 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(i)->IsNull() ) {
param.value.type = A_VAL32;
param.value.is_null = new sacapi_bool;
*param.value.is_null = true;
} else{
return false;
}
params.push_back( param );
}
return true;
}
bool getResultSet( Local &Result
, int &rows_affected
, std::vector &colNames
, std::vector &string_vals
, std::vector &num_vals
, std::vector &int_vals
, std::vector &string_len
, std::vector &col_types ) {
int num_rows = 0;
size_t num_cols = colNames.size();
if( rows_affected >= 0 ) {
Result = Integer::New( rows_affected );
return true;
}
if( num_cols > 0 ) {
size_t count = 0;
size_t count_int = 0, count_num = 0, count_string = 0;
Result = Array::New();
while( count_int < int_vals.size() ||
count_num < num_vals.size() ||
count_string < string_vals.size() ) {
Local ResultSet = Local::Cast( Result );
Local curr_row = Object::New();
num_rows++;
for( size_t i = 0; i < num_cols; i++ ) {
switch( col_types[count] ) {
case A_INVALID_TYPE:
curr_row->Set( String::NewSymbol( colNames[i] ), Null() );
break;
case A_VAL32:
case A_VAL16:
case A_UVAL16:
case A_VAL8:
case A_UVAL8:
if( int_vals[count_int] == NULL ) {
curr_row->Set( String::NewSymbol( colNames[i] ), Null() );
} else {
curr_row->Set( String::NewSymbol( colNames[i] ), Integer::New( *int_vals[count_int] ) );
}
delete int_vals[count_int];
int_vals[count_int] = NULL;
count_int++;
break;
case A_UVAL32:
case A_UVAL64:
case A_VAL64:
case A_DOUBLE:
if( num_vals[count_num] == NULL ) {
curr_row->Set( String::NewSymbol( colNames[i] ), Null() );
} else {
curr_row->Set( String::NewSymbol( colNames[i] ), Number::New( *num_vals[count_num] ) );
}
delete num_vals[count_num];
num_vals[count_num] = NULL;
count_num++;
break;
case A_BINARY:
if( string_vals[count_string] == NULL ) {
curr_row->Set( String::NewSymbol( colNames[i] ), Null() );
} else {
Buffer *bp = Buffer::New( *string_len[count_string] );
memcpy( Buffer::Data(bp), string_vals[count_string], *string_len[count_string] );
curr_row->Set( String::NewSymbol( colNames[i] ), bp->handle_ ) ;
}
delete string_vals[count_string];
delete string_len[count_string];
string_vals[count_string] = NULL;
string_len[count_string] = NULL;
count_string++;
break;
case A_STRING:
if( string_vals[count_string] == NULL ) {
curr_row->Set( String::NewSymbol( colNames[i] ), Null() );
} else {
curr_row->Set( String::NewSymbol( colNames[i] ), String::New( string_vals[count_string], *( (int*)string_len[count_string] ) ) );
}
delete string_vals[count_string];
delete string_len[count_string];
string_vals[count_string] = NULL;
string_len[count_string] = NULL;
count_string++;
break;
default:
return false;
break;
}
ResultSet->Set( num_rows - 1, curr_row );
count++;
}
}
} else {
Result = Local::New( Undefined() );
}
return true;
}
bool fetchResultSet( a_sqlany_stmt *sqlany_stmt
, int &rows_affected
, std::vector &colNames
, std::vector &string_vals
, std::vector &num_vals
, std::vector &int_vals
, std::vector &string_len
, 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 );
string_vals.push_back( val );
string_len.push_back( 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 );
string_vals.push_back( val );
string_len.push_back( size );
count_string++;
break;
}
case A_VAL64:
{
double *val = new double;
*val = (double)*(long long *)value.buffer;
num_vals.push_back( val );
count_num++;
break;
}
case A_UVAL64:
{
double *val = new double;
*val = (double)*(unsigned long long *)value.buffer;
num_vals.push_back( val );
count_num++;
break;
}
case A_VAL32:
{
int *val = new int;
*val = *(int*)value.buffer;
int_vals.push_back( val );
count_int++;
break;
}
case A_UVAL32:
{
double *val = new double;
*val = (double)*(unsigned int*)value.buffer;
num_vals.push_back( val );
count_num++;
break;
}
case A_VAL16:
{
int *val = new int;
*val = (int)*(short*)value.buffer;
int_vals.push_back( val );
count_int++;
break;
}
case A_UVAL16:
{
int *val = new int;
*val = (int)*(unsigned short*)value.buffer;
int_vals.push_back( val );
count_int++;
break;
}
case A_VAL8:
{
int *val = new int;
*val = (int)*(char *)value.buffer;
int_vals.push_back( val );
count_int++;
break;
}
case A_UVAL8:
{
int *val = new int;
*val = (int)*(unsigned char *)value.buffer;
int_vals.push_back( val );
count_int++;
break;
}
case A_DOUBLE:
{
double *val = new double;
*val = (double)*(double *)value.buffer;
num_vals.push_back( 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 )
{
noParamBaton *baton = static_cast(req->data);
if( baton->err ) {
callBack( &( baton->error_msg ), baton->callback, Local::New( Undefined() ), baton->callback_required );
return;
}
callBack( NULL, baton->callback, Local::New( Undefined() ), baton->callback_required );
delete baton;
delete req;
}
// Stmt Object Functions
StmtObject::StmtObject()
{
connection = NULL;
sqlany_stmt = NULL;
}
StmtObject::~StmtObject()
{
api.sqlany_free_stmt( sqlany_stmt );
}
Persistent StmtObject::constructor;
void StmtObject::Init()
{
// Prepare constructor template
Local tpl = FunctionTemplate::New( New );
tpl->SetClassName( String::NewSymbol( "StmtObject" ) );
tpl->InstanceTemplate()->SetInternalFieldCount( 1 );
// Prototype
tpl->PrototypeTemplate()->Set( String::NewSymbol( "exec" ),
FunctionTemplate::New( exec )->GetFunction() );
tpl->PrototypeTemplate()->Set( String::NewSymbol( "drop" ),
FunctionTemplate::New( drop )->GetFunction() );
constructor = Persistent::New( tpl->GetFunction() );
}
Handle StmtObject::New( const Arguments &args )
{
HandleScope scope;
StmtObject* obj = new StmtObject();
obj->Wrap( args.This() );
return args.This();
}
Handle StmtObject::NewInstance( const Arguments &args )
{
HandleScope scope;
const unsigned argc = 1;
Handle argv[argc] = { args[0] };
Local instance = constructor->NewInstance( argc, argv );
return scope.Close( instance );
}
// Connection Functions
Handle CreateConnection( const Arguments &args )
{
HandleScope scope;
return scope.Close( Connection::NewInstance( args ) );
}
Persistent HashToString( Local obj )
/**************************************************/
{
Local props = obj->GetOwnPropertyNames();
int length = props->Length();
std::string params = "";
bool first = true;;
for( int i = 0; i < length; i++ ) {
Local key = props->Get(i).As();
Local val = obj->Get(key).As();
String::Utf8Value key_utf8( key );
String::Utf8Value val_utf8( val );
if( !first ) {
params += ";";
}
first = false;
params += std::string(*key_utf8);
params += "=";
params += std::string(*val_utf8);
}
return Persistent::New( String::New((const char *)params.c_str()) );
}
#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 Arguments &args )
/*********************************************/
{
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() ) {
Local str = args[0]->ToString();
int string_len = str->Utf8Length();
char *buf = new char[string_len+1];
str->WriteUtf8( buf );
_arg = Persistent::New( String::New(buf, string_len) );
delete [] buf;
} else if( args[0]->IsObject() ) {
_arg = HashToString( args[0]->ToObject() );
} else if( !args[0]->IsUndefined() && !args[0]->IsNull() ) {
throwError( JS_ERR_INVALID_ARGUMENTS );
} else {
_arg = Persistent::New( String::New("") );
}
} else {
_arg = Persistent::New( String::New("") );
}
}
Connection::~Connection()
/***********************/
{
scoped_lock api_lock( api_mutex );
scoped_lock lock( conn_mutex );
_arg.Dispose();
if( conn != NULL ) {
api.sqlany_disconnect( conn );
api.sqlany_free_connection( conn );
conn = NULL;
openConnections--;
}
cleanAPI();
};
Persistent Connection::constructor;
void Connection::Init()
/*********************/
{
// Prepare constructor template
Local tpl = FunctionTemplate::New( New );
tpl->SetClassName( String::NewSymbol( "Connection" ) );
tpl->InstanceTemplate()->SetInternalFieldCount( 1 );
// Prototype
tpl->PrototypeTemplate()->Set( String::NewSymbol( "exec" ),
FunctionTemplate::New( exec )->GetFunction() );
tpl->PrototypeTemplate()->Set( String::NewSymbol( "prepare" ),
FunctionTemplate::New( prepare )->GetFunction() );
tpl->PrototypeTemplate()->Set( String::NewSymbol( "connect" ),
FunctionTemplate::New( connect )->GetFunction() );
tpl->PrototypeTemplate()->Set( String::NewSymbol( "disconnect" ),
FunctionTemplate::New( disconnect )->GetFunction() );
tpl->PrototypeTemplate()->Set( String::NewSymbol( "commit" ),
FunctionTemplate::New( commit )->GetFunction() );
tpl->PrototypeTemplate()->Set( String::NewSymbol( "rollback" ),
FunctionTemplate::New( rollback )->GetFunction() );
constructor = Persistent::New( tpl->GetFunction() );
}
Handle Connection::New( const Arguments &args )
{
HandleScope scope;
Connection *obj = new Connection( args );
obj->Wrap( args.This() );
return args.This();
}
Handle Connection::NewInstance( const Arguments &args )
{
HandleScope scope;
const unsigned argc = 1;
Handle argv[argc] = { args[0] };
Local instance = constructor->NewInstance( argc, argv );
return scope.Close( instance );
}