From 0f853bd3d171e154eb813a5a6d79fea17a016976 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 11 Apr 2012 16:38:12 -0400 Subject: [PATCH 001/511] replace eio_custom with uv_queue_work, rename EIO_functionName to UV_functionName --- src/Database.cpp | 136 +++++++++++++++++++++++------------------------ src/Database.h | 25 ++++++--- 2 files changed, 83 insertions(+), 78 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 757d3697..20c22d5e 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "Database.h" @@ -27,12 +28,6 @@ using namespace v8; using namespace node; -typedef struct { - unsigned char *name; - unsigned int len; - SQLLEN type; -} Column; - pthread_mutex_t Database::m_odbcMutex; void Database::Init(v8::Handle target) { @@ -63,14 +58,14 @@ Handle Database::New(const Arguments& args) { return args.This(); } -int Database::EIO_AfterOpen(eio_req *req) { +void Database::UV_AfterOpen(uv_work_t* req) { ev_unref(EV_DEFAULT_UC); HandleScope scope; - struct open_request *open_req = (struct open_request *)(req->data); + open_request* open_req = (open_request *)(req->data); Local argv[1]; bool err = false; - if (req->result) { + if (open_req->result) { err = true; argv[0] = Exception::Error(String::New("Error opening database")); } @@ -88,13 +83,14 @@ int Database::EIO_AfterOpen(eio_req *req) { free(open_req); scope.Close(Undefined()); - return 0; } -void Database::EIO_Open(eio_req *req) { - struct open_request *open_req = (struct open_request *)(req->data); - Database *self = open_req->dbo->self(); +void Database::UV_Open(uv_work_t* req) { + open_request* open_req = (open_request *)(req->data); + Database* self = open_req->dbo->self(); + pthread_mutex_lock(&Database::m_odbcMutex); + int ret = SQLAllocEnv( &self->m_hEnv ); if( ret == SQL_SUCCESS ) { ret = SQLAllocConnect( self->m_hEnv,&self->m_hDBC ); @@ -120,7 +116,7 @@ void Database::EIO_Open(eio_req *req) { } } pthread_mutex_unlock(&Database::m_odbcMutex); - req->result = ret; + open_req->result = ret; } Handle Database::Open(const Arguments& args) { @@ -130,9 +126,8 @@ Handle Database::Open(const Arguments& args) { REQ_FUN_ARG(1, cb); Database* dbo = ObjectWrap::Unwrap(args.This()); - - struct open_request *open_req = (struct open_request *) - calloc(1, sizeof(struct open_request) + connection.length()); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + open_request* open_req = (open_request *) calloc(1, sizeof(open_request) + connection.length()); if (!open_req) { V8::LowMemoryNotification(); @@ -142,8 +137,10 @@ Handle Database::Open(const Arguments& args) { strcpy(open_req->connection, *connection); open_req->cb = Persistent::New(cb); open_req->dbo = dbo; - - eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, open_req); + + work_req->data = open_req; + + uv_queue_work(uv_default_loop(), work_req, UV_Open, UV_AfterOpen); ev_ref(EV_DEFAULT_UC); dbo->Ref(); @@ -151,16 +148,16 @@ Handle Database::Open(const Arguments& args) { return Undefined(); } -int Database::EIO_AfterClose(eio_req *req) { +void Database::UV_AfterClose(uv_work_t* req) { ev_unref(EV_DEFAULT_UC); HandleScope scope; - struct close_request *close_req = (struct close_request *)(req->data); + close_request* close_req = (close_request *)(req->data); Local argv[1]; bool err = false; - if (req->result) { + if (close_req->result) { err = true; argv[0] = Exception::Error(String::New("Error closing database")); } @@ -178,16 +175,18 @@ int Database::EIO_AfterClose(eio_req *req) { free(close_req); scope.Close(Undefined()); - return 0; } -void Database::EIO_Close(eio_req *req) { - struct close_request *close_req = (struct close_request *)(req->data); +void Database::UV_Close(uv_work_t* req) { + close_request* close_req = (close_request *)(req->data); Database* dbo = close_req->dbo; + pthread_mutex_lock(&Database::m_odbcMutex); + SQLDisconnect(dbo->m_hDBC); SQLFreeHandle(SQL_HANDLE_ENV, dbo->m_hEnv); SQLFreeHandle(SQL_HANDLE_DBC, dbo->m_hDBC); + pthread_mutex_unlock(&Database::m_odbcMutex); } @@ -197,9 +196,8 @@ Handle Database::Close(const Arguments& args) { REQ_FUN_ARG(0, cb); Database* dbo = ObjectWrap::Unwrap(args.This()); - - struct close_request *close_req = (struct close_request *) - calloc(1, sizeof(struct close_request)); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + close_request* close_req = (close_request *) (calloc(1, sizeof(close_request))); if (!close_req) { V8::LowMemoryNotification(); @@ -209,7 +207,9 @@ Handle Database::Close(const Arguments& args) { close_req->cb = Persistent::New(cb); close_req->dbo = dbo; - eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, close_req); + work_req->data = close_req; + + uv_queue_work(uv_default_loop(), work_req, UV_Close, UV_AfterClose); ev_ref(EV_DEFAULT_UC); dbo->Ref(); @@ -217,15 +217,15 @@ Handle Database::Close(const Arguments& args) { return Undefined(); } -int Database::EIO_AfterQuery(eio_req *req) { +void Database::UV_AfterQuery(uv_work_t* req) { ev_unref(EV_DEFAULT_UC); - struct query_request *prep_req = (struct query_request *)(req->data); + query_request* prep_req = (query_request *)(req->data); struct tm timeInfo = { 0 }; //used for processing date/time datatypes HandleScope scope; - Database *self = prep_req->dbo->self(); //an easy reference to the Database object + Database* self = prep_req->dbo->self(); //an easy reference to the Database object Local objError = Object::New(); //our error object which we will use if we discover errors while processing the result set short colCount = 0; //used to keep track of the number of columns received in a result set @@ -235,7 +235,7 @@ int Database::EIO_AfterQuery(eio_req *req) { SQLSMALLINT buflen; //used as a place holder for the length of column names SQLRETURN ret; //used to capture the return value from various SQL function calls - char *buf = (char *) malloc(MAX_VALUE_SIZE); //allocate a buffer for incoming column values + char* buf = (char *) malloc(MAX_VALUE_SIZE); //allocate a buffer for incoming column values //check to make sure malloc succeeded if (buf == NULL) { @@ -262,8 +262,8 @@ int Database::EIO_AfterQuery(eio_req *req) { memset(buf,0,MAX_VALUE_SIZE); //set all of the bytes of the buffer to 0; I tried doing this inside the loop, but it increased processing time dramatically - //First thing, let's check if the execution of the query returned any errors (in EIO_Query) - if(req->result == SQL_ERROR) + //First thing, let's check if the execution of the query returned any errors (in UV_Query) + if(prep_req->result == SQL_ERROR) { errorCount++; @@ -329,10 +329,10 @@ int Database::EIO_AfterQuery(eio_req *req) { char errorSQLState[128]; SQLError(self->m_hEnv, self->m_hDBC, self->m_hStmt,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); - //printf("EIO_Query ret => %i\n", ret); - printf("EIO_Query => %s\n", errorMessage); - printf("EIO_Query => %s\n", errorSQLState); - //printf("EIO_Query sql => %s\n", prep_req->sql); + //printf("UV_Query ret => %i\n", ret); + printf("UV_Query => %s\n", errorMessage); + printf("UV_Query => %s\n", errorSQLState); + //printf("UV_Query sql => %s\n", prep_req->sql); } if (ret == SQL_ERROR) { @@ -483,11 +483,11 @@ int Database::EIO_AfterQuery(eio_req *req) { free(prep_req->type); free(prep_req); scope.Close(Undefined()); - return 0; } -void Database::EIO_Query(eio_req *req) { - struct query_request *prep_req = (struct query_request *)(req->data); +void Database::UV_Query(uv_work_t* req) { + query_request* prep_req = (query_request *)(req->data); + Parameter prm; SQLRETURN ret; @@ -540,8 +540,7 @@ void Database::EIO_Query(eio_req *req) { free(prep_req->params); } - req->result = ret; // this will be checked later in EIO_AfterQuery - + prep_req->result = ret; // this will be checked later in UV_AfterQuery } Handle Database::Query(const Arguments& args) { @@ -555,9 +554,8 @@ Handle Database::Query(const Arguments& args) { Parameter* params; Database* dbo = ObjectWrap::Unwrap(args.This()); - - struct query_request *prep_req = (struct query_request *) - calloc(1, sizeof(struct query_request)); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); if (!prep_req) { V8::LowMemoryNotification(); @@ -593,7 +591,7 @@ Handle Database::Query(const Arguments& args) { Local value = values->Get(i); params[i].size = 0; - params[i].length = NULL; + params[i].length = SQL_NULL_DATA; params[i].buffer_length = 0; if (value->IsString()) @@ -663,8 +661,9 @@ Handle Database::Query(const Arguments& args) { strcpy(prep_req->sql, *sql); prep_req->dbo = dbo; - - eio_custom(EIO_Query, EIO_PRI_DEFAULT, EIO_AfterQuery, prep_req); + work_req->data = prep_req; + + uv_queue_work(uv_default_loop(), work_req, UV_Query, UV_AfterQuery); ev_ref(EV_DEFAULT_UC); dbo->Ref(); @@ -672,8 +671,8 @@ Handle Database::Query(const Arguments& args) { return Undefined(); } -void Database::EIO_Tables(eio_req *req) { - struct query_request *prep_req = (struct query_request *)(req->data); +void Database::UV_Tables(uv_work_t* req) { + query_request* prep_req = (query_request *)(req->data); if(prep_req->dbo->m_hStmt) { @@ -689,9 +688,7 @@ void Database::EIO_Tables(eio_req *req) { (SQLCHAR *) prep_req->type, SQL_NTS ); - req->result = ret; // this will be checked later in EIO_AfterQuery - - + prep_req->result = ret; // this will be checked later in UV_AfterQuery } Handle Database::Tables(const Arguments& args) { @@ -704,9 +701,8 @@ Handle Database::Tables(const Arguments& args) { Local cb = Local::Cast(args[4]); Database* dbo = ObjectWrap::Unwrap(args.This()); - - struct query_request *prep_req = (struct query_request *) - calloc(1, sizeof(struct query_request)); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); if (!prep_req) { V8::LowMemoryNotification(); @@ -742,8 +738,9 @@ Handle Database::Tables(const Arguments& args) { } prep_req->dbo = dbo; - - eio_custom(EIO_Tables, EIO_PRI_DEFAULT, EIO_AfterQuery, prep_req); + work_req->data = prep_req; + + uv_queue_work(uv_default_loop(), work_req, UV_Tables, UV_AfterQuery); ev_ref(EV_DEFAULT_UC); dbo->Ref(); @@ -751,8 +748,8 @@ Handle Database::Tables(const Arguments& args) { return Undefined(); } -void Database::EIO_Columns(eio_req *req) { - struct query_request *prep_req = (struct query_request *)(req->data); +void Database::UV_Columns(uv_work_t* req) { + query_request* prep_req = (query_request *)(req->data); if(prep_req->dbo->m_hStmt) { @@ -768,8 +765,7 @@ void Database::EIO_Columns(eio_req *req) { (SQLCHAR *) prep_req->column, SQL_NTS ); - req->result = ret; // this will be checked later in EIO_AfterQuery - + prep_req->result = ret; // this will be checked later in UV_AfterQuery } Handle Database::Columns(const Arguments& args) { @@ -782,9 +778,8 @@ Handle Database::Columns(const Arguments& args) { Local cb = Local::Cast(args[4]); Database* dbo = ObjectWrap::Unwrap(args.This()); - - struct query_request *prep_req = (struct query_request *) - calloc(1, sizeof(struct query_request)); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); if (!prep_req) { V8::LowMemoryNotification(); @@ -820,9 +815,10 @@ Handle Database::Columns(const Arguments& args) { } prep_req->dbo = dbo; - - eio_custom(EIO_Columns, EIO_PRI_DEFAULT, EIO_AfterQuery, prep_req); - + work_req->data = prep_req; + + uv_queue_work(uv_default_loop(), work_req, UV_Columns, UV_AfterQuery); + ev_ref(EV_DEFAULT_UC); dbo->Ref(); scope.Close(Undefined()); diff --git a/src/Database.h b/src/Database.h index 66a5976d..bc583299 100644 --- a/src/Database.h +++ b/src/Database.h @@ -44,22 +44,22 @@ class Database : public node::ObjectWrap { static Handle New(const Arguments& args); - static int EIO_AfterOpen(eio_req *req); - static void EIO_Open(eio_req *req); + static void UV_AfterOpen(uv_work_t* req); + static void UV_Open(uv_work_t* req); static Handle Open(const Arguments& args); - static int EIO_AfterClose(eio_req *req); - static void EIO_Close(eio_req *req); + static void UV_AfterClose(uv_work_t* req); + static void UV_Close(uv_work_t* req); static Handle Close(const Arguments& args); - static int EIO_AfterQuery(eio_req *req); - static void EIO_Query(eio_req *req); + static void UV_AfterQuery(uv_work_t* req); + static void UV_Query(uv_work_t* req); static Handle Query(const Arguments& args); - static void EIO_Tables(eio_req *req); + static void UV_Tables(uv_work_t* req); static Handle Tables(const Arguments& args); - static void EIO_Columns(eio_req *req); + static void UV_Columns(uv_work_t* req); static Handle Columns(const Arguments& args); Database *self(void) { return this; } @@ -83,13 +83,21 @@ struct open_request { Persistent cb; Database *dbo; char connection[1]; + int result; }; struct close_request { Persistent cb; Database *dbo; + int result; }; +typedef struct { + unsigned char *name; + unsigned int len; + SQLLEN type; +} Column; + typedef struct { SQLSMALLINT c_type; SQLSMALLINT type; @@ -111,6 +119,7 @@ struct query_request { char *column; Parameter *params; int paramCount; + int result; }; #define REQ_ARGS(N) \ From eb27a8fd8bc5d832650c7efacbe1c61615843149 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 12 Apr 2012 12:16:23 -0400 Subject: [PATCH 002/511] replace pthread_mutex_* with uv_mutex_* --- src/Database.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 20c22d5e..25f59d50 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -28,7 +28,7 @@ using namespace v8; using namespace node; -pthread_mutex_t Database::m_odbcMutex; +uv_mutex_t Database::m_odbcMutex; void Database::Init(v8::Handle target) { HandleScope scope; @@ -47,7 +47,7 @@ void Database::Init(v8::Handle target) { target->Set(v8::String::NewSymbol("Database"), constructor_template->GetFunction()); scope.Close(Undefined()); - pthread_mutex_init(&Database::m_odbcMutex, NULL); + uv_mutex_init(&Database::m_odbcMutex); } Handle Database::New(const Arguments& args) { @@ -89,7 +89,7 @@ void Database::UV_Open(uv_work_t* req) { open_request* open_req = (open_request *)(req->data); Database* self = open_req->dbo->self(); - pthread_mutex_lock(&Database::m_odbcMutex); + uv_mutex_lock(&Database::m_odbcMutex); int ret = SQLAllocEnv( &self->m_hEnv ); if( ret == SQL_SUCCESS ) { @@ -115,7 +115,7 @@ void Database::UV_Open(uv_work_t* req) { } } } - pthread_mutex_unlock(&Database::m_odbcMutex); + uv_mutex_unlock(&Database::m_odbcMutex); open_req->result = ret; } @@ -181,13 +181,13 @@ void Database::UV_Close(uv_work_t* req) { close_request* close_req = (close_request *)(req->data); Database* dbo = close_req->dbo; - pthread_mutex_lock(&Database::m_odbcMutex); + uv_mutex_lock(&Database::m_odbcMutex); SQLDisconnect(dbo->m_hDBC); SQLFreeHandle(SQL_HANDLE_ENV, dbo->m_hEnv); SQLFreeHandle(SQL_HANDLE_DBC, dbo->m_hDBC); - pthread_mutex_unlock(&Database::m_odbcMutex); + uv_mutex_unlock(&Database::m_odbcMutex); } Handle Database::Close(const Arguments& args) { From 61260e7826c4ef07cc74953d3dcc044e3490bd73 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 12 Apr 2012 12:21:33 -0400 Subject: [PATCH 003/511] remove pthread.h include --- src/Database.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Database.h b/src/Database.h index bc583299..82a97550 100644 --- a/src/Database.h +++ b/src/Database.h @@ -25,8 +25,6 @@ #include #include -#include - using namespace v8; using namespace node; From 0309ce08a31702751a34d40095abeadac3e2fcde Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 13 Apr 2012 18:04:32 -0400 Subject: [PATCH 004/511] set version for this branch --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 04ffba6c..3f57a8c6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.3.1", + "version": "0.4.1pre", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { "type": "git", From 497555851d91cb11603a7e4891787a8384fac0bf Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 13 Apr 2012 18:17:04 -0400 Subject: [PATCH 005/511] Add ability to pass object instead of connection string Generate a connection string using an object's key/value pairs if an object is passed to odbc.open(). --- odbc.js | 9 +++++++++ test/common.js | 4 ++++ test/test-connection-object.js | 16 ++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 test/test-connection-object.js diff --git a/odbc.js b/odbc.js index 494ab0a3..b9671b37 100644 --- a/odbc.js +++ b/odbc.js @@ -94,6 +94,15 @@ Database.prototype.open = function(connectionString, callback) { return callback( { message : "Connection already open." }, [], false); } + if (typeof(connectionString) == "object") { + var obj = connectionString; + connectionString = ""; + + Object.keys(obj).forEach(function (key) { + connectionString += key + "=" + obj[key] + ";"; + }); + } + self.dispatchOpen(connectionString, function (err) { self.connected = true; self.processQueue(); diff --git a/test/common.js b/test/common.js index 14d94230..f757a207 100644 --- a/test/common.js +++ b/test/common.js @@ -1 +1,5 @@ module.exports.connectionString = "DRIVER={SQLite};DATABASE=data/sqlite-test.db"; +module.exports.connectionObject = { + DRIVER : "{SQLITE}", + DATABASE : "data/sqlite-test.db" +}; diff --git a/test/test-connection-object.js b/test/test-connection-object.js new file mode 100644 index 00000000..5cf70d9f --- /dev/null +++ b/test/test-connection-object.js @@ -0,0 +1,16 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database(); + +db.open(common.connectionObject, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + console.error("Open success"); + + db.close(function () { + console.error("Close Success"); + }); +}); From c50317ea889e3cf0a6125b12f5d07964174b6919 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 13 Apr 2012 19:11:51 -0400 Subject: [PATCH 006/511] added a couple quick benchmarks --- test/bench-insert.js | 62 ++++++++++++++++++++++++++++++++++++++++++++ test/bench-query.js | 40 ++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 test/bench-insert.js create mode 100644 test/bench-query.js diff --git a/test/bench-insert.js b/test/bench-insert.js new file mode 100644 index 00000000..ac85ed0f --- /dev/null +++ b/test/bench-insert.js @@ -0,0 +1,62 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database(); + +db.open(common.connectionObject, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + createTable(); +}); + +function createTable() { + db.query("create table bench_insert (str varchar(50))", function (err) { + if (err) { + console.error(err); + return finish(); + } + + return insertData(); + }); +} + +function dropTable() { + db.query("drop table bench_insert", function (err) { + if (err) { + console.error(err); + return finish(); + } + + return finish(); + }); +} + +function insertData() { + var count = 0 + , iterations = 1000 + , time = new Date().getTime(); + + for (var x = 0; x < iterations; x++) { + db.query("insert into bench_insert (str) values ('testing')", function (err) { + if (err) { + console.error(err);639.38619 + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d records inserted in %d seconds, %d/sec", iterations, elapsed/1000, iterations/(elapsed/1000)); + return dropTable(); + } + }); + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} \ No newline at end of file diff --git a/test/bench-query.js b/test/bench-query.js new file mode 100644 index 00000000..0a4ff2fc --- /dev/null +++ b/test/bench-query.js @@ -0,0 +1,40 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database(); + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , iterations = 10000 + , time = new Date().getTime(); + + for (var x = 0; x < iterations; x++) { + db.query("select 1 + 1 as test", function (err, data) { + if (err) { + console.error(err); + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec", iterations, elapsed/1000, iterations/(elapsed/1000)); + return finish(); + } + }); + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} \ No newline at end of file From 1e927d77ed4515becf0499a9576e7ff63121f655 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 18 Apr 2012 15:57:00 -0400 Subject: [PATCH 007/511] update bench-query to use a named function as the callback --- test/bench-query.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/bench-query.js b/test/bench-query.js index 0a4ff2fc..9198c787 100644 --- a/test/bench-query.js +++ b/test/bench-query.js @@ -17,19 +17,21 @@ function issueQuery() { , time = new Date().getTime(); for (var x = 0; x < iterations; x++) { - db.query("select 1 + 1 as test", function (err, data) { - if (err) { - console.error(err); - return finish(); - } + db.query("select 1 + 1 as test", cb); + } + + function cb (err, data) { + if (err) { + console.error(err); + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; - if (++count == iterations) { - var elapsed = new Date().getTime() - time; - - console.log("%d queries issued in %d seconds, %d/sec", iterations, elapsed/1000, iterations/(elapsed/1000)); - return finish(); - } - }); + console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, count/(elapsed/1000)); + return finish(); + } } } From 1566f777d3addb81118a791db54e827165dca595 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 18 Apr 2012 15:57:37 -0400 Subject: [PATCH 008/511] test: being preparing for running tests against multiple drivers --- test/common.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/common.js b/test/common.js index f757a207..92abb998 100644 --- a/test/common.js +++ b/test/common.js @@ -1,5 +1,13 @@ module.exports.connectionString = "DRIVER={SQLite};DATABASE=data/sqlite-test.db"; + module.exports.connectionObject = { DRIVER : "{SQLITE}", DATABASE : "data/sqlite-test.db" }; + +module.exports.connections = [ + { + DRIVER : "{SQLITE}", + DATABASE : "data/sqlite-test.db" + } +]; \ No newline at end of file From 33a00f19ddabe1e93098bee91566a2585f7ca4b2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 18 Apr 2012 16:00:54 -0400 Subject: [PATCH 009/511] add super tight odbc benchmark written in C --- test/odbc-bench.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 test/odbc-bench.c diff --git a/test/odbc-bench.c b/test/odbc-bench.c new file mode 100644 index 00000000..120163c5 --- /dev/null +++ b/test/odbc-bench.c @@ -0,0 +1,123 @@ +/* + Copyright (c) 2012, Dan VerWeire + Copyright (c) 2011, Lee Smith + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FIELD_SIZE 1024 +#define MAX_VALUE_SIZE 1048576 + + + +int main() { + HENV m_hEnv; + HDBC m_hDBC; + HSTMT m_hStmt; + SQLRETURN ret; + SQLUSMALLINT canHaveMoreResults; + //SQLCHAR outstr[1024]; + //SQLSMALLINT outstrlen; + + if( SQL_SUCCEEDED(SQLAllocEnv( &m_hEnv )) ) { + + if( SQL_SUCCEEDED(SQLAllocHandle( SQL_HANDLE_DBC, m_hEnv, &m_hDBC )) ) { + SQLSetConnectOption( m_hDBC, SQL_LOGIN_TIMEOUT,5 ); + + ret = SQLDriverConnect( + m_hDBC, + NULL, + "DRIVER={MySQL};SERVER=mysql-02;USER=test;PASSWORD=;DATABASE=test;", + SQL_NTS, + NULL,//outstr, + 0,//sizeof(outstr), + NULL,//&outstrlen, + SQL_DRIVER_NOPROMPT + ); + + if( SQL_SUCCEEDED(ret) ) { + int iterations = 10000; + int i = 0; + struct timeb start; + + ftime(&start); + + for (i =0 ; i <= iterations; i ++) { + SQLAllocHandle(SQL_HANDLE_STMT, m_hDBC, &m_hStmt); + + SQLExecDirect(m_hStmt, "select 1 + 1 as test;", SQL_NTS); + + while ( SQL_SUCCEEDED(SQLFetch(m_hStmt) )) { + //printf("sql query succeeded\n"); + } + + SQLFreeHandle(SQL_HANDLE_STMT, m_hStmt); + } + + struct timeb stop; + ftime(&stop); + + double elapsed = ((stop.time * 1000 + stop.millitm) - (start.time * 1000 + start.millitm)); + + printf("%d queries issued in %f seconds, %f/sec\n", iterations, (double) elapsed / 1000, iterations/((double) elapsed / 1000)); + } + else { + printf("here3\n"); + printError("SQLDriverConnect", m_hDBC, SQL_HANDLE_DBC); + } + } + else { + printError("SQLAllocHandle - dbc", m_hEnv, SQL_HANDLE_ENV); + } + } + else { + printError("SQLAllocHandle - env", m_hEnv, SQL_HANDLE_ENV); + } + + //SQLFreeHandle(SQL_HANDLE_DBC, m_hDBC); + //SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv); + + return 0; +} + +void printError(const char *fn, SQLHANDLE handle, SQLSMALLINT type) +{ + SQLINTEGER i = 0; + SQLINTEGER native; + SQLCHAR state[ 7 ]; + SQLCHAR text[256]; + SQLSMALLINT len; + SQLRETURN ret; + + printf( + "\n" + "The driver reported the following diagnostics whilst running " + "%s\n\n", + fn + ); + + do { + ret = SQLGetDiagRec(type, handle, ++i, state, &native, text, sizeof(text), &len ); + if (SQL_SUCCEEDED(ret)) + printf("%s:%ld:%ld:%s\n", state, (long int) i, (long int) native, text); + } + while( ret == SQL_SUCCESS ); +} From 0d8e8060e4d66fffc2685fe4c20553e93b4ac349 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 18 Apr 2012 16:13:46 -0400 Subject: [PATCH 010/511] remove cxxflags from wscript as suggested by @Sannis --- wscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wscript b/wscript index 78ead27f..a399d646 100644 --- a/wscript +++ b/wscript @@ -10,7 +10,7 @@ def configure(conf): def build(bld): obj = bld.new_task_gen("cxx", "shlib", "node_addon") - obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall"] obj.target = "odbc_bindings" obj.source = "src/Database.cpp" - obj.uselib = "ODBC" \ No newline at end of file + obj.uselib = "ODBC" + From 82185cdceb27e522c083a1d5b5933c060b060426 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 19 Apr 2012 11:06:18 -0400 Subject: [PATCH 011/511] test: add date type test --- test/test-date.js | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 test/test-date.js diff --git a/test/test-date.js b/test/test-date.js new file mode 100644 index 00000000..7955f38a --- /dev/null +++ b/test/test-date.js @@ -0,0 +1,83 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database(); + +db.open(common.connectionString, function(err) +{ + if (err) { + console.error(err); + process.exit(1); + } + + createTable(); +}); + +function createTable() { + db.query("CREATE TABLE IF NOT EXISTS date_test ( dt1 datetime )", function (err) { + if (err) { + console.error(err); + return cleanup(1); + } + + insertData(); + }); +} + +function dropTable(exitCode) { + db.query("DROP TABLE IF EXISTS date_test", function (err) { + if (err) { + console.error(err); + return cleanup(1); + } + + cleanup(exitCode || 0); + }); +} + + +function insertData() { + db.query("INSERT INTO date_test (dt1) values ('2012-04-17')", function (err) { + if (err) { + console.error(err); + return dropTable(1); + } + + selectData(); + }); +} + +function selectData() { + db.query("SELECT * FROM date_test", function (err, data) { + if (err) { + console.error(err); + return cleanup(1); + } + + //test selected data + if (data[0].dt1 instanceof Date) { + dropTable(0); + } + else { + console.error("dt1 is instance of: %s", data[0].dt1.constructor.name); + dropTable(1); + } + }); +} + +function cleanup(exitCode) { + db.close(function (err) { + if (exitCode == 0) { + console.error("success"); + } + else { + console.error("failure"); + } + + if (err) { + process.error(err); + process.exit(1); + } + + process.exit(exitCode); + }); +} From 79430a55a6e4c1a7ad3896f642c63843295a18d6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 19 Apr 2012 11:08:44 -0400 Subject: [PATCH 012/511] test: use SQLite3 instead of SQLite2 --- test/common.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/common.js b/test/common.js index 92abb998..b73757cf 100644 --- a/test/common.js +++ b/test/common.js @@ -1,13 +1,13 @@ -module.exports.connectionString = "DRIVER={SQLite};DATABASE=data/sqlite-test.db"; +module.exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; module.exports.connectionObject = { - DRIVER : "{SQLITE}", + DRIVER : "{SQLITE3}", DATABASE : "data/sqlite-test.db" }; module.exports.connections = [ { - DRIVER : "{SQLITE}", + DRIVER : "{SQLITE3}", DATABASE : "data/sqlite-test.db" } -]; \ No newline at end of file +]; From f7fdb3aa552e49c9c42f097b5dc02beef6778fb7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 19 Apr 2012 11:09:41 -0400 Subject: [PATCH 013/511] test: remove database file, the driver should create it automatically --- test/data/sqlite-test.db | Bin 3072 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/data/sqlite-test.db diff --git a/test/data/sqlite-test.db b/test/data/sqlite-test.db deleted file mode 100644 index 8136db4d2dc459d4eaf373f06a032acb74bef8c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3072 zcmdPWQV7Y&ELKR%%t=*9&d)1J%*-oRNX%0R4)n<^NmVe?GgL@PEJ;jCEKXI>(qhmk zeSC|Vfq{V$i1#ruFt7mWQ7{?;qaiSqLx6+f2QX7Z^0Gfe12B8D@d%1Z_yhBOPAWr5 zYH Date: Thu, 19 Apr 2012 11:11:20 -0400 Subject: [PATCH 014/511] remove references to ev_ref. libuv handles this now. --- src/Database.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 20c22d5e..01d1cad3 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -59,7 +59,6 @@ Handle Database::New(const Arguments& args) { } void Database::UV_AfterOpen(uv_work_t* req) { - ev_unref(EV_DEFAULT_UC); HandleScope scope; open_request* open_req = (open_request *)(req->data); @@ -142,15 +141,12 @@ Handle Database::Open(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Open, UV_AfterOpen); - ev_ref(EV_DEFAULT_UC); dbo->Ref(); scope.Close(Undefined()); return Undefined(); } void Database::UV_AfterClose(uv_work_t* req) { - ev_unref(EV_DEFAULT_UC); - HandleScope scope; close_request* close_req = (close_request *)(req->data); @@ -211,15 +207,12 @@ Handle Database::Close(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Close, UV_AfterClose); - ev_ref(EV_DEFAULT_UC); dbo->Ref(); scope.Close(Undefined()); return Undefined(); } void Database::UV_AfterQuery(uv_work_t* req) { - ev_unref(EV_DEFAULT_UC); - query_request* prep_req = (query_request *)(req->data); struct tm timeInfo = { 0 }; //used for processing date/time datatypes @@ -665,7 +658,6 @@ Handle Database::Query(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Query, UV_AfterQuery); - ev_ref(EV_DEFAULT_UC); dbo->Ref(); scope.Close(Undefined()); return Undefined(); @@ -742,7 +734,6 @@ Handle Database::Tables(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Tables, UV_AfterQuery); - ev_ref(EV_DEFAULT_UC); dbo->Ref(); scope.Close(Undefined()); return Undefined(); @@ -819,7 +810,6 @@ Handle Database::Columns(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Columns, UV_AfterQuery); - ev_ref(EV_DEFAULT_UC); dbo->Ref(); scope.Close(Undefined()); return Undefined(); From a619797c129d6a892d8cf7a08e49e4ce4e264757 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 19 Apr 2012 11:13:02 -0400 Subject: [PATCH 015/511] optimize by removing memset() calls; instead set first byte to \0 --- src/Database.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 01d1cad3..955fcb5a 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -249,11 +249,10 @@ void Database::UV_AfterQuery(uv_work_t* req) { goto cleanupshutdown; } //else { - //malloc succeeded so let's continue -- I'm not too fond of having all this code in the else statement, but I don't know what else to do... - // you could use goto ;-) + //malloc succeeded so let's continue - memset(buf,0,MAX_VALUE_SIZE); //set all of the bytes of the buffer to 0; I tried doing this inside the loop, but it increased processing time dramatically - + //set the first byte of the buffer to \0 instead of memsetting the entire buffer to 0 + buf[0] = '\0'; //First thing, let's check if the execution of the query returned any errors (in UV_Query) if(prep_req->result == SQL_ERROR) @@ -295,8 +294,8 @@ void Database::UV_AfterQuery(uv_work_t* req) { { columns[i].name = new unsigned char[MAX_FIELD_SIZE]; - //zero out the space where the column name will be stored - memset(columns[i].name, 0, MAX_FIELD_SIZE); + //set the first byte of name to \0 instead of memsetting the entire buffer + columns[i].name[0] = '\n'; //get the column name ret = SQLColAttribute(self->m_hStmt, (SQLUSMALLINT)i+1, SQL_DESC_LABEL, columns[i].name, (SQLSMALLINT)MAX_FIELD_SIZE, (SQLSMALLINT *)&buflen, NULL); From 09d16e7120571378b02eebea34fc00811b190969 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 19 Apr 2012 11:14:39 -0400 Subject: [PATCH 016/511] fix double allocations of handles --- src/Database.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 955fcb5a..8fba84dc 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -426,14 +426,6 @@ void Database::UV_AfterQuery(uv_work_t* req) { //move to the next result set ret = SQLMoreResults( self->m_hStmt ); - if ( ret != SQL_SUCCESS ) { - //there are no more recordsets so free the statement now before we emit - //because as soon as we emit the last recordest, we are clear to submit another query - //which could cause a race condition with freeing and allocating handles. - SQLFreeHandle( SQL_HANDLE_STMT, self->m_hStmt ); - SQLAllocHandle( SQL_HANDLE_STMT, self->m_hDBC, &self->m_hStmt ); - } - //Only trigger an emit if there are columns OR if this is the last result and none others have been emitted //odbc will process individual statments like select @something = 1 as a recordset even though it doesn't have //any columns. We don't want to emit those unless there are actually columns @@ -486,7 +478,7 @@ void Database::UV_Query(uv_work_t* req) { if(prep_req->dbo->m_hStmt) { SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); - SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->dbo->m_hStmt ); + SQLAllocHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hDBC, &prep_req->dbo->m_hStmt ); } //check to see if should excute a direct or a parameter bound query From c68be235da223db4b6a6ad8e2c720e4feff81cc8 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 20 Apr 2012 16:54:52 -0400 Subject: [PATCH 017/511] bench-insert: make callback a named function; use common.connectionString --- test/bench-insert.js | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/test/bench-insert.js b/test/bench-insert.js index ac85ed0f..d112cb04 100644 --- a/test/bench-insert.js +++ b/test/bench-insert.js @@ -2,7 +2,7 @@ var common = require("./common") , odbc = require("../odbc.js") , db = new odbc.Database(); -db.open(common.connectionObject, function(err){ +db.open(common.connectionString, function(err){ if (err) { console.error(err); process.exit(1); @@ -35,23 +35,26 @@ function dropTable() { function insertData() { var count = 0 - , iterations = 1000 + , iterations = 10000 , time = new Date().getTime(); for (var x = 0; x < iterations; x++) { - db.query("insert into bench_insert (str) values ('testing')", function (err) { - if (err) { - console.error(err);639.38619 - return finish(); - } + db.query("insert into bench_insert (str) values ('testing')", cb); + + } + + function cb (err) { + if (err) { + console.error(err); + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; - if (++count == iterations) { - var elapsed = new Date().getTime() - time; - - console.log("%d records inserted in %d seconds, %d/sec", iterations, elapsed/1000, iterations/(elapsed/1000)); - return dropTable(); - } - }); + console.log("%d records inserted in %d seconds, %d/sec", iterations, elapsed/1000, iterations/(elapsed/1000)); + return dropTable(); + } } } @@ -59,4 +62,4 @@ function finish() { db.close(function () { console.log("connection closed"); }); -} \ No newline at end of file +} From 95da63a70ae6e5d90a919f57da8c5f317c74b67c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 20 Apr 2012 16:55:57 -0400 Subject: [PATCH 018/511] test: odbc-bench.c: use mysql on localhost --- test/odbc-bench.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/odbc-bench.c b/test/odbc-bench.c index 120163c5..ff912e5d 100644 --- a/test/odbc-bench.c +++ b/test/odbc-bench.c @@ -45,7 +45,7 @@ int main() { ret = SQLDriverConnect( m_hDBC, NULL, - "DRIVER={MySQL};SERVER=mysql-02;USER=test;PASSWORD=;DATABASE=test;", + "DRIVER={MySQL};SERVER=localhost;USER=test;PASSWORD=;DATABASE=test;", SQL_NTS, NULL,//outstr, 0,//sizeof(outstr), From 95a0d76aff76efce4102811dac07c512826fe5c4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 20 Apr 2012 16:56:30 -0400 Subject: [PATCH 019/511] bench-query: reformat results --- test/bench-query.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/bench-query.js b/test/bench-query.js index 9198c787..8e3e3343 100644 --- a/test/bench-query.js +++ b/test/bench-query.js @@ -29,7 +29,7 @@ function issueQuery() { if (++count == iterations) { var elapsed = new Date().getTime() - time; - console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, count/(elapsed/1000)); + console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); return finish(); } } @@ -39,4 +39,4 @@ function finish() { db.close(function () { console.log("connection closed"); }); -} \ No newline at end of file +} From 2edb3c6f7f5da04a30c506cac2937a7831588b89 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 25 Apr 2012 07:42:58 -0400 Subject: [PATCH 020/511] bump version to v0.3.2; specify node compatibility < 0.7.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 04ffba6c..372ba954 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.3.1", + "version": "0.3.2", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { "type": "git", @@ -12,7 +12,7 @@ "lib": "." }, "engines": { - "node": "*" + "node": "<0.7.0" }, "scripts": { "preinstall":"node-waf configure build" From f93662585f67fc50f5ec5c6224e92823badff89f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 25 Apr 2012 07:45:16 -0400 Subject: [PATCH 021/511] release v0.4.0; specify node compatibility >= 0.7.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3f57a8c6..67fa9211 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.4.1pre", + "version": "0.4.0", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { "type": "git", @@ -12,7 +12,7 @@ "lib": "." }, "engines": { - "node": "*" + "node": ">=0.7.0" }, "scripts": { "preinstall":"node-waf configure build" From e4618cc6a7641000d4e9a5051675cdecfc4db7c2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 30 May 2012 10:32:07 -0400 Subject: [PATCH 022/511] introduce uv_rev() and uv_unref() to keep the event loop running while a database connection is open --- src/Database.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Database.cpp b/src/Database.cpp index 8fba84dc..7a43c876 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -80,6 +80,8 @@ void Database::UV_AfterOpen(uv_work_t* req) { open_req->cb.Dispose(); + uv_ref(uv_default_loop()); + free(open_req); scope.Close(Undefined()); } @@ -169,6 +171,8 @@ void Database::UV_AfterClose(uv_work_t* req) { close_req->cb.Dispose(); + uv_unref(uv_default_loop()); + free(close_req); scope.Close(Undefined()); } From 9c378a40f6f0f46030973c344d0d46666e35b4c1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 26 Jun 2012 10:38:12 -0400 Subject: [PATCH 023/511] free all req's after they have been used --- src/Database.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Database.cpp b/src/Database.cpp index 7a43c876..a1b79f86 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -83,6 +83,7 @@ void Database::UV_AfterOpen(uv_work_t* req) { uv_ref(uv_default_loop()); free(open_req); + free(req); scope.Close(Undefined()); } @@ -174,6 +175,7 @@ void Database::UV_AfterClose(uv_work_t* req) { uv_unref(uv_default_loop()); free(close_req); + free(req); scope.Close(Undefined()); } @@ -470,6 +472,7 @@ void Database::UV_AfterQuery(uv_work_t* req) { free(prep_req->table); free(prep_req->type); free(prep_req); + free(req); scope.Close(Undefined()); } From 305a49e0911aeda26b166c82700319f2781a946d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 26 Jun 2012 10:38:12 -0400 Subject: [PATCH 024/511] free all req's after they have been used --- src/Database.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Database.cpp b/src/Database.cpp index 9eb5049d..d884d291 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -81,6 +81,7 @@ void Database::UV_AfterOpen(uv_work_t* req) { open_req->cb.Dispose(); free(open_req); + free(req); scope.Close(Undefined()); } @@ -170,6 +171,7 @@ void Database::UV_AfterClose(uv_work_t* req) { close_req->cb.Dispose(); free(close_req); + free(req); scope.Close(Undefined()); } @@ -466,6 +468,7 @@ void Database::UV_AfterQuery(uv_work_t* req) { free(prep_req->table); free(prep_req->type); free(prep_req); + free(req); scope.Close(Undefined()); } From a18845f08bd7213a2884e8aa134077e71992817f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 30 May 2012 10:32:07 -0400 Subject: [PATCH 025/511] introduce uv_rev() and uv_unref() to keep the event loop running while a database connection is open --- src/Database.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Database.cpp b/src/Database.cpp index d884d291..53893523 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -80,6 +80,8 @@ void Database::UV_AfterOpen(uv_work_t* req) { open_req->cb.Dispose(); + uv_ref(uv_default_loop()); + free(open_req); free(req); scope.Close(Undefined()); @@ -170,6 +172,8 @@ void Database::UV_AfterClose(uv_work_t* req) { close_req->cb.Dispose(); + uv_unref(uv_default_loop()); + free(close_req); free(req); scope.Close(Undefined()); From d150ffd948a31d33441e53c4535a3094cd8035ff Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 26 Jun 2012 10:54:49 -0400 Subject: [PATCH 026/511] remove uv_ref()/uv_unref() After node v0.7.9, the uv_ref()/uv_unref() functions take a uv_handle_t* The only way to keep the database connection open until close() is called is if we initialize an async handle using uv_async_init() and then ref and unref using that handle. See: https://github.com/rbranson/node-ffi/blob/02588f775de89aecde82a43bc537dd09e71d958d/src/callback_info.cc#L175 and https://github.com/rbranson/node-ffi/commit/02588f775de89aecde82a43bc537dd09e71d958d This introduces a problem where node will exit even if the database connection is still open. --- src/Database.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 53893523..d884d291 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -80,8 +80,6 @@ void Database::UV_AfterOpen(uv_work_t* req) { open_req->cb.Dispose(); - uv_ref(uv_default_loop()); - free(open_req); free(req); scope.Close(Undefined()); @@ -172,8 +170,6 @@ void Database::UV_AfterClose(uv_work_t* req) { close_req->cb.Dispose(); - uv_unref(uv_default_loop()); - free(close_req); free(req); scope.Close(Undefined()); From 0222c3c255e7b9f0ec8321b6b7288b5783f3fc32 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 17 Jul 2012 12:51:53 -0400 Subject: [PATCH 027/511] introduce Database::g_async to reintroduce uv_ref()/uv_unref() As mentioned in d150ffd9 in order to keep node alive while the database connections were open I had to introduce a uv_async_t g_async static member to the Database class. This is used by uv_async_init(), uv_ref() and uv_unref() in versions of node > 0.7.9. In order to call uv_async_init(), I had to add a callback watcher function called WatcherCallback which is a static method of the Database class. --- src/Database.cpp | 19 +++++++++++++++++++ src/Database.h | 3 +++ 2 files changed, 22 insertions(+) diff --git a/src/Database.cpp b/src/Database.cpp index d884d291..1d45383b 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ using namespace v8; using namespace node; uv_mutex_t Database::m_odbcMutex; +uv_async_t Database::g_async; void Database::Init(v8::Handle target) { HandleScope scope; @@ -47,6 +49,7 @@ void Database::Init(v8::Handle target) { target->Set(v8::String::NewSymbol("Database"), constructor_template->GetFunction()); scope.Close(Undefined()); + uv_async_init(uv_default_loop(), &Database::g_async, Database::WatcherCallback); uv_mutex_init(&Database::m_odbcMutex); } @@ -58,6 +61,10 @@ Handle Database::New(const Arguments& args) { return args.This(); } +void Database::WatcherCallback(uv_async_t *w, int revents) { + //i don't know if we need to do anything here +} + void Database::UV_AfterOpen(uv_work_t* req) { HandleScope scope; open_request* open_req = (open_request *)(req->data); @@ -80,6 +87,12 @@ void Database::UV_AfterOpen(uv_work_t* req) { open_req->cb.Dispose(); +#if NODE_VERSION_AT_LEAST(0, 7, 9) + uv_ref((uv_handle_t *)&Database::g_async); +#else + uv_ref(uv_default_loop()); +#endif + free(open_req); free(req); scope.Close(Undefined()); @@ -170,6 +183,12 @@ void Database::UV_AfterClose(uv_work_t* req) { close_req->cb.Dispose(); +#if NODE_VERSION_AT_LEAST(0, 7, 9) + uv_unref((uv_handle_t *)&Database::g_async); +#else + uv_unref(uv_default_loop()); +#endif + free(close_req); free(req); scope.Close(Undefined()); diff --git a/src/Database.h b/src/Database.h index 82a97550..1f638260 100644 --- a/src/Database.h +++ b/src/Database.h @@ -33,6 +33,7 @@ class Database : public node::ObjectWrap { static Persistent constructor_template; static void Init(v8::Handle target); static pthread_mutex_t m_odbcMutex; + static uv_async_t g_async; protected: Database() { } @@ -60,6 +61,8 @@ class Database : public node::ObjectWrap { static void UV_Columns(uv_work_t* req); static Handle Columns(const Arguments& args); + static void WatcherCallback(uv_async_t *w, int revents); + Database *self(void) { return this; } void printError(const char *fn, SQLHANDLE handle, SQLSMALLINT type); From 68a1e7de3df426539607ec1146e8762a4e3ceb44 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 17 Jul 2012 12:57:34 -0400 Subject: [PATCH 028/511] rename m_odbcMutex to g_odbcMutex --- src/Database.cpp | 12 ++++++------ src/Database.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 1d45383b..297dc134 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -29,7 +29,7 @@ using namespace v8; using namespace node; -uv_mutex_t Database::m_odbcMutex; +uv_mutex_t Database::g_odbcMutex; uv_async_t Database::g_async; void Database::Init(v8::Handle target) { @@ -50,7 +50,7 @@ void Database::Init(v8::Handle target) { target->Set(v8::String::NewSymbol("Database"), constructor_template->GetFunction()); scope.Close(Undefined()); uv_async_init(uv_default_loop(), &Database::g_async, Database::WatcherCallback); - uv_mutex_init(&Database::m_odbcMutex); + uv_mutex_init(&Database::g_odbcMutex); } Handle Database::New(const Arguments& args) { @@ -102,7 +102,7 @@ void Database::UV_Open(uv_work_t* req) { open_request* open_req = (open_request *)(req->data); Database* self = open_req->dbo->self(); - uv_mutex_lock(&Database::m_odbcMutex); + uv_mutex_lock(&Database::g_odbcMutex); int ret = SQLAllocEnv( &self->m_hEnv ); if( ret == SQL_SUCCESS ) { @@ -128,7 +128,7 @@ void Database::UV_Open(uv_work_t* req) { } } } - uv_mutex_unlock(&Database::m_odbcMutex); + uv_mutex_unlock(&Database::g_odbcMutex); open_req->result = ret; } @@ -198,13 +198,13 @@ void Database::UV_Close(uv_work_t* req) { close_request* close_req = (close_request *)(req->data); Database* dbo = close_req->dbo; - uv_mutex_lock(&Database::m_odbcMutex); + uv_mutex_lock(&Database::g_odbcMutex); SQLDisconnect(dbo->m_hDBC); SQLFreeHandle(SQL_HANDLE_ENV, dbo->m_hEnv); SQLFreeHandle(SQL_HANDLE_DBC, dbo->m_hDBC); - uv_mutex_unlock(&Database::m_odbcMutex); + uv_mutex_unlock(&Database::g_odbcMutex); } Handle Database::Close(const Arguments& args) { diff --git a/src/Database.h b/src/Database.h index 1f638260..c166c721 100644 --- a/src/Database.h +++ b/src/Database.h @@ -32,7 +32,7 @@ class Database : public node::ObjectWrap { public: static Persistent constructor_template; static void Init(v8::Handle target); - static pthread_mutex_t m_odbcMutex; + static pthread_mutex_t g_odbcMutex; static uv_async_t g_async; protected: From c4e5d0b6485e4af76de950237eab318d3b997473 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 17 Jul 2012 15:17:14 -0400 Subject: [PATCH 029/511] add new test for uv_ref (test-open-dont-close) and pool close --- test/test-open-close.js | 12 ++++++++++++ test/test-open-dont-close.js | 9 +++++++++ test/test-pool-close.js | 38 ++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 test/test-open-close.js create mode 100644 test/test-open-dont-close.js create mode 100644 test/test-pool-close.js diff --git a/test/test-open-close.js b/test/test-open-close.js new file mode 100644 index 00000000..402d1703 --- /dev/null +++ b/test/test-open-close.js @@ -0,0 +1,12 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database(); + +db.open(common.connectionString, function(err) +{ + console.error('db.open callback'); + + db.close(function () { + console.error('db.close callback'); + }); +}); diff --git a/test/test-open-dont-close.js b/test/test-open-dont-close.js new file mode 100644 index 00000000..b7bc6367 --- /dev/null +++ b/test/test-open-dont-close.js @@ -0,0 +1,9 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database(); + +db.open(common.connectionString, function(err) +{ + console.error('db.open callback'); + console.error('node should just sit and wait'); +}); diff --git a/test/test-pool-close.js b/test/test-pool-close.js new file mode 100644 index 00000000..353a039a --- /dev/null +++ b/test/test-pool-close.js @@ -0,0 +1,38 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , pool = new odbc.Pool() + , connectionString = common.connectionString + , connections = [] + , connectCount = 10; + +openConnectionsUsingPool(connections); + +function openConnectionsUsingPool(connections) { + for (var x = 0; x <= connectCount; x++) { + + (function (connectionIndex) { + console.error("Opening connection #", connectionIndex); + + pool.open(connectionString, function (err, connection) { + //console.error("Opened connection #", connectionIndex); + + if (err) { + console.error("error: ", err.message); + return false; + } + + connections.push(connection); + + if (connectionIndex == connectCount) { + closeConnections(connections); + } + }); + })(x); + } +} + +function closeConnections (connections) { + pool.close(function () { + console.error("pool closed"); + }); +} \ No newline at end of file From c37e772942880d4c78ec864be36118ccdea168bf Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 17 Jul 2012 15:36:30 -0400 Subject: [PATCH 030/511] add close() method to Pool and rework the pool a little bit to keep track of in use connections and available connections. --- odbc.js | 122 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 106 insertions(+), 16 deletions(-) diff --git a/odbc.js b/odbc.js index 1b477e13..243fb25c 100644 --- a/odbc.js +++ b/odbc.js @@ -26,7 +26,10 @@ try { } } -var Database = exports.Database = function () { +exports.debug = false; +exports.Database = Database; + +function Database () { var self = this; var db = new odbc.Database(); db.executing = false; @@ -47,11 +50,11 @@ Database.prototype.processQueue = function () { var self = this; if (!self.queue) self.queue = []; - + if (self.connected && !self.executing && self.queue.length) { var currentQuery = self.queue[0]; self.executing = true; - + currentQuery.method.apply(currentQuery.context, currentQuery.args); //TODO: we need to make sure we aren't sending any extra arguments to the cpp method } }; @@ -233,22 +236,31 @@ Database.prototype.describe = function(obj, callback) { } }; +exports.Pool = Pool; + +Pool.count = 0; -var Pool = exports.Pool = function () { +function Pool () { var self = this; - - self.connectionPool = {}; + self.index = Pool.count++; + self.availablePool = {}; + self.usedPool = {}; } Pool.prototype.open = function (connectionString, callback) { - var self = this; - + var self = this + , db + ; + //check to see if we already have a connection for this connection string - if (self.connectionPool[connectionString] && self.connectionPool[connectionString].length) { - callback(null, self.connectionPool[connectionString].shift()); + if (self.availablePool[connectionString] && self.availablePool[connectionString].length) { + db = self.availablePool[connectionString].shift() + self.usedPool[connectionString].push(db) + + callback(null, db); } else { - var db = new Database(); + db = new Database(); db.realClose = db.close; db.close = function (cb) { @@ -259,21 +271,99 @@ Pool.prototype.open = function (connectionString, callback) { //close the connection for real //this will kill any temp tables or anything that might be a security issue. - db.realClose( function () { - + db.realClose(function () { + //remove this db from the usedPool + self.usedPool[connectionString].splice(self.usedPool[connectionString].indexOf(db), 1); + //re-open the connection using the connection string db.open(connectionString, function (error) { + if (error) { + console.error(error); + return; + } //add this clean connection to the connection pool - self.connectionPool[connectionString] = self.connectionPool[connectionString] || []; - self.connectionPool[connectionString].push(db); - + self.availablePool[connectionString] = self.availablePool[connectionString] || []; + self.availablePool[connectionString].push(db); + exports.debug && console.dir(self); }); }); }; db.open(connectionString, function (error) { + exports.debug && console.log("odbc.js : pool[%s] : pool.db.open callback()", self.index); + + self.usedPool[connectionString] = self.usedPool[connectionString] || []; + self.usedPool[connectionString].push(db); + callback(error, db); }); } }; + +Pool.prototype.close = function (callback) { + var self = this + , required = 0 + , received = 0 + , connections + , key + , x + ; + + exports.debug && console.log("odbc.js : pool[%s] : pool.close()", self.index); + //we set a timeout because a previous db.close() may + //have caused the a behind the scenes db.open() to prepare + //a new connection + setTimeout(function () { + //merge the available pool and the usedPool + var pools = {}; + + for (key in self.availablePool) { + pools[key] = (pools[key] || []).concat(self.availablePool[key]); + } + + for (key in self.usedPool) { + pools[key] = (pools[key] || []).concat(self.usedPool[key]); + } + + exports.debug && console.log("odbc.js : pool[%s] : pool.close() - setTimeout() callback", self.index); + exports.debug && console.dir(pools); + + if (Object.keys(pools).length == 0) { + return callback(); + } + + for (key in pools) { + connections = pools[key]; + required += connections.length; + + exports.debug && console.log("odbc.js : pool[%s] : pool.close() - processing pools %s - connections: %s", self.index, key, connections.length); + + for (x = 0 ; x < connections.length; x ++) { + (function (x) { + //call the realClose method to avoid + //automatically re-opening the connection + exports.debug && console.log("odbc.js : pool[%s] : pool.close() - calling realClose() for connection #%s", self.index, x); + + connections[x].realClose(function () { + exports.debug && console.log("odbc.js : pool[%s] : pool.close() - realClose() callback for connection #%s", self.index, x); + received += 1; + + if (received === required) { + callback(); + + //prevent mem leaks + self = null; + connections = null; + required = null; + received = null; + key = null; + + return; + } + }); + })(x); + } + } + }, 2000); +}; \ No newline at end of file From 8c9762ce5422790a9cff0a9d5f06d31cb0f1f2ad Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 20 Jul 2012 10:14:12 -0400 Subject: [PATCH 031/511] Move the connection member to the end of the open_request struct. This is what was causing node-gyp to build a module which would fail with 'buffer overflow detected' possibly due to its use of the -O2 compiler flag. --- src/Database.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database.h b/src/Database.h index c166c721..92090f45 100644 --- a/src/Database.h +++ b/src/Database.h @@ -83,8 +83,8 @@ enum ExecMode struct open_request { Persistent cb; Database *dbo; - char connection[1]; int result; + char connection[1]; }; struct close_request { From 53a8c76c59a1d4cc0ed6dabcb513d68422f33f51 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 20 Jul 2012 10:14:32 -0400 Subject: [PATCH 032/511] Remove include dirs from binding.gyp --- binding.gyp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/binding.gyp b/binding.gyp index c05093db..2000d8bf 100644 --- a/binding.gyp +++ b/binding.gyp @@ -8,11 +8,6 @@ 'libraries' : [ '-lodbc' ], - 'include_dirs' : [ - '/usr/local/lib', - '/opt/local/lib', - '/usr/include' - ], 'conditions' : [ [ 'OS == "linux"', { From 64d2bd26b319344cd471fa25114a44cdac666b2f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 9 Aug 2012 12:54:09 -0400 Subject: [PATCH 033/511] added more mutex locks - not sure if necessary --- src/Database.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Database.cpp b/src/Database.cpp index 297dc134..31308777 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -102,7 +102,7 @@ void Database::UV_Open(uv_work_t* req) { open_request* open_req = (open_request *)(req->data); Database* self = open_req->dbo->self(); - uv_mutex_lock(&Database::g_odbcMutex); + uv_mutex_lock(&Database::g_odbcMutex); int ret = SQLAllocEnv( &self->m_hEnv ); if( ret == SQL_SUCCESS ) { @@ -499,8 +499,12 @@ void Database::UV_Query(uv_work_t* req) { if(prep_req->dbo->m_hStmt) { + uv_mutex_lock(&Database::g_odbcMutex); + SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); SQLAllocHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hDBC, &prep_req->dbo->m_hStmt ); + + uv_mutex_unlock(&Database::g_odbcMutex); } //check to see if should excute a direct or a parameter bound query @@ -681,8 +685,10 @@ void Database::UV_Tables(uv_work_t* req) { if(prep_req->dbo->m_hStmt) { + uv_mutex_lock(&Database::g_odbcMutex); SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->dbo->m_hStmt ); + uv_mutex_unlock(&Database::g_odbcMutex); } SQLRETURN ret = SQLTables( From 1fe8d4d10dda07621d8c449af8a98afc54c4ab3e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 15 Aug 2012 17:01:45 -0400 Subject: [PATCH 034/511] use node-gyp to build --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 67fa9211..a3baf8e6 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,6 @@ "node": ">=0.7.0" }, "scripts": { - "preinstall":"node-waf configure build" + "preinstall":"node-gyp configure build" } } From 81fabe01af91c3c1ac9271bfa239517509a3c402 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 20 Sep 2012 10:39:09 -0400 Subject: [PATCH 035/511] v0.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a3baf8e6..aecb4914 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.4.0", + "version": "0.4.1", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { "type": "git", From af42e85b2ae6e79f92a665eedcae204786100c0d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Oct 2012 17:58:40 -0400 Subject: [PATCH 036/511] updated readme with better api docs --- README.md | 276 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 231 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 2362c85d..4f70b0c1 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,226 @@ -NAME ----- +node-odbc +--------- -node-odbc - An asynchronous Node interface to unixodbc and its supported drivers +An asynchronous interface for node.js to unixODBC and its supported drivers. -SYNOPSIS --------- +requirements +------------ - var sys = require("sys"); - var odbc = require("odbc"); +* unixODBC binaries and development libraries for module compilation + * on Ubuntu/Debian `sudo apt-get install unixodbc unixodbc-dev` + * on OSX using macports.org `sudo port unixODBC` +* odbc drivers for target database +* properly configured odbc.ini and odbcinst.ini. - var db = new odbc.Database(); - db.open("DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname", function(err) - { - db.query("select * from table", function(err, rows, moreResultSets) - { - sys.debug(sys.inspect(rows)); - db.close(function(){}); - }); +install +------- + +After insuring that all requirements are installed you may install by one of the +two following options: + +### git + +```bash +git clone git://github.com/wankdanker/node-odbc.git +cd node-odbc +node-gyp configure build +``` +### npm + +```bash +npm install odbc +``` + +api +--- + +### Database + +#### .open(connectionString, callback) + +Open a connection to a database. + +* **connectionString** - The ODBC connection string for your database +* **callback** - `callback (err)` + +```javascript +var Database = require("odbc").Database + , db = new Database() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +db.open(cn, function (err) { + if (err) { + return console.log(err); + } + + //we now have an open connection to the database +}); +``` + +#### .query(sqlQuery [, bindingParameters], callback) + +Issue a SQL query to the database which is currently open. + +* **sqlQuery** - The SQL query to be executed. +* **bindingParameters** - _OPTIONAL_ - An array of values that will be bound to + any '?' characters in `sqlQuery`. +* **callback** - `callback (err, rows, moreResultSets)` + +```javascript +var Database = require("odbc").Database + , db = new Database() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +db.open(cn, function (err) { + if (err) { + return console.log(err); + } + + //we now have an open connection to the database + //so lets get some data + db.query("select top 10 * from customers", function (err, rows, moreResultSets) { + if (err) { + return console.log(err); + } + + console.log(rows); + + //if moreResultSets is truthy, then this callback function will be called + //again with the next set of rows. }); +}); +``` +#### .close(callback) -DESCRIPTION ------------ +Close the currently opened database. -unixODBC binding to node. Needs a properly configured odbc(inst).ini. Tested locally using the FreeTDS and Postgres drivers. +* **callback** - `callback (err)` +```javascript +var Database = require("odbc").Database + , db = new Database() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; -INSTALLATION ------------- +db.open(cn, function (err) { + if (err) { + return console.log(err); + } + + //we now have an open connection to the database + + db.close(function (err) { + console.log("the database connection is now closed"); + }); +}); +``` -- Make sure you have the unixODBC binaries and unixODBC headers installed and the drivers configured. - - On ubuntu and probably most linux distros the unixODBC header files are in the unixodbc-dev package (apt-get install unixodbc-dev) - - On OSX one can use macports.org to install unixODBC (sudo port unixODBC) +---------- -###git +### Pool - git clone git://github.com/w1nk/node-odbc.git - cd node-odbc - node-waf configure build +The node-odbc `Pool` is a rudimentary connection pool which will attempt to have +database connections ready and waiting for you when you call the `open` method. -###npm +If you use a `Pool` instance, any connection that you close will cause another +connection to be opened for that same connection string. That connection will +be used the next time you call `Pool.open()` for the same connection string. - npm install odbc +This should probably be changed. +#### .open(connectionString, callback) -TIPS ----- +Get a Database` instance which is already connected to `connectionString` + +* **connectionString** - The ODBC connection string for your database +* **callback** - `callback (err, db)` + +```javascript +var Pool = require("odbc").Pool + , pool = new Pool() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +pool.open(cn, function (err, db) { + if (err) { + return console.log(err); + } -- If you are using the FreeTDS ODBC driver and you have column names longer than 30 characters, you should add "TDS_Version=7.0" to your connection string to retrive the full column name. + //db is now an open database connection and can be used like normal + //if we run some queries with db.query(...) and then call db.close(); + //a connection to `cn` will be re-opened silently behind the scense + //and will be ready the next time we do `pool.open(cn)` +}); +``` -###Example +#### .close(callback) + +Close all connections in the `Pool` instance + +* **callback** - `callback (err)` + +```javascript +var Pool = require("odbc").Pool + , pool = new Pool() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +pool.open(cn, function (err, db) { + if (err) { + return console.log(err); + } + + //db is now an open database connection and can be used like normal + //but all we will do now is close the whole pool + + pool.close(function () { + console.log("all connections in the pool are closed"); + }); +}); +``` - "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname;TDS_Version=7.0" +example +------- +```javascript +var odbc = require("odbc") + , util = require('util') + , db = new odbc.Database() + ; -BUGS +var connectionString = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname"; + +db.open(connectionString, function(err) { + db.query("select * from table", function(err, rows, moreResultSets) { + console.log(util.inspect(rows, null, 10)); + + db.close(function() { + console.log("Database connection closed"); + }); + }); +}); +``` + +testing +------- + +There is a tests folder which contains scripts which are more examples than tests. +We will be working on bundling these tests into an actual test suite. Sorry about +the state of this. Please feel free to submit patches for this. + +tips ---- -None known, but there might be one ;). +* If you are using the FreeTDS ODBC driver and you have column names longer than +30 characters, you should add "TDS_Version=7.0" to your connection string to +retrive the full column name. + * Example : "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname;TDS_Version=7.0" -COMPLETE + +complete -------- - Connection Management @@ -67,22 +228,47 @@ COMPLETE - Database Descriptions - Binding Parameters (thanks to @gurzgri) -TODO +todo ---- -- Option to emit on each record to avoid collecting the entire dataset first and increasing memory usage +- Option to emit on each record to avoid collecting the entire dataset first and + increasing memory usage - More error handling. - Tests -- SQLGetData needs to support retrieving multiple chunks and concatenation in the case of large column values +- SQLGetData needs to support retrieving multiple chunks and concatenation in + the case of large column values -ACKNOWLEDGEMENTS +acknowledgements ---------------- -- orlandov's node-sqlite binding was the framework I used to figure out using eio's thread pool to handle blocking calls since non blocking odbc doesn't seem to appear until 3.8. +- orlandov's node-sqlite binding was the framework I used to figure out using + eio's thread pool to handle blocking calls since non blocking odbc doesn't + seem to appear until 3.8. -AUTHORS +authors ------ -Lee Smith (notwink@gmail.com) +* Lee Smith (notwink@gmail.com) +* Dan VerWeire (dverweire@gmail.com) + +license +------- + +Copyright (c) 2010 Lee Smith + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -Dan VerWeire (dverweire@gmail.com) +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From bd249111628fdf60e6c23caba69df7d86a41e372 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Oct 2012 12:27:42 -0400 Subject: [PATCH 037/511] updated readme tips section to mention nodes thread pool limitation and workaround --- README.md | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4f70b0c1..ceababe1 100644 --- a/README.md +++ b/README.md @@ -213,12 +213,49 @@ the state of this. Please feel free to submit patches for this. tips ---- +### Using node < v0.10 on Linux -* If you are using the FreeTDS ODBC driver and you have column names longer than -30 characters, you should add "TDS_Version=7.0" to your connection string to -retrive the full column name. +Be aware that through node v0.9 the uv_queue_work function, which is used to +execute the ODBC functions on a separate thread, uses libeio for its thread +pool. This thread pool by default is limited to 4 threads. + +This means that if you have long running queries spread across multiple +instances of odbc.Database() or using odbc.Pool(), you will only be able to +have 4 concurrent queries. + +You can increase the thread pool size by using @developmentseed's [node-eio] +(https://github.com/developmentseed/node-eio). + +#### install: +```bash +npm install eio +``` + +#### usage: +```javascript +var eio = require('eio'); +eio.setMinParallel(threadCount); +``` + +### Using the FreeTDS ODBC driver + +* If you have column names longer than 30 characters, you should add + "TDS_Version=7.0" to your connection string to retrive the full column name. * Example : "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname;TDS_Version=7.0" +* Be sure that your odbcinst.ini has the proper threading configuration for your + FreeTDS driver. If you choose the incorrect threading model it may cause + the thread pool to be blocked by long running queries. This is what + @wankdanker currently uses on Ubuntu 12.04: +``` +[FreeTDS] +Description = TDS driver (Sybase/MS SQL) +Driver = libtdsodbc.so +Setup = libtdsS.so +CPTimeout = 120 +CPReuse = +Threading = 0 +``` complete -------- From 074e2c20d5d274d21ec546f8af4c86e576466865 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Oct 2012 18:30:00 -0400 Subject: [PATCH 038/511] added .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..6c270187 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +a.out +build +node_modules +deps From b4afe6b101802078f667a8093c8962da9c5e2b88 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Oct 2012 18:31:14 -0400 Subject: [PATCH 039/511] change pthread_mutex_t to uv_mutex_t in the header file --- src/Database.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database.h b/src/Database.h index 92090f45..4734264e 100644 --- a/src/Database.h +++ b/src/Database.h @@ -32,7 +32,7 @@ class Database : public node::ObjectWrap { public: static Persistent constructor_template; static void Init(v8::Handle target); - static pthread_mutex_t g_odbcMutex; + static uv_mutex_t g_odbcMutex; static uv_async_t g_async; protected: From 6b94950a622aea5dc91b46880f03888c4c62bfcf Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Oct 2012 18:32:46 -0400 Subject: [PATCH 040/511] add NODE_MODULE declaration in Database.cpp --- src/Database.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Database.cpp b/src/Database.cpp index 31308777..96bdbf7d 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -864,3 +864,5 @@ Persistent Database::constructor_template; extern "C" void init (v8::Handle target) { Database::Init(target); } + +NODE_MODULE(odbc_bindings, init) \ No newline at end of file From af6976f7f98a5f450f90c5fec03ed994b3a7e773 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Oct 2012 18:43:53 -0400 Subject: [PATCH 041/511] Add support for Windows * modify binding.gyp to build strptime.c for Windows only * add strptime.{c,h} * include strptime.h in Database.cpp for _WIN32 --- binding.gyp | 17 ++- src/Database.cpp | 5 +- src/strptime.c | 389 +++++++++++++++++++++++++++++++++++++++++++++++ src/strptime.h | 42 +++++ 4 files changed, 446 insertions(+), 7 deletions(-) create mode 100644 src/strptime.c create mode 100644 src/strptime.h diff --git a/binding.gyp b/binding.gyp index 2000d8bf..ee12d595 100644 --- a/binding.gyp +++ b/binding.gyp @@ -3,17 +3,22 @@ { 'target_name' : 'odbc_bindings', 'sources' : [ - 'src/Database.cpp' - ], - 'libraries' : [ - '-lodbc' + 'src/Database.cpp' ], 'conditions' : [ [ 'OS == "linux"', { - + 'libraries' : [ + '-lodbc' + ] }], [ 'OS=="win"', { - + 'sources' : [ + 'src/strptime.c', + 'src/Database.cpp' + ], + 'libraries' : [ + '-lodbccp32.lib' + ] }] ] } diff --git a/src/Database.cpp b/src/Database.cpp index 96bdbf7d..19bc48f6 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -22,6 +22,10 @@ #include #include "Database.h" +#ifdef _WIN32 +#include "strptime.h" +#endif + #define MAX_FIELD_SIZE 1024 #define MAX_VALUE_SIZE 1048576 @@ -417,7 +421,6 @@ void Database::UV_AfterQuery(uv_work_t* req) { strptime(buf, "%Y-%m-%d %H:%M:%S", &timeInfo); timeInfo.tm_isdst = -1; //a negative value means that mktime() should (use timezone information and system //databases to) attempt to determine whether DST is in effect at the specified time. - tuple->Set(String::New((const char *)columns[i].name), Date::New(double(mktime(&timeInfo)) * 1000)); break; diff --git a/src/strptime.c b/src/strptime.c new file mode 100644 index 00000000..cca482fd --- /dev/null +++ b/src/strptime.c @@ -0,0 +1,389 @@ +/*- + * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * Heavily optimised by David Laight + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Inclusion in node-odbc note: + * + * This code was found here: http://social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/25a654f9-b6b6-490a-8f36-c87483bb36b7 + * One user posted what looks to be a scaled down version of the NetBSD code + * but did not include any header with their work. Since it seems pretty obvious + * that the user took much of the code from NetBSD, that is why the NetBSD header + * is displayed above. + */ + +#include "strptime.h" + +static int conv_num(const char **, int *, int, int); +static int strncasecmp(char *s1, char *s2, size_t n); + +char * strptime(const char *buf, const char *fmt, struct tm *tm) +{ + char c; + const char *bp; + size_t len = 0; + int alt_format, i, split_year = 0; + + bp = buf; + + while ((c = *fmt) != '\0') + { + /* Clear `alternate' modifier prior to new conversion. */ + alt_format = 0; + + /* Eat up white-space. */ + if (isspace(c)) + { + while (isspace(*bp)) + bp++; + + fmt++; + continue; + } + + if ((c = *fmt++) != '%') + goto literal; + + +again: switch (c = *fmt++) + { + case '%': /* "%%" is converted to "%". */ + literal: + if (c != *bp++) + return (0); + break; + + /* + * "Alternative" modifiers. Just set the appropriate flag + * and start over again. + */ + case 'E': /* "%E?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_E; + goto again; + + case 'O': /* "%O?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_O; + goto again; + + /* + * "Complex" conversion rules, implemented through recursion. + */ + case 'c': /* Date and time, using the locale's format. */ + LEGAL_ALT(ALT_E); + if (!(bp = strptime(bp, "%x %X", tm))) + return (0); + break; + + case 'D': /* The date as "%m/%d/%y". */ + LEGAL_ALT(0); + if (!(bp = strptime(bp, "%m/%d/%y", tm))) + return (0); + break; + + case 'R': /* The time as "%H:%M". */ + LEGAL_ALT(0); + if (!(bp = strptime(bp, "%H:%M", tm))) + return (0); + break; + + case 'r': /* The time in 12-hour clock representation. */ + LEGAL_ALT(0); + if (!(bp = strptime(bp, "%I:%M:%S %p", tm))) + return (0); + break; + + case 'T': /* The time as "%H:%M:%S". */ + LEGAL_ALT(0); + if (!(bp = strptime(bp, "%H:%M:%S", tm))) + return (0); + break; + + case 'X': /* The time, using the locale's format. */ + LEGAL_ALT(ALT_E); + if (!(bp = strptime(bp, "%H:%M:%S", tm))) + return (0); + break; + + case 'x': /* The date, using the locale's format. */ + LEGAL_ALT(ALT_E); + if (!(bp = strptime(bp, "%m/%d/%y", tm))) + return (0); + break; + + /* + * "Elementary" conversion rules. + */ + case 'A': /* The day of week, using the locale's form. */ + case 'a': + LEGAL_ALT(0); + for (i = 0; i < 7; i++) + { + /* Full name. */ + len = strlen(day[i]); + if (strncasecmp((char *)(day[i]), (char *)bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(abday[i]); + if (strncasecmp((char *)(abday[i]), (char *)bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 7) + return (0); + + tm->tm_wday = i; + bp += len; + break; + + case 'B': /* The month, using the locale's form. */ + case 'b': + case 'h': + LEGAL_ALT(0); + for (i = 0; i < 12; i++) + { + /* Full name. */ + + len = strlen(mon[i]); + if (strncasecmp((char *)(mon[i]), (char *)bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(abmon[i]); + if (strncasecmp((char *)(abmon[i]),(char *) bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 12) + return (0); + + tm->tm_mon = i; + bp += len; + break; + + case 'C': /* The century number. */ + LEGAL_ALT(ALT_E); + if (!(conv_num(&bp, &i, 0, 99))) + return (0); + + if (split_year) + { + tm->tm_year = (tm->tm_year % 100) + (i * 100); + } else { + tm->tm_year = i * 100; + split_year = 1; + } + break; + + case 'd': /* The day of month. */ + case 'e': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_mday, 1, 31))) + return (0); + break; + + case 'k': /* The hour (24-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'H': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_hour, 0, 23))) + return (0); + break; + + case 'l': /* The hour (12-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'I': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_hour, 1, 12))) + return (0); + if (tm->tm_hour == 12) + tm->tm_hour = 0; + break; + + case 'j': /* The day of year. */ + LEGAL_ALT(0); + if (!(conv_num(&bp, &i, 1, 366))) + return (0); + tm->tm_yday = i - 1; + break; + + case 'M': /* The minute. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_min, 0, 59))) + return (0); + break; + + case 'm': /* The month. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &i, 1, 12))) + return (0); + tm->tm_mon = i - 1; + break; + +// case 'p': /* The locale's equivalent of AM/PM. */ +// LEGAL_ALT(0); +// /* AM? */ +// if (strcasecmp(am_pm[0], bp) == 0) +// { +// if (tm->tm_hour > 11) +// return (0); +// +// bp += strlen(am_pm[0]); +// break; +// } +// /* PM? */ +// else if (strcasecmp(am_pm[1], bp) == 0) +// { +// if (tm->tm_hour > 11) +// return (0); +// +// tm->tm_hour += 12; +// bp += strlen(am_pm[1]); +// break; +// } +// +// /* Nothing matched. */ +// return (0); + + case 'S': /* The seconds. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_sec, 0, 61))) + return (0); + break; + + case 'U': /* The week of year, beginning on sunday. */ + case 'W': /* The week of year, beginning on monday. */ + LEGAL_ALT(ALT_O); + /* + * XXX This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so just check the + * range for now. + */ + if (!(conv_num(&bp, &i, 0, 53))) + return (0); + break; + + case 'w': /* The day of week, beginning on sunday. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_wday, 0, 6))) + return (0); + break; + + case 'Y': /* The year. */ + LEGAL_ALT(ALT_E); + if (!(conv_num(&bp, &i, 0, 9999))) + return (0); + + tm->tm_year = i - TM_YEAR_BASE; + break; + + case 'y': /* The year within 100 years of the epoch. */ + LEGAL_ALT(ALT_E | ALT_O); + if (!(conv_num(&bp, &i, 0, 99))) + return (0); + + if (split_year) + { + tm->tm_year = ((tm->tm_year / 100) * 100) + i; + break; + } + split_year = 1; + if (i <= 68) + tm->tm_year = i + 2000 - TM_YEAR_BASE; + else + tm->tm_year = i + 1900 - TM_YEAR_BASE; + break; + + /* + * Miscellaneous conversions. + */ + case 'n': /* Any kind of white-space. */ + case 't': + LEGAL_ALT(0); + while (isspace(*bp)) + bp++; + break; + + + default: /* Unknown/unsupported conversion. */ + return (0); + } + + + } + + /* LINTED functional specification */ + return ((char *)bp); +} + + +static int conv_num(const char **buf, int *dest, int llim, int ulim) +{ + int result = 0; + + /* The limit also determines the number of valid digits. */ + int rulim = ulim; + + if (**buf < '0' || **buf > '9') + return (0); + + do { + result *= 10; + result += *(*buf)++ - '0'; + rulim /= 10; + } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); + + if (result < llim || result > ulim) + return (0); + + *dest = result; + return (1); +} + +int strncasecmp(char *s1, char *s2, size_t n) +{ + if (n == 0) + return 0; + + while (n-- != 0 && tolower(*s1) == tolower(*s2)) + { + if (n == 0 || *s1 == '\0' || *s2 == '\0') + break; + s1++; + s2++; + } + + return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2); +} diff --git a/src/strptime.h b/src/strptime.h new file mode 100644 index 00000000..6e5c71e2 --- /dev/null +++ b/src/strptime.h @@ -0,0 +1,42 @@ +#define ALT_E 0x01 +#define ALT_O 0x02 +//#define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } +#define LEGAL_ALT(x) { ; } +#define TM_YEAR_BASE (1900) + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +char * strptime(const char *buf, const char *fmt, struct tm *tm); + +#ifdef __cplusplus +} +#endif + +static const char *day[7] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday" +}; + +static const char *abday[7] = { + "Sun","Mon","Tue","Wed","Thu","Fri","Sat" +}; + +static const char *mon[12] = { + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December" +}; + +static const char *abmon[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +static const char *am_pm[2] = { + "AM", "PM" +}; From 73a82aa876fa7d8da6e80d5d3519e38258c45444 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Oct 2012 18:48:19 -0400 Subject: [PATCH 042/511] 0.4.2 --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index aecb4914..a64842cd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.4.1", + "version": "0.4.2", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { "type": "git", @@ -14,7 +14,7 @@ "engines": { "node": ">=0.7.0" }, - "scripts": { - "preinstall":"node-gyp configure build" - } + "scripts": { + "preinstall": "node-gyp configure build" + } } From e57d6795adf310f9656776f30771c6658f77cdad Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Oct 2012 19:49:26 -0400 Subject: [PATCH 043/511] Specify libraries for mac *Assuming that not having this breaks installs on macs. --- binding.gyp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/binding.gyp b/binding.gyp index ee12d595..3260852c 100644 --- a/binding.gyp +++ b/binding.gyp @@ -11,6 +11,11 @@ '-lodbc' ] }], + [ 'OS == "mac"', { + 'libraries' : [ + '-lodbc' + ] + }], [ 'OS=="win"', { 'sources' : [ 'src/strptime.c', From 2ad273d89006b62cb45cfda662b326ad4af9a05f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Oct 2012 19:49:42 -0400 Subject: [PATCH 044/511] 0.4.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a64842cd..805cdebe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.4.2", + "version": "0.4.3", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { "type": "git", From 1f74b16b9989007e05e9e0ed6ae1858f33c9d08a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Oct 2012 17:44:56 -0400 Subject: [PATCH 045/511] Return helpful error object on failures and remove printError() on C++ side --- src/Database.cpp | 100 +++++++++++++++++++++++++++-------------------- src/Database.h | 20 +++------- 2 files changed, 64 insertions(+), 56 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 19bc48f6..e8d337de 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -74,10 +74,39 @@ void Database::UV_AfterOpen(uv_work_t* req) { open_request* open_req = (open_request *)(req->data); Local argv[1]; + bool err = false; + if (open_req->result) { err = true; - argv[0] = Exception::Error(String::New("Error opening database")); + + SQLINTEGER i = 0; + SQLINTEGER native; + char errorSQLState[ 7 ]; + char errorMessage[256]; + SQLSMALLINT len; + SQLRETURN ret; + + do { + ret = SQLGetDiagRec( SQL_HANDLE_DBC, + open_req->dbo->self()->m_hDBC, + ++i, + (SQLCHAR *) errorSQLState, + &native, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + &len ); + + if (SQL_SUCCEEDED(ret)) { + Local objError = Object::New(); + + objError->Set(String::New("state"), String::New(errorSQLState)); + objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); + objError->Set(String::New("message"), String::New(errorMessage)); + + argv[0] = objError; + } + } while( ret == SQL_SUCCESS ); } TryCatch try_catch; @@ -109,29 +138,41 @@ void Database::UV_Open(uv_work_t* req) { uv_mutex_lock(&Database::g_odbcMutex); int ret = SQLAllocEnv( &self->m_hEnv ); + if( ret == SQL_SUCCESS ) { ret = SQLAllocConnect( self->m_hEnv,&self->m_hDBC ); + if( ret == SQL_SUCCESS ) { - SQLSetConnectOption( self->m_hDBC,SQL_LOGIN_TIMEOUT,5 ); + SQLSetConnectOption( self->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); + char connstr[1024]; - ret = SQLDriverConnect(self->m_hDBC,NULL,(SQLCHAR*)open_req->connection,strlen(open_req->connection),(SQLCHAR*)connstr,1024,NULL,SQL_DRIVER_NOPROMPT); - if( ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ) - { - ret = SQLAllocStmt( self->m_hDBC,&self->m_hStmt ); - if (ret != SQL_SUCCESS) printf("not connected\n"); - - if ( !SQL_SUCCEEDED( SQLGetFunctions(self->m_hDBC, SQL_API_SQLMORERESULTS, &self->canHaveMoreResults))) - { - self->canHaveMoreResults = 0; - } - } - else - { - self->printError("SQLDriverConnect", self->m_hDBC, SQL_HANDLE_DBC); + //Attempt to connect + ret = SQLDriverConnect( self->m_hDBC, + NULL, + (SQLCHAR*) open_req->connection, + strlen(open_req->connection), + (SQLCHAR*) connstr, + 1024, + NULL, + SQL_DRIVER_NOPROMPT); + + if( ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ) { + ret = SQLAllocStmt( self->m_hDBC, &self->m_hStmt ); + + if (ret != SQL_SUCCESS) printf("not connected\n"); + + ret = SQLGetFunctions( self->m_hDBC, + SQL_API_SQLMORERESULTS, + &self->canHaveMoreResults); + + if ( !SQL_SUCCEEDED(ret)) { + self->canHaveMoreResults = 0; } + } } } + uv_mutex_unlock(&Database::g_odbcMutex); open_req->result = ret; } @@ -833,35 +874,10 @@ Handle Database::Columns(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Columns, UV_AfterQuery); dbo->Ref(); - scope.Close(Undefined()); - return Undefined(); -} - -void Database::printError(const char *fn, SQLHANDLE handle, SQLSMALLINT type) -{ - SQLINTEGER i = 0; - SQLINTEGER native; - SQLCHAR state[ 7 ]; - SQLCHAR text[256]; - SQLSMALLINT len; - SQLRETURN ret; - fprintf(stderr, - "\n" - "The driver reported the following diagnostics whilst running " - "%s\n\n", - fn - ); - - do { - ret = SQLGetDiagRec(type, handle, ++i, state, &native, text, sizeof(text), &len ); - if (SQL_SUCCEEDED(ret)) - printf("%s:%ld:%ld:%s\n", state, (long int) i, (long int) native, text); - } - while( ret == SQL_SUCCESS ); + return scope.Close(Undefined()); } - Persistent Database::constructor_template; extern "C" void init (v8::Handle target) { diff --git a/src/Database.h b/src/Database.h index 4734264e..20864491 100644 --- a/src/Database.h +++ b/src/Database.h @@ -63,23 +63,15 @@ class Database : public node::ObjectWrap { static void WatcherCallback(uv_async_t *w, int revents); - Database *self(void) { return this; } - void printError(const char *fn, SQLHANDLE handle, SQLSMALLINT type); + Database *self(void) { return this; } - protected: - HENV m_hEnv; - HDBC m_hDBC; - HSTMT m_hStmt; - SQLUSMALLINT canHaveMoreResults; + protected: + HENV m_hEnv; + HDBC m_hDBC; + HSTMT m_hStmt; + SQLUSMALLINT canHaveMoreResults; }; -enum ExecMode - { - EXEC_EMPTY = 0, - EXEC_LAST_INSERT_ID = 1, - EXEC_AFFECTED_ROWS = 2 - }; - struct open_request { Persistent cb; Database *dbo; From 83c0f37d6410a8d44ee2711a9ab4ea071e8e0bc3 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Oct 2012 17:45:29 -0400 Subject: [PATCH 046/511] Don't set connected to true when the connect failed --- odbc.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/odbc.js b/odbc.js index 243fb25c..9d167d77 100644 --- a/odbc.js +++ b/odbc.js @@ -117,9 +117,11 @@ Database.prototype.open = function(connectionString, callback) { } self.dispatchOpen(connectionString, function (err) { - self.connected = true; - self.processQueue(); - + if (!err) { + self.connected = true; + self.processQueue(); + } + return callback(err); }); }; From 046f1022bccbebf6a5471208d63a30dff816fe5c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Oct 2012 17:46:16 -0400 Subject: [PATCH 047/511] Wrap strptime.h in #ifndef _STRPTIME_H ... #endif --- src/strptime.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/strptime.h b/src/strptime.h index 6e5c71e2..ce0cbfaa 100644 --- a/src/strptime.h +++ b/src/strptime.h @@ -1,3 +1,6 @@ +#ifndef _STRPTIME_H +#define _STRPTIME_H + #define ALT_E 0x01 #define ALT_O 0x02 //#define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } @@ -40,3 +43,5 @@ static const char *abmon[12] = { static const char *am_pm[2] = { "AM", "PM" }; + +#endif \ No newline at end of file From 1d2e6450469e6919fdb37b175efdd8bbf7524456 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Oct 2012 17:46:43 -0400 Subject: [PATCH 048/511] Make whitespace consistent --- src/Database.h | 63 +++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/Database.h b/src/Database.h index 20864491..249400b6 100644 --- a/src/Database.h +++ b/src/Database.h @@ -14,8 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef DATABASE_H -#define DATABASE_H +#ifndef _DATABASE_H +#define _DATABASE_H #include #include @@ -29,39 +29,38 @@ using namespace v8; using namespace node; class Database : public node::ObjectWrap { - public: - static Persistent constructor_template; - static void Init(v8::Handle target); - static uv_mutex_t g_odbcMutex; - static uv_async_t g_async; + public: + static Persistent constructor_template; + static void Init(v8::Handle target); + static uv_mutex_t g_odbcMutex; + static uv_async_t g_async; - protected: - Database() { } + protected: + Database() {} - ~Database() { - } + ~Database() {} - static Handle New(const Arguments& args); + static Handle New(const Arguments& args); - static void UV_AfterOpen(uv_work_t* req); - static void UV_Open(uv_work_t* req); - static Handle Open(const Arguments& args); + static void UV_AfterOpen(uv_work_t* req); + static void UV_Open(uv_work_t* req); + static Handle Open(const Arguments& args); - static void UV_AfterClose(uv_work_t* req); - static void UV_Close(uv_work_t* req); - static Handle Close(const Arguments& args); + static void UV_AfterClose(uv_work_t* req); + static void UV_Close(uv_work_t* req); + static Handle Close(const Arguments& args); - static void UV_AfterQuery(uv_work_t* req); - static void UV_Query(uv_work_t* req); - static Handle Query(const Arguments& args); + static void UV_AfterQuery(uv_work_t* req); + static void UV_Query(uv_work_t* req); + static Handle Query(const Arguments& args); - static void UV_Tables(uv_work_t* req); - static Handle Tables(const Arguments& args); + static void UV_Tables(uv_work_t* req); + static Handle Tables(const Arguments& args); - static void UV_Columns(uv_work_t* req); - static Handle Columns(const Arguments& args); + static void UV_Columns(uv_work_t* req); + static Handle Columns(const Arguments& args); - static void WatcherCallback(uv_async_t *w, int revents); + static void WatcherCallback(uv_async_t *w, int revents); Database *self(void) { return this; } @@ -92,12 +91,12 @@ typedef struct { } Column; typedef struct { - SQLSMALLINT c_type; - SQLSMALLINT type; - SQLLEN size; - void *buffer; - SQLLEN buffer_length; - SQLLEN length; + SQLSMALLINT c_type; + SQLSMALLINT type; + SQLLEN size; + void *buffer; + SQLLEN buffer_length; + SQLLEN length; } Parameter; struct query_request { From f1b61b32ed5a0a01ecb84ca6883f477948117009 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Oct 2012 17:47:57 -0400 Subject: [PATCH 049/511] Update comments and return styles --- src/Database.cpp | 91 +++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index e8d337de..567d4b68 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -41,19 +41,29 @@ void Database::Init(v8::Handle target) { Local t = FunctionTemplate::New(New); + // Constructor Template constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); constructor_template->SetClassName(String::NewSymbol("Database")); + // Reserve space for one Handle + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + + // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchOpen", Open); NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchClose", Close); NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchQuery", Query); NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchTables", Tables); NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchColumns", Columns); + // Attach the Database Constructor to the target object target->Set(v8::String::NewSymbol("Database"), constructor_template->GetFunction()); + scope.Close(Undefined()); + + // Initialize uv_async so that we can prevent node from exiting uv_async_init(uv_default_loop(), &Database::g_async, Database::WatcherCallback); + + // Initialize the cross platform mutex provided by libuv uv_mutex_init(&Database::g_odbcMutex); } @@ -61,8 +71,8 @@ Handle Database::New(const Arguments& args) { HandleScope scope; Database* dbo = new Database(); dbo->Wrap(args.This()); - scope.Close(Undefined()); - return args.This(); + + return scope.Close(args.This()); } void Database::WatcherCallback(uv_async_t *w, int revents) { @@ -201,8 +211,8 @@ Handle Database::Open(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Open, UV_AfterOpen); dbo->Ref(); - scope.Close(Undefined()); - return Undefined(); + + return scope.Close(Undefined()); } void Database::UV_AfterClose(uv_work_t* req) { @@ -256,7 +266,7 @@ Handle Database::Close(const Arguments& args) { HandleScope scope; REQ_FUN_ARG(0, cb); - +//allocate a buffer for incoming column values Database* dbo = ObjectWrap::Unwrap(args.This()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); close_request* close_req = (close_request *) (calloc(1, sizeof(close_request))); @@ -274,30 +284,44 @@ Handle Database::Close(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Close, UV_AfterClose); dbo->Ref(); - scope.Close(Undefined()); - return Undefined(); -} + return scope.Close(Undefined()); +} +//allocate a buffer for incoming column values void Database::UV_AfterQuery(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); struct tm timeInfo = { 0 }; //used for processing date/time datatypes HandleScope scope; - Database* self = prep_req->dbo->self(); //an easy reference to the Database object - Local objError = Object::New(); //our error object which we will use if we discover errors while processing the result set + //an easy reference to the Database object + Database* self = prep_req->dbo->self(); + + //our error object which we will use if we discover errors while processing the result set + Local objError; + + //used to keep track of the number of columns received in a result set + short colCount = 0; + + //used to keep track of the number of event emittions that have occurred + short emitCount = 0; - short colCount = 0; //used to keep track of the number of columns received in a result set - short emitCount = 0; //used to keep track of the number of event emittions that have occurred - short errorCount = 0; //used to keep track of the number of errors that have been found + //used to keep track of the number of errors that have been found + short errorCount = 0; - SQLSMALLINT buflen; //used as a place holder for the length of column names - SQLRETURN ret; //used to capture the return value from various SQL function calls + //used as a place holder for the length of column names + SQLSMALLINT buflen; - char* buf = (char *) malloc(MAX_VALUE_SIZE); //allocate a buffer for incoming column values + //used to capture the return value from various SQL function calls + SQLRETURN ret; + + //allocate a buffer for incoming column values + char* buf = (char *) malloc(MAX_VALUE_SIZE); //check to make sure malloc succeeded if (buf == NULL) { + objError = Object::New(); + //malloc failed, set an error message objError->Set(String::New("error"), String::New("[node-odbc] Failed Malloc")); objError->Set(String::New("message"), String::New("An attempt to allocate memory failed. This allocation was for a value buffer of incoming recordset values.")); @@ -321,8 +345,9 @@ void Database::UV_AfterQuery(uv_work_t* req) { buf[0] = '\0'; //First thing, let's check if the execution of the query returned any errors (in UV_Query) - if(prep_req->result == SQL_ERROR) - { + if(prep_req->result == SQL_ERROR) { + objError = Object::New(); + errorCount++; char errorMessage[512]; @@ -392,8 +417,10 @@ void Database::UV_AfterQuery(uv_work_t* req) { printf("UV_Query => %s\n", errorSQLState); //printf("UV_Query sql => %s\n", prep_req->sql); } - + if (ret == SQL_ERROR) { + objError = Object::New(); + char errorMessage[512]; char errorSQLState[128]; SQLError(self->m_hEnv, self->m_hDBC, self->m_hStmt,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); @@ -500,7 +527,7 @@ void Database::UV_AfterQuery(uv_work_t* req) { Local args[3]; if (errorCount) { - args[0] = objError; + args[0] = objError; //(objError->IsUndefined()) ? Undefined() : ; } else { args[0] = Local::New(Null()); @@ -543,12 +570,12 @@ void Database::UV_Query(uv_work_t* req) { if(prep_req->dbo->m_hStmt) { - uv_mutex_lock(&Database::g_odbcMutex); - + uv_mutex_lock(&Database::g_odbcMutex); + SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); SQLAllocHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hDBC, &prep_req->dbo->m_hStmt ); - - uv_mutex_unlock(&Database::g_odbcMutex); + + uv_mutex_unlock(&Database::g_odbcMutex); } //check to see if should excute a direct or a parameter bound query @@ -720,8 +747,8 @@ Handle Database::Query(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Query, UV_AfterQuery); dbo->Ref(); - scope.Close(Undefined()); - return Undefined(); + + return scope.Close(Undefined()); } void Database::UV_Tables(uv_work_t* req) { @@ -743,7 +770,8 @@ void Database::UV_Tables(uv_work_t* req) { (SQLCHAR *) prep_req->type, SQL_NTS ); - prep_req->result = ret; // this will be checked later in UV_AfterQuery + // this will be checked later in UV_AfterQuery + prep_req->result = ret; } Handle Database::Tables(const Arguments& args) { @@ -798,8 +826,8 @@ Handle Database::Tables(const Arguments& args) { uv_queue_work(uv_default_loop(), work_req, UV_Tables, UV_AfterQuery); dbo->Ref(); - scope.Close(Undefined()); - return Undefined(); + + return scope.Close(Undefined()); } void Database::UV_Columns(uv_work_t* req) { @@ -819,7 +847,8 @@ void Database::UV_Columns(uv_work_t* req) { (SQLCHAR *) prep_req->column, SQL_NTS ); - prep_req->result = ret; // this will be checked later in UV_AfterQuery + // this will be checked later in UV_AfterQuery + prep_req->result = ret; } Handle Database::Columns(const Arguments& args) { From 11938f88aaa79113d1e2730c350aa5e5c54ef50d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Oct 2012 17:58:24 -0400 Subject: [PATCH 050/511] Rearrange the order of attributes in the error object --- src/Database.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 567d4b68..998c367b 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -92,10 +92,10 @@ void Database::UV_AfterOpen(uv_work_t* req) { SQLINTEGER i = 0; SQLINTEGER native; - char errorSQLState[ 7 ]; - char errorMessage[256]; SQLSMALLINT len; SQLRETURN ret; + char errorSQLState[7]; + char errorMessage[256]; do { ret = SQLGetDiagRec( SQL_HANDLE_DBC, @@ -110,10 +110,10 @@ void Database::UV_AfterOpen(uv_work_t* req) { if (SQL_SUCCEEDED(ret)) { Local objError = Object::New(); - objError->Set(String::New("state"), String::New(errorSQLState)); objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); objError->Set(String::New("message"), String::New(errorMessage)); - + objError->Set(String::New("state"), String::New(errorSQLState)); + argv[0] = objError; } } while( ret == SQL_SUCCESS ); From de8e179dea4ced48d7a52c10f6cb66383cd65e05 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Oct 2012 18:02:16 -0400 Subject: [PATCH 051/511] Remove index.js in lieu of specifying `"main" : "odbc.js"` in package.json --- index.js | 2 -- package.json | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 index.js diff --git a/index.js b/index.js deleted file mode 100644 index 601fc3ce..00000000 --- a/index.js +++ /dev/null @@ -1,2 +0,0 @@ -module.exports = require('./odbc.js'); - diff --git a/package.json b/package.json index 805cdebe..5b593f25 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "odbc", "description": "unixodbc bindings for node", "version": "0.4.3", + "main": "odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { "type": "git", From 2576d838218818ff5dc67be220c49341ed3c44a1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Oct 2012 18:06:14 -0400 Subject: [PATCH 052/511] Use the external bindings module to resolve the path of odbc_bindings; remove odbc_bindings.node link --- odbc.js | 12 +----------- odbc_bindings.node | 1 - package.json | 3 +++ 3 files changed, 4 insertions(+), 12 deletions(-) delete mode 120000 odbc_bindings.node diff --git a/odbc.js b/odbc.js index 9d167d77..35971851 100644 --- a/odbc.js +++ b/odbc.js @@ -14,17 +14,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -var odbc; - -try { - odbc = require("./odbc_bindings"); -} catch (e) { - try { - odbc = require("./build/default/odbc_bindings"); - } catch (e) { - odbc = require("./build/Release/odbc_bindings"); - } -} +var odbc = require("bindings")("odbc_bindings"); exports.debug = false; exports.Database = Database; diff --git a/odbc_bindings.node b/odbc_bindings.node deleted file mode 120000 index 01a8ba84..00000000 --- a/odbc_bindings.node +++ /dev/null @@ -1 +0,0 @@ -build/Release/odbc_bindings.node \ No newline at end of file diff --git a/package.json b/package.json index 5b593f25..de11ab70 100644 --- a/package.json +++ b/package.json @@ -17,5 +17,8 @@ }, "scripts": { "preinstall": "node-gyp configure build" + }, + "dependencies": { + "bindings": "~1.0.0" } } From 243b995b3a6c036223fc0b435be3843bc34b74ce Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Oct 2012 18:10:59 -0400 Subject: [PATCH 053/511] remove build.sh; just use `node-gyp configure build` --- build.sh | 3 --- 1 file changed, 3 deletions(-) delete mode 100755 build.sh diff --git a/build.sh b/build.sh deleted file mode 100755 index 0ea79514..00000000 --- a/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -rm -rf build -node-waf configure build \ No newline at end of file From a4036f6034dd01fc327ee91552250fc97b969227 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 26 Oct 2012 14:08:03 -0400 Subject: [PATCH 054/511] Create GetColumns() GetColumnValue() functions and use them --- src/Database.cpp | 202 ++++++++++++++++++++++++----------------------- src/Database.h | 16 ++-- 2 files changed, 114 insertions(+), 104 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 998c367b..55d256c9 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -170,8 +170,6 @@ void Database::UV_Open(uv_work_t* req) { if( ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ) { ret = SQLAllocStmt( self->m_hDBC, &self->m_hStmt ); - if (ret != SQL_SUCCESS) printf("not connected\n"); - ret = SQLGetFunctions( self->m_hDBC, SQL_API_SQLMORERESULTS, &self->canHaveMoreResults); @@ -290,7 +288,6 @@ Handle Database::Close(const Arguments& args) { //allocate a buffer for incoming column values void Database::UV_AfterQuery(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); - struct tm timeInfo = { 0 }; //used for processing date/time datatypes HandleScope scope; @@ -309,9 +306,6 @@ void Database::UV_AfterQuery(uv_work_t* req) { //used to keep track of the number of errors that have been found short errorCount = 0; - //used as a place holder for the length of column names - SQLSMALLINT buflen; - //used to capture the return value from various SQL function calls SQLRETURN ret; @@ -366,43 +360,21 @@ void Database::UV_AfterQuery(uv_work_t* req) { Local args[1]; args[0] = objError; prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); - //self->Emit(String::New("error"), 1, args); goto cleanupshutdown; } //loop through all result sets do { - colCount = 0; //always reset colCount for the current result set to 0; - - SQLNumResultCols(self->m_hStmt, &colCount); - Column *columns = new Column[colCount]; - Local rows = Array::New(); - + + // retrieve and store column attributes to build the row object + Column *columns = GetColumns(self->m_hStmt, &colCount); + if (colCount > 0) { - // retrieve and store column attributes to build the row object - for(int i = 0; i < colCount; i++) - { - columns[i].name = new unsigned char[MAX_FIELD_SIZE]; - - //set the first byte of name to \0 instead of memsetting the entire buffer - columns[i].name[0] = '\n'; - - //get the column name - ret = SQLColAttribute(self->m_hStmt, (SQLUSMALLINT)i+1, SQL_DESC_LABEL, columns[i].name, (SQLSMALLINT)MAX_FIELD_SIZE, (SQLSMALLINT *)&buflen, NULL); - - //store the len attribute - columns[i].len = buflen; - - //get the column type and store it directly in column[i].type - ret = SQLColAttribute( self->m_hStmt, (SQLUSMALLINT)i+1, SQL_COLUMN_TYPE, NULL, 0, NULL, &columns[i].type ); - } - int count = 0; // i dont think odbc will tell how many rows are returned, loop until out... - while(true) - { + while(true) { Local tuple = Object::New(); ret = SQLFetch(self->m_hStmt); @@ -412,10 +384,8 @@ void Database::UV_AfterQuery(uv_work_t* req) { char errorSQLState[128]; SQLError(self->m_hEnv, self->m_hDBC, self->m_hStmt,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); - //printf("UV_Query ret => %i\n", ret); printf("UV_Query => %s\n", errorMessage); printf("UV_Query => %s\n", errorSQLState); - //printf("UV_Query sql => %s\n", prep_req->sql); } if (ret == SQL_ERROR) { @@ -442,73 +412,17 @@ void Database::UV_AfterQuery(uv_work_t* req) { if (ret == SQL_NO_DATA) { break; } - - for(int i = 0; i < colCount; i++) - { - SQLLEN len; - - // SQLGetData can supposedly return multiple chunks, need to do this to retrieve large fields - int ret = SQLGetData(self->m_hStmt, i+1, SQL_CHAR, (char *) buf, MAX_VALUE_SIZE-1, (SQLLEN *) &len); - - //printf("%s %i\n", columns[i].name, columns[i].type); - - if(ret == SQL_NULL_DATA || len < 0) - { - tuple->Set(String::New((const char *)columns[i].name), Null()); - } - else - { - switch (columns[i].type) { - case SQL_NUMERIC : - tuple->Set(String::New((const char *)columns[i].name), Number::New(atof(buf))); - break; - case SQL_DECIMAL : - tuple->Set(String::New((const char *)columns[i].name), Number::New(atof(buf))); - break; - case SQL_INTEGER : - tuple->Set(String::New((const char *)columns[i].name), Number::New(atof(buf))); - break; - case SQL_SMALLINT : - tuple->Set(String::New((const char *)columns[i].name), Number::New(atof(buf))); - break; - case SQL_BIGINT : - tuple->Set(String::New((const char *)columns[i].name), Number::New(atof(buf))); - break; - case SQL_FLOAT : - tuple->Set(String::New((const char *)columns[i].name), Number::New(atof(buf))); - break; - case SQL_REAL : - tuple->Set(String::New((const char *)columns[i].name), Number::New(atof(buf))); - break; - case SQL_DOUBLE : - tuple->Set(String::New((const char *)columns[i].name), Number::New(atof(buf))); - break; - case SQL_DATETIME : - case SQL_TIMESTAMP : - //I am not sure if this is locale-safe or cross database safe, but it works for me on MSSQL - strptime(buf, "%Y-%m-%d %H:%M:%S", &timeInfo); - timeInfo.tm_isdst = -1; //a negative value means that mktime() should (use timezone information and system - //databases to) attempt to determine whether DST is in effect at the specified time. - tuple->Set(String::New((const char *)columns[i].name), Date::New(double(mktime(&timeInfo)) * 1000)); - - break; - case SQL_BIT : - //again, i'm not sure if this is cross database safe, but it works for MSSQL - tuple->Set(String::New((const char *)columns[i].name), Boolean::New( ( *buf == '0') ? false : true )); - break; - default : - tuple->Set(String::New((const char *)columns[i].name), String::New(buf)); - break; - } - } + + for(int i = 0; i < colCount; i++) { + tuple->Set( String::New((const char *) columns[i].name), + GetColumnValue( self->m_hStmt, columns[i], buf, MAX_VALUE_SIZE - 1)); } rows->Set(Integer::New(count), tuple); count++; } - for(int i = 0; i < colCount; i++) - { + for(int i = 0; i < colCount; i++) { delete [] columns[i].name; } @@ -534,7 +448,8 @@ void Database::UV_AfterQuery(uv_work_t* req) { } args[1] = rows; - args[2] = Local::New(( ret == SQL_SUCCESS ) ? True() : False() ); //true or false, are there more result sets to follow this emit? + //true or false, are there more result sets to follow this emit? + args[2] = Local::New(( ret == SQL_SUCCESS ) ? True() : False() ); prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); } @@ -562,6 +477,97 @@ void Database::UV_AfterQuery(uv_work_t* req) { scope.Close(Undefined()); } +Column* Database::GetColumns(SQLHSTMT hStmt, short* colCount) { + SQLRETURN ret; + SQLSMALLINT buflen; + + //always reset colCount for the current result set to 0; + *colCount = 0; + + ret = SQLNumResultCols(hStmt, colCount); + Column *columns = new Column[*colCount]; + + for (int i = 0; i < *colCount; i++) { + //save the index number of this column + columns[i].index = i + 1; + columns[i].name = new unsigned char[MAX_FIELD_SIZE]; + + //set the first byte of name to \0 instead of memsetting the entire buffer + columns[i].name[0] = '\n'; + + //get the column name + ret = SQLColAttribute( hStmt, + columns[i].index, + SQL_DESC_LABEL, + columns[i].name, + (SQLSMALLINT) MAX_FIELD_SIZE, + (SQLSMALLINT *) &buflen, + NULL); + + //store the len attribute + columns[i].len = buflen; + + //get the column type and store it directly in column[i].type + ret = SQLColAttribute( hStmt, + columns[i].index, + SQL_COLUMN_TYPE, + NULL, + 0, + NULL, + &columns[i].type); + } + + return columns; +} + +Handle Database::GetColumnValue(SQLHSTMT hStmt, Column column, char* buffer, int bufferLength) { + SQLLEN len; + struct tm timeInfo = { 0 }; + + //reset the buffer + buffer[0] = '\n'; + + //TODO: SQLGetData can supposedly return multiple chunks, need to do this to retrieve large fields + int ret = SQLGetData( hStmt, + column.index, + SQL_CHAR, + (char *) buffer, + bufferLength, + (SQLLEN *) &len); + + if(ret == SQL_NULL_DATA || len < 0) { + return Null(); + } + else { + switch (column.type) { + case SQL_NUMERIC : + case SQL_DECIMAL : + case SQL_INTEGER : + case SQL_SMALLINT : + case SQL_BIGINT : + case SQL_FLOAT : + case SQL_REAL : + case SQL_DOUBLE : + return Number::New(atof(buffer)); + case SQL_DATETIME : + case SQL_TIMESTAMP : + //I am not sure if this is locale-safe or cross database safe, but it works for me on MSSQL + strptime(buffer, "%Y-%m-%d %H:%M:%S", &timeInfo); + + //a negative value means that mktime() should use timezone information and system + //databases to attempt to determine whether DST is in effect at the specified time. + timeInfo.tm_isdst = -1; + + return Date::New(double(mktime(&timeInfo)) * 1000); + case SQL_BIT : + //again, i'm not sure if this is cross database safe, but it works for MSSQL + return Boolean::New(( *buffer == '0') ? false : true ); + default : + return String::New(buffer); + } + } +} + void Database::UV_Query(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); @@ -913,4 +919,4 @@ extern "C" void init (v8::Handle target) { Database::Init(target); } -NODE_MODULE(odbc_bindings, init) \ No newline at end of file +NODE_MODULE(odbc_bindings, init) diff --git a/src/Database.h b/src/Database.h index 249400b6..ebff8e53 100644 --- a/src/Database.h +++ b/src/Database.h @@ -28,6 +28,13 @@ using namespace v8; using namespace node; +typedef struct { + unsigned char *name; + unsigned int len; + SQLLEN type; + SQLUSMALLINT index; +} Column; + class Database : public node::ObjectWrap { public: static Persistent constructor_template; @@ -60,7 +67,9 @@ class Database : public node::ObjectWrap { static void UV_Columns(uv_work_t* req); static Handle Columns(const Arguments& args); - static void WatcherCallback(uv_async_t *w, int revents); + static void WatcherCallback(uv_async_t* w, int revents); + static Column* GetColumns(SQLHSTMT hStmt, short* colCount); + static Handle GetColumnValue(SQLHSTMT hStmt, Column column, char* buffer, int bufferLength); Database *self(void) { return this; } @@ -84,11 +93,6 @@ struct close_request { int result; }; -typedef struct { - unsigned char *name; - unsigned int len; - SQLLEN type; -} Column; typedef struct { SQLSMALLINT c_type; From bfeeca7f642f072dc3a99403488a2ba934742bef Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 26 Oct 2012 18:28:32 -0400 Subject: [PATCH 055/511] Added mode setter/getter and code reorganizing --- src/Database.cpp | 587 +++++++++++++++++++++++++++++------------------ src/Database.h | 17 +- 2 files changed, 384 insertions(+), 220 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 55d256c9..b2508c5e 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -26,10 +26,6 @@ #include "strptime.h" #endif - -#define MAX_FIELD_SIZE 1024 -#define MAX_VALUE_SIZE 1048576 - using namespace v8; using namespace node; @@ -46,7 +42,11 @@ void Database::Init(v8::Handle target) { constructor_template->SetClassName(String::NewSymbol("Database")); // Reserve space for one Handle - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + Local instance_template = constructor_template->InstanceTemplate(); + instance_template->SetInternalFieldCount(1); + + // Properties + instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchOpen", Open); @@ -56,12 +56,15 @@ void Database::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchColumns", Columns); // Attach the Database Constructor to the target object - target->Set(v8::String::NewSymbol("Database"), constructor_template->GetFunction()); + target->Set( v8::String::NewSymbol("Database"), + constructor_template->GetFunction()); scope.Close(Undefined()); // Initialize uv_async so that we can prevent node from exiting - uv_async_init(uv_default_loop(), &Database::g_async, Database::WatcherCallback); + uv_async_init( uv_default_loop(), + &Database::g_async, + Database::WatcherCallback); // Initialize the cross platform mutex provided by libuv uv_mutex_init(&Database::g_odbcMutex); @@ -70,7 +73,9 @@ void Database::Init(v8::Handle target) { Handle Database::New(const Arguments& args) { HandleScope scope; Database* dbo = new Database(); + dbo->Wrap(args.This()); + dbo->mode = 1; return scope.Close(args.This()); } @@ -79,6 +84,28 @@ void Database::WatcherCallback(uv_async_t *w, int revents) { //i don't know if we need to do anything here } +Handle Database::ModeGetter(Local property, const AccessorInfo &info) { + HandleScope scope; + + Database *obj = ObjectWrap::Unwrap(info.Holder()); + + if (obj->mode > 0) { + return scope.Close(Integer::New(obj->mode)); + } else { + return Undefined(); + } +} + +void Database::ModeSetter(Local property, Local value, const AccessorInfo &info) { + HandleScope scope; + + Database *obj = ObjectWrap::Unwrap(info.Holder()); + + if (value->IsNumber()) { + obj->mode = value->Int32Value(); + } +} + void Database::UV_AfterOpen(uv_work_t* req) { HandleScope scope; open_request* open_req = (open_request *)(req->data); @@ -264,7 +291,7 @@ Handle Database::Close(const Arguments& args) { HandleScope scope; REQ_FUN_ARG(0, cb); -//allocate a buffer for incoming column values + Database* dbo = ObjectWrap::Unwrap(args.This()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); close_request* close_req = (close_request *) (calloc(1, sizeof(close_request))); @@ -285,7 +312,7 @@ Handle Database::Close(const Arguments& args) { return scope.Close(Undefined()); } -//allocate a buffer for incoming column values + void Database::UV_AfterQuery(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); @@ -294,7 +321,8 @@ void Database::UV_AfterQuery(uv_work_t* req) { //an easy reference to the Database object Database* self = prep_req->dbo->self(); - //our error object which we will use if we discover errors while processing the result set + //our error object which we will use if we discover errors while processing + //the result set Local objError; //used to keep track of the number of columns received in a result set @@ -317,145 +345,189 @@ void Database::UV_AfterQuery(uv_work_t* req) { objError = Object::New(); //malloc failed, set an error message - objError->Set(String::New("error"), String::New("[node-odbc] Failed Malloc")); - objError->Set(String::New("message"), String::New("An attempt to allocate memory failed. This allocation was for a value buffer of incoming recordset values.")); - - //emit an error event immidiately. + objError->Set(String::New("error"), + String::New("[node-odbc] Failed Malloc")); + + objError->Set(String::New("message"), + String::New("An attempt to allocate memory failed. This " + "allocation was for a value buffer of incoming " + "recordset values.")); + + //Prepare arguments for the callback Local args[3]; args[0] = objError; args[1] = Local::New(Null()); args[2] = Local::New(False()); - - //emit an error event + + //Callback immidiately prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); - + //emit a result event goto cleanupshutdown; } - //else { - //malloc succeeded so let's continue + + //First thing, let's check if the execution of the query returned any errors + //in UV_Query + if(prep_req->result == SQL_ERROR) { + objError = Object::New(); + + errorCount++; - //set the first byte of the buffer to \0 instead of memsetting the entire buffer to 0 - buf[0] = '\0'; + char errorMessage[512]; + char errorSQLState[128]; - //First thing, let's check if the execution of the query returned any errors (in UV_Query) - if(prep_req->result == SQL_ERROR) { - objError = Object::New(); - - errorCount++; - - char errorMessage[512]; - char errorSQLState[128]; - SQLError(self->m_hEnv, self->m_hDBC, self->m_hStmt,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); - objError->Set(String::New("message"), String::New(errorMessage)); - - //only set the query value of the object if we actually have a query - if (prep_req->sql != NULL) { - objError->Set(String::New("query"), String::New(prep_req->sql)); - } - - //emit an error event immidiately. - Local args[1]; - args[0] = objError; - prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); - goto cleanupshutdown; + SQLError( self->m_hEnv, + self->m_hDBC, + self->m_hStmt, + (SQLCHAR *) errorSQLState, + NULL, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + NULL); + + objError->Set(String::New("state"), String::New(errorSQLState)); + objError->Set(String::New("error"), String::New("[node-odbc] Error in " + "SQLExecDirect or SQLExecute")); + objError->Set(String::New("message"), String::New(errorMessage)); + + //only set the query value of the object if we actually have a query + if (prep_req->sql != NULL) { + objError->Set(String::New("query"), String::New(prep_req->sql)); } - //loop through all result sets - do { - Local rows = Array::New(); + //emit an error event immidiately. + Local args[1]; + args[0] = objError; + prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); + + goto cleanupshutdown; + } + + //Loop through all result sets + do { + Local rows = Array::New(); - // retrieve and store column attributes to build the row object - Column *columns = GetColumns(self->m_hStmt, &colCount); + //Retrieve and store all columns and their attributes + Column *columns = GetColumns(self->m_hStmt, &colCount); - if (colCount > 0) { - int count = 0; + if (colCount > 0) { + int count = 0; + + //I dont think odbc will tell how many rows are returned, loop until out + while(true) { - // i dont think odbc will tell how many rows are returned, loop until out... - while(true) { - Local tuple = Object::New(); - ret = SQLFetch(self->m_hStmt); - - //TODO: Do something to enable/disable dumping these info messages to the console. - if (ret == SQL_SUCCESS_WITH_INFO ) { - char errorMessage[512]; - char errorSQLState[128]; - SQLError(self->m_hEnv, self->m_hDBC, self->m_hStmt,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); - - printf("UV_Query => %s\n", errorMessage); - printf("UV_Query => %s\n", errorSQLState); - } + ret = SQLFetch(self->m_hStmt); + + //TODO: Do something to enable/disable dumping these info messages to + //the console. + if (ret == SQL_SUCCESS_WITH_INFO ) { + char errorMessage[512]; + char errorSQLState[128]; + + SQLError( self->m_hEnv, + self->m_hDBC, + self->m_hStmt, + (SQLCHAR *) errorSQLState, + NULL, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + NULL); + + printf("UV_Query => %s\n", errorMessage); + printf("UV_Query => %s\n", errorSQLState); + } - if (ret == SQL_ERROR) { - objError = Object::New(); - - char errorMessage[512]; - char errorSQLState[128]; - SQLError(self->m_hEnv, self->m_hDBC, self->m_hStmt,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); - - errorCount++; - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); - objError->Set(String::New("message"), String::New(errorMessage)); - objError->Set(String::New("query"), String::New(prep_req->sql)); - - //emit an error event immidiately. - Local args[1]; - args[0] = objError; - prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); - - break; - } - - if (ret == SQL_NO_DATA) { - break; - } + if (ret == SQL_ERROR) { + objError = Object::New(); - for(int i = 0; i < colCount; i++) { - tuple->Set( String::New((const char *) columns[i].name), - GetColumnValue( self->m_hStmt, columns[i], buf, MAX_VALUE_SIZE - 1)); - } + char errorMessage[512]; + char errorSQLState[128]; + + SQLError( self->m_hEnv, + self->m_hDBC, + self->m_hStmt, + (SQLCHAR *) errorSQLState, + NULL, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + NULL); + + errorCount++; + + objError->Set(String::New("state"), String::New(errorSQLState)); + objError->Set(String::New("error"), + String::New("[node-odbc] Error in SQLFetch")); + objError->Set(String::New("message"), String::New(errorMessage)); + objError->Set(String::New("query"), String::New(prep_req->sql)); - rows->Set(Integer::New(count), tuple); - count++; + //emit an error event immidiately. + Local args[1]; + args[0] = objError; + prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); + + break; } - for(int i = 0; i < colCount; i++) { - delete [] columns[i].name; + if (ret == SQL_NO_DATA) { + break; } - - delete [] columns; - } - - //move to the next result set - ret = SQLMoreResults( self->m_hStmt ); - - //Only trigger an emit if there are columns OR if this is the last result and none others have been emitted - //odbc will process individual statments like select @something = 1 as a recordset even though it doesn't have - //any columns. We don't want to emit those unless there are actually columns - if (colCount > 0 || ( ret != SQL_SUCCESS && emitCount == 0 )) { - emitCount++; - - Local args[3]; - if (errorCount) { - args[0] = objError; //(objError->IsUndefined()) ? Undefined() : ; + if (self->mode == MODE_CALLBACK_FOR_EACH) { + Handle args[2]; + + args[0] = Null(); + args[1] = GetRecordTuple( self->m_hStmt, + columns, + &colCount, + buf, + MAX_VALUE_SIZE - 1); + + prep_req->cb->Call(Context::GetCurrent()->Global(), 2, args); } else { - args[0] = Local::New(Null()); + rows->Set( Integer::New(count), + GetRecordTuple( self->m_hStmt, + columns, + &colCount, + buf, + MAX_VALUE_SIZE - 1)); } - args[1] = rows; - //true or false, are there more result sets to follow this emit? - args[2] = Local::New(( ret == SQL_SUCCESS ) ? True() : False() ); - - prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); + count++; + } + + FreeColumns(columns, &colCount); + } + + //move to the next result set + ret = SQLMoreResults( self->m_hStmt ); + + //Only trigger an emit if there are columns OR if this is the last result + //and none others have been emitted odbc will process individual statments + //like select @something = 1 as a recordset even though it doesn't have any + //columns. We don't want to emit those unless there are actually columns + if (colCount > 0 || ( ret != SQL_SUCCESS && emitCount == 0 )) { + emitCount++; + + Local args[3]; + + if (errorCount) { + args[0] = objError; + } + else { + args[0] = Local::New(Null()); } + + args[1] = rows; + + //true or false, are there more result sets to follow this emit? + args[2] = Local::New((ret == SQL_SUCCESS) ? True() : False()); + + prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); } - while ( self->canHaveMoreResults && ret == SQL_SUCCESS ); - //} //end of malloc check + } + while ( self->canHaveMoreResults && ret == SQL_SUCCESS ); + cleanupshutdown: TryCatch try_catch; @@ -474,98 +546,8 @@ void Database::UV_AfterQuery(uv_work_t* req) { free(prep_req->type); free(prep_req); free(req); - scope.Close(Undefined()); -} - -Column* Database::GetColumns(SQLHSTMT hStmt, short* colCount) { - SQLRETURN ret; - SQLSMALLINT buflen; - - //always reset colCount for the current result set to 0; - *colCount = 0; - - ret = SQLNumResultCols(hStmt, colCount); - Column *columns = new Column[*colCount]; - - for (int i = 0; i < *colCount; i++) { - //save the index number of this column - columns[i].index = i + 1; - columns[i].name = new unsigned char[MAX_FIELD_SIZE]; - - //set the first byte of name to \0 instead of memsetting the entire buffer - columns[i].name[0] = '\n'; - - //get the column name - ret = SQLColAttribute( hStmt, - columns[i].index, - SQL_DESC_LABEL, - columns[i].name, - (SQLSMALLINT) MAX_FIELD_SIZE, - (SQLSMALLINT *) &buflen, - NULL); - - //store the len attribute - columns[i].len = buflen; - - //get the column type and store it directly in column[i].type - ret = SQLColAttribute( hStmt, - columns[i].index, - SQL_COLUMN_TYPE, - NULL, - 0, - NULL, - &columns[i].type); - } - - return columns; -} - -Handle Database::GetColumnValue(SQLHSTMT hStmt, Column column, char* buffer, int bufferLength) { - SQLLEN len; - struct tm timeInfo = { 0 }; - - //reset the buffer - buffer[0] = '\n'; - - //TODO: SQLGetData can supposedly return multiple chunks, need to do this to retrieve large fields - int ret = SQLGetData( hStmt, - column.index, - SQL_CHAR, - (char *) buffer, - bufferLength, - (SQLLEN *) &len); - if(ret == SQL_NULL_DATA || len < 0) { - return Null(); - } - else { - switch (column.type) { - case SQL_NUMERIC : - case SQL_DECIMAL : - case SQL_INTEGER : - case SQL_SMALLINT : - case SQL_BIGINT : - case SQL_FLOAT : - case SQL_REAL : - case SQL_DOUBLE : - return Number::New(atof(buffer)); - case SQL_DATETIME : - case SQL_TIMESTAMP : - //I am not sure if this is locale-safe or cross database safe, but it works for me on MSSQL - strptime(buffer, "%Y-%m-%d %H:%M:%S", &timeInfo); - - //a negative value means that mktime() should use timezone information and system - //databases to attempt to determine whether DST is in effect at the specified time. - timeInfo.tm_isdst = -1; - - return Date::New(double(mktime(&timeInfo)) * 1000); - case SQL_BIT : - //again, i'm not sure if this is cross database safe, but it works for MSSQL - return Boolean::New(( *buffer == '0') ? false : true ); - default : - return String::New(buffer); - } - } + scope.Close(Undefined()); } void Database::UV_Query(uv_work_t* req) { @@ -578,8 +560,13 @@ void Database::UV_Query(uv_work_t* req) { { uv_mutex_lock(&Database::g_odbcMutex); + //free the previously used handle SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); - SQLAllocHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hDBC, &prep_req->dbo->m_hStmt ); + + //allocate a new handle + SQLAllocHandle( SQL_HANDLE_STMT, + prep_req->dbo->m_hDBC, + &prep_req->dbo->m_hStmt ); uv_mutex_unlock(&Database::g_odbcMutex); } @@ -588,19 +575,34 @@ void Database::UV_Query(uv_work_t* req) { if (!prep_req->paramCount) { // execute the query directly - ret = SQLExecDirect( prep_req->dbo->m_hStmt,(SQLCHAR *)prep_req->sql, strlen(prep_req->sql) ); + ret = SQLExecDirect( prep_req->dbo->m_hStmt, + (SQLCHAR *) prep_req->sql, + strlen(prep_req->sql)); } else { // prepare statement, bind parameters and execute statement - ret = SQLPrepare(prep_req->dbo->m_hStmt, (SQLCHAR *)prep_req->sql, strlen(prep_req->sql)); + ret = SQLPrepare( prep_req->dbo->m_hStmt, + (SQLCHAR *) prep_req->sql, + strlen(prep_req->sql)); + if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { for (int i = 0; i < prep_req->paramCount; i++) { prm = prep_req->params[i]; - ret = SQLBindParameter(prep_req->dbo->m_hStmt, i + 1, SQL_PARAM_INPUT, prm.c_type, prm.type, prm.size, 0, prm.buffer, prm.buffer_length, &prm.length); + ret = SQLBindParameter( prep_req->dbo->m_hStmt, + i + 1, + SQL_PARAM_INPUT, + prm.c_type, + prm.type, + prm.size, + 0, + prm.buffer, + prm.buffer_length, + &prm.length); + if (ret == SQL_ERROR) {break;} } @@ -610,7 +612,6 @@ void Database::UV_Query(uv_work_t* req) { } // free parameters - // for (int i = 0; i < prep_req->paramCount; i++) { if (prm = prep_req->params[i], prm.buffer != NULL) @@ -627,7 +628,8 @@ void Database::UV_Query(uv_work_t* req) { free(prep_req->params); } - prep_req->result = ret; // this will be checked later in UV_AfterQuery + // this will be checked later in UV_AfterQuery + prep_req->result = ret; } Handle Database::Query(const Arguments& args) { @@ -913,6 +915,155 @@ Handle Database::Columns(const Arguments& args) { return scope.Close(Undefined()); } +Column* Database::GetColumns(SQLHSTMT hStmt, short* colCount) { + SQLRETURN ret; + SQLSMALLINT buflen; + + //always reset colCount for the current result set to 0; + *colCount = 0; + + //get the number of columns in the result set + ret = SQLNumResultCols(hStmt, colCount); + + if (!SQL_SUCCEEDED(ret)) { + return new Column[0]; + } + + Column *columns = new Column[*colCount]; + + for (int i = 0; i < *colCount; i++) { + //save the index number of this column + columns[i].index = i + 1; + columns[i].name = new unsigned char[MAX_FIELD_SIZE]; + + //set the first byte of name to \0 instead of memsetting the entire buffer + columns[i].name[0] = '\n'; + + //get the column name + ret = SQLColAttribute( hStmt, + columns[i].index, + SQL_DESC_LABEL, + columns[i].name, + (SQLSMALLINT) MAX_FIELD_SIZE, + (SQLSMALLINT *) &buflen, + NULL); + + //store the len attribute + columns[i].len = buflen; + + //get the column type and store it directly in column[i].type + ret = SQLColAttribute( hStmt, + columns[i].index, + SQL_COLUMN_TYPE, + NULL, + 0, + NULL, + &columns[i].type); + } + + return columns; +} + +void Database::FreeColumns(Column* columns, short* colCount) { + for(int i = 0; i < *colCount; i++) { + delete [] columns[i].name; + } + + delete [] columns; +} + +Handle Database::GetColumnValue( SQLHSTMT hStmt, Column column, + char* buffer, int bufferLength) { + //HandleScope scope; + SQLLEN len; + + struct tm timeInfo = { 0 }; + + //reset the buffer + buffer[0] = '\n'; + + //TODO: SQLGetData can supposedly return multiple chunks, need to do this to + //retrieve large fields + int ret = SQLGetData( hStmt, + column.index, + SQL_CHAR, + (char *) buffer, + bufferLength, + (SQLLEN *) &len); + + if(ret == SQL_NULL_DATA || len < 0) { + //return scope.Close(Null()); + return Null(); + } + else { + switch (column.type) { + case SQL_NUMERIC : + case SQL_DECIMAL : + case SQL_INTEGER : + case SQL_SMALLINT : + case SQL_BIGINT : + case SQL_FLOAT : + case SQL_REAL : + case SQL_DOUBLE : + //return scope.Close(Number::New(atof(buffer))); + return Number::New(atof(buffer)); + case SQL_DATETIME : + case SQL_TIMESTAMP : + //I am not sure if this is locale-safe or cross database safe, but it + //works for me on MSSQL + strptime(buffer, "%Y-%m-%d %H:%M:%S", &timeInfo); + + //a negative value means that mktime() should use timezone information + //and system databases to attempt to determine whether DST is in effect + //at the specified time. + timeInfo.tm_isdst = -1; + + //return scope.Close(Date::New(double(mktime(&timeInfo)) * 1000)); + return Date::New(double(mktime(&timeInfo)) * 1000); + case SQL_BIT : + //again, i'm not sure if this is cross database safe, but it works for + //MSSQL + //return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); + return Boolean::New(( *buffer == '0') ? false : true ); + default : + //return scope.Close(String::New(buffer)); + return String::New(buffer); + } + } +} + +Handle Database::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, + short* colCount, char* buffer, + int bufferLength) { + //HandleScope scope; + + Local tuple = Object::New(); + + for(int i = 0; i < *colCount; i++) { + tuple->Set( String::New((const char *) columns[i].name), + GetColumnValue( hStmt, columns[i], buffer, bufferLength)); + } + + return tuple; + //return scope.Close(tuple); +} + +Handle Database::GetRecordArray ( SQLHSTMT hStmt, Column* columns, + short* colCount, char* buffer, + int bufferLength) { + //HandleScope scope; + + Local array = Array::New(); + + for(int i = 0; i < *colCount; i++) { + array->Set( Integer::New(i), + GetColumnValue( hStmt, columns[i], buffer, bufferLength)); + } + + return array; + //return scope.Close(array); +} + Persistent Database::constructor_template; extern "C" void init (v8::Handle target) { diff --git a/src/Database.h b/src/Database.h index ebff8e53..954f2805 100644 --- a/src/Database.h +++ b/src/Database.h @@ -28,6 +28,12 @@ using namespace v8; using namespace node; +#define MAX_FIELD_SIZE 1024 +#define MAX_VALUE_SIZE 1048576 + +#define MODE_COLLECT_AND_CALLBACK 1 +#define MODE_CALLBACK_FOR_EACH 2 + typedef struct { unsigned char *name; unsigned int len; @@ -49,6 +55,9 @@ class Database : public node::ObjectWrap { static Handle New(const Arguments& args); + static Handle ModeGetter(Local property, const AccessorInfo &info); + static void ModeSetter(Local property, Local value, const AccessorInfo &info); + static void UV_AfterOpen(uv_work_t* req); static void UV_Open(uv_work_t* req); static Handle Open(const Arguments& args); @@ -69,8 +78,11 @@ class Database : public node::ObjectWrap { static void WatcherCallback(uv_async_t* w, int revents); static Column* GetColumns(SQLHSTMT hStmt, short* colCount); - static Handle GetColumnValue(SQLHSTMT hStmt, Column column, char* buffer, int bufferLength); - + static void FreeColumns(Column* columns, short* colCount); + static Handle GetColumnValue(SQLHSTMT hStmt, Column column, char* buffer, int bufferLength); + static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); + static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); + Database *self(void) { return this; } protected: @@ -78,6 +90,7 @@ class Database : public node::ObjectWrap { HDBC m_hDBC; HSTMT m_hStmt; SQLUSMALLINT canHaveMoreResults; + int mode; }; struct open_request { From 2bc759ecaccb9570ef0239edbcb6111457916b43 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Oct 2012 12:12:01 -0400 Subject: [PATCH 056/511] rename Database to ODBC along with related files --- binding.gyp | 4 +- src/{Database.cpp => odbc.cpp} | 104 ++++++++++++++++----------------- src/{Database.h => odbc.h} | 14 ++--- 3 files changed, 61 insertions(+), 61 deletions(-) rename src/{Database.cpp => odbc.cpp} (91%) rename src/{Database.h => odbc.h} (97%) diff --git a/binding.gyp b/binding.gyp index 3260852c..304ad8d5 100644 --- a/binding.gyp +++ b/binding.gyp @@ -3,7 +3,7 @@ { 'target_name' : 'odbc_bindings', 'sources' : [ - 'src/Database.cpp' + 'src/odbc.cpp' ], 'conditions' : [ [ 'OS == "linux"', { @@ -19,7 +19,7 @@ [ 'OS=="win"', { 'sources' : [ 'src/strptime.c', - 'src/Database.cpp' + 'src/odbc.cpp' ], 'libraries' : [ '-lodbccp32.lib' diff --git a/src/Database.cpp b/src/odbc.cpp similarity index 91% rename from src/Database.cpp rename to src/odbc.cpp index b2508c5e..b17ca544 100644 --- a/src/Database.cpp +++ b/src/odbc.cpp @@ -21,7 +21,7 @@ #include #include -#include "Database.h" +#include "odbc.h" #ifdef _WIN32 #include "strptime.h" #endif @@ -29,10 +29,10 @@ using namespace v8; using namespace node; -uv_mutex_t Database::g_odbcMutex; -uv_async_t Database::g_async; +uv_mutex_t ODBC::g_odbcMutex; +uv_async_t ODBC::g_async; -void Database::Init(v8::Handle target) { +void ODBC::Init(v8::Handle target) { HandleScope scope; Local t = FunctionTemplate::New(New); @@ -63,16 +63,16 @@ void Database::Init(v8::Handle target) { // Initialize uv_async so that we can prevent node from exiting uv_async_init( uv_default_loop(), - &Database::g_async, - Database::WatcherCallback); + &ODBC::g_async, + ODBC::WatcherCallback); // Initialize the cross platform mutex provided by libuv - uv_mutex_init(&Database::g_odbcMutex); + uv_mutex_init(&ODBC::g_odbcMutex); } -Handle Database::New(const Arguments& args) { +Handle ODBC::New(const Arguments& args) { HandleScope scope; - Database* dbo = new Database(); + ODBC* dbo = new ODBC(); dbo->Wrap(args.This()); dbo->mode = 1; @@ -80,14 +80,14 @@ Handle Database::New(const Arguments& args) { return scope.Close(args.This()); } -void Database::WatcherCallback(uv_async_t *w, int revents) { +void ODBC::WatcherCallback(uv_async_t *w, int revents) { //i don't know if we need to do anything here } -Handle Database::ModeGetter(Local property, const AccessorInfo &info) { +Handle ODBC::ModeGetter(Local property, const AccessorInfo &info) { HandleScope scope; - Database *obj = ObjectWrap::Unwrap(info.Holder()); + ODBC *obj = ObjectWrap::Unwrap(info.Holder()); if (obj->mode > 0) { return scope.Close(Integer::New(obj->mode)); @@ -96,17 +96,17 @@ Handle Database::ModeGetter(Local property, const AccessorInfo &i } } -void Database::ModeSetter(Local property, Local value, const AccessorInfo &info) { +void ODBC::ModeSetter(Local property, Local value, const AccessorInfo &info) { HandleScope scope; - Database *obj = ObjectWrap::Unwrap(info.Holder()); + ODBC *obj = ObjectWrap::Unwrap(info.Holder()); if (value->IsNumber()) { obj->mode = value->Int32Value(); } } -void Database::UV_AfterOpen(uv_work_t* req) { +void ODBC::UV_AfterOpen(uv_work_t* req) { HandleScope scope; open_request* open_req = (open_request *)(req->data); @@ -158,7 +158,7 @@ void Database::UV_AfterOpen(uv_work_t* req) { open_req->cb.Dispose(); #if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_ref((uv_handle_t *)&Database::g_async); + uv_ref((uv_handle_t *)&ODBC::g_async); #else uv_ref(uv_default_loop()); #endif @@ -168,11 +168,11 @@ void Database::UV_AfterOpen(uv_work_t* req) { scope.Close(Undefined()); } -void Database::UV_Open(uv_work_t* req) { +void ODBC::UV_Open(uv_work_t* req) { open_request* open_req = (open_request *)(req->data); - Database* self = open_req->dbo->self(); + ODBC* self = open_req->dbo->self(); - uv_mutex_lock(&Database::g_odbcMutex); + uv_mutex_lock(&ODBC::g_odbcMutex); int ret = SQLAllocEnv( &self->m_hEnv ); @@ -208,17 +208,17 @@ void Database::UV_Open(uv_work_t* req) { } } - uv_mutex_unlock(&Database::g_odbcMutex); + uv_mutex_unlock(&ODBC::g_odbcMutex); open_req->result = ret; } -Handle Database::Open(const Arguments& args) { +Handle ODBC::Open(const Arguments& args) { HandleScope scope; REQ_STR_ARG(0, connection); REQ_FUN_ARG(1, cb); - Database* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.This()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); open_request* open_req = (open_request *) calloc(1, sizeof(open_request) + connection.length()); @@ -240,7 +240,7 @@ Handle Database::Open(const Arguments& args) { return scope.Close(Undefined()); } -void Database::UV_AfterClose(uv_work_t* req) { +void ODBC::UV_AfterClose(uv_work_t* req) { HandleScope scope; close_request* close_req = (close_request *)(req->data); @@ -264,7 +264,7 @@ void Database::UV_AfterClose(uv_work_t* req) { close_req->cb.Dispose(); #if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_unref((uv_handle_t *)&Database::g_async); + uv_unref((uv_handle_t *)&ODBC::g_async); #else uv_unref(uv_default_loop()); #endif @@ -274,25 +274,25 @@ void Database::UV_AfterClose(uv_work_t* req) { scope.Close(Undefined()); } -void Database::UV_Close(uv_work_t* req) { +void ODBC::UV_Close(uv_work_t* req) { close_request* close_req = (close_request *)(req->data); - Database* dbo = close_req->dbo; + ODBC* dbo = close_req->dbo; - uv_mutex_lock(&Database::g_odbcMutex); + uv_mutex_lock(&ODBC::g_odbcMutex); SQLDisconnect(dbo->m_hDBC); SQLFreeHandle(SQL_HANDLE_ENV, dbo->m_hEnv); SQLFreeHandle(SQL_HANDLE_DBC, dbo->m_hDBC); - uv_mutex_unlock(&Database::g_odbcMutex); + uv_mutex_unlock(&ODBC::g_odbcMutex); } -Handle Database::Close(const Arguments& args) { +Handle ODBC::Close(const Arguments& args) { HandleScope scope; REQ_FUN_ARG(0, cb); - Database* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.This()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); close_request* close_req = (close_request *) (calloc(1, sizeof(close_request))); @@ -313,13 +313,13 @@ Handle Database::Close(const Arguments& args) { return scope.Close(Undefined()); } -void Database::UV_AfterQuery(uv_work_t* req) { +void ODBC::UV_AfterQuery(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); HandleScope scope; //an easy reference to the Database object - Database* self = prep_req->dbo->self(); + ODBC* self = prep_req->dbo->self(); //our error object which we will use if we discover errors while processing //the result set @@ -550,7 +550,7 @@ void Database::UV_AfterQuery(uv_work_t* req) { scope.Close(Undefined()); } -void Database::UV_Query(uv_work_t* req) { +void ODBC::UV_Query(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); Parameter prm; @@ -558,7 +558,7 @@ void Database::UV_Query(uv_work_t* req) { if(prep_req->dbo->m_hStmt) { - uv_mutex_lock(&Database::g_odbcMutex); + uv_mutex_lock(&ODBC::g_odbcMutex); //free the previously used handle SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); @@ -568,7 +568,7 @@ void Database::UV_Query(uv_work_t* req) { prep_req->dbo->m_hDBC, &prep_req->dbo->m_hStmt ); - uv_mutex_unlock(&Database::g_odbcMutex); + uv_mutex_unlock(&ODBC::g_odbcMutex); } //check to see if should excute a direct or a parameter bound query @@ -632,7 +632,7 @@ void Database::UV_Query(uv_work_t* req) { prep_req->result = ret; } -Handle Database::Query(const Arguments& args) { +Handle ODBC::Query(const Arguments& args) { HandleScope scope; REQ_STR_ARG(0, sql); @@ -642,7 +642,7 @@ Handle Database::Query(const Arguments& args) { int paramCount = 0; Parameter* params; - Database* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.This()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); @@ -759,15 +759,15 @@ Handle Database::Query(const Arguments& args) { return scope.Close(Undefined()); } -void Database::UV_Tables(uv_work_t* req) { +void ODBC::UV_Tables(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); if(prep_req->dbo->m_hStmt) { - uv_mutex_lock(&Database::g_odbcMutex); + uv_mutex_lock(&ODBC::g_odbcMutex); SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->dbo->m_hStmt ); - uv_mutex_unlock(&Database::g_odbcMutex); + uv_mutex_unlock(&ODBC::g_odbcMutex); } SQLRETURN ret = SQLTables( @@ -782,7 +782,7 @@ void Database::UV_Tables(uv_work_t* req) { prep_req->result = ret; } -Handle Database::Tables(const Arguments& args) { +Handle ODBC::Tables(const Arguments& args) { HandleScope scope; REQ_STR_OR_NULL_ARG(0, catalog); @@ -791,7 +791,7 @@ Handle Database::Tables(const Arguments& args) { REQ_STR_OR_NULL_ARG(3, type); Local cb = Local::Cast(args[4]); - Database* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.This()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); @@ -838,7 +838,7 @@ Handle Database::Tables(const Arguments& args) { return scope.Close(Undefined()); } -void Database::UV_Columns(uv_work_t* req) { +void ODBC::UV_Columns(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); if(prep_req->dbo->m_hStmt) @@ -859,7 +859,7 @@ void Database::UV_Columns(uv_work_t* req) { prep_req->result = ret; } -Handle Database::Columns(const Arguments& args) { +Handle ODBC::Columns(const Arguments& args) { HandleScope scope; REQ_STR_OR_NULL_ARG(0, catalog); @@ -868,7 +868,7 @@ Handle Database::Columns(const Arguments& args) { REQ_STR_OR_NULL_ARG(3, column); Local cb = Local::Cast(args[4]); - Database* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.This()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); @@ -915,7 +915,7 @@ Handle Database::Columns(const Arguments& args) { return scope.Close(Undefined()); } -Column* Database::GetColumns(SQLHSTMT hStmt, short* colCount) { +Column* ODBC::GetColumns(SQLHSTMT hStmt, short* colCount) { SQLRETURN ret; SQLSMALLINT buflen; @@ -964,7 +964,7 @@ Column* Database::GetColumns(SQLHSTMT hStmt, short* colCount) { return columns; } -void Database::FreeColumns(Column* columns, short* colCount) { +void ODBC::FreeColumns(Column* columns, short* colCount) { for(int i = 0; i < *colCount; i++) { delete [] columns[i].name; } @@ -972,7 +972,7 @@ void Database::FreeColumns(Column* columns, short* colCount) { delete [] columns; } -Handle Database::GetColumnValue( SQLHSTMT hStmt, Column column, +Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, char* buffer, int bufferLength) { //HandleScope scope; SQLLEN len; @@ -1032,7 +1032,7 @@ Handle Database::GetColumnValue( SQLHSTMT hStmt, Column column, } } -Handle Database::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, +Handle ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength) { //HandleScope scope; @@ -1048,7 +1048,7 @@ Handle Database::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, //return scope.Close(tuple); } -Handle Database::GetRecordArray ( SQLHSTMT hStmt, Column* columns, +Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength) { //HandleScope scope; @@ -1064,10 +1064,10 @@ Handle Database::GetRecordArray ( SQLHSTMT hStmt, Column* columns, //return scope.Close(array); } -Persistent Database::constructor_template; +Persistent ODBC::constructor_template; extern "C" void init (v8::Handle target) { - Database::Init(target); + ODBC::Init(target); } NODE_MODULE(odbc_bindings, init) diff --git a/src/Database.h b/src/odbc.h similarity index 97% rename from src/Database.h rename to src/odbc.h index 954f2805..41ab8b05 100644 --- a/src/Database.h +++ b/src/odbc.h @@ -41,7 +41,7 @@ typedef struct { SQLUSMALLINT index; } Column; -class Database : public node::ObjectWrap { +class ODBC : public node::ObjectWrap { public: static Persistent constructor_template; static void Init(v8::Handle target); @@ -49,9 +49,9 @@ class Database : public node::ObjectWrap { static uv_async_t g_async; protected: - Database() {} + ODBC() {} - ~Database() {} + ~ODBC() {} static Handle New(const Arguments& args); @@ -83,7 +83,7 @@ class Database : public node::ObjectWrap { static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); - Database *self(void) { return this; } + ODBC *self(void) { return this; } protected: HENV m_hEnv; @@ -95,14 +95,14 @@ class Database : public node::ObjectWrap { struct open_request { Persistent cb; - Database *dbo; + ODBC *dbo; int result; char connection[1]; }; struct close_request { Persistent cb; - Database *dbo; + ODBC *dbo; int result; }; @@ -118,7 +118,7 @@ typedef struct { struct query_request { Persistent cb; - Database *dbo; + ODBC *dbo; int affectedRows; char *sql; char *catalog; From feab96611c1400d73ca89d8df1f82fd01bbc74ab Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Oct 2012 19:07:05 -0400 Subject: [PATCH 057/511] Implemented result/recordset object with fetch(), fetchAll(), close(), moreResults() --- binding.gyp | 3 +- src/odbc.cpp | 245 +++-------------- src/odbc.h | 27 +- src/odbc_result.cpp | 493 +++++++++++++++++++++++++++++++++++ src/odbc_result.h | 74 ++++++ test/bench-query-fetch.js | 61 +++++ test/bench-query-fetchAll.js | 51 ++++ 7 files changed, 735 insertions(+), 219 deletions(-) create mode 100644 src/odbc_result.cpp create mode 100644 src/odbc_result.h create mode 100644 test/bench-query-fetch.js create mode 100644 test/bench-query-fetchAll.js diff --git a/binding.gyp b/binding.gyp index 304ad8d5..bc0196a0 100644 --- a/binding.gyp +++ b/binding.gyp @@ -3,7 +3,8 @@ { 'target_name' : 'odbc_bindings', 'sources' : [ - 'src/odbc.cpp' + 'src/odbc.cpp', + 'src/odbc_result.cpp' ], 'conditions' : [ [ 'OS == "linux"', { diff --git a/src/odbc.cpp b/src/odbc.cpp index b17ca544..b38b5637 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -22,6 +22,8 @@ #include #include "odbc.h" +#include "odbc_result.h" + #ifdef _WIN32 #include "strptime.h" #endif @@ -195,7 +197,9 @@ void ODBC::UV_Open(uv_work_t* req) { SQL_DRIVER_NOPROMPT); if( ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ) { - ret = SQLAllocStmt( self->m_hDBC, &self->m_hStmt ); + HSTMT hStmt; + + ret = SQLAllocStmt( self->m_hDBC, &hStmt ); ret = SQLGetFunctions( self->m_hDBC, SQL_API_SQLMORERESULTS, @@ -204,6 +208,8 @@ void ODBC::UV_Open(uv_work_t* req) { if ( !SQL_SUCCEEDED(ret)) { self->canHaveMoreResults = 0; } + + ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt); } } } @@ -320,65 +326,21 @@ void ODBC::UV_AfterQuery(uv_work_t* req) { //an easy reference to the Database object ODBC* self = prep_req->dbo->self(); - - //our error object which we will use if we discover errors while processing - //the result set - Local objError; - - //used to keep track of the number of columns received in a result set - short colCount = 0; - - //used to keep track of the number of event emittions that have occurred - short emitCount = 0; - - //used to keep track of the number of errors that have been found - short errorCount = 0; //used to capture the return value from various SQL function calls SQLRETURN ret; - - //allocate a buffer for incoming column values - char* buf = (char *) malloc(MAX_VALUE_SIZE); - - //check to make sure malloc succeeded - if (buf == NULL) { - objError = Object::New(); - - //malloc failed, set an error message - objError->Set(String::New("error"), - String::New("[node-odbc] Failed Malloc")); - - objError->Set(String::New("message"), - String::New("An attempt to allocate memory failed. This " - "allocation was for a value buffer of incoming " - "recordset values.")); - - //Prepare arguments for the callback - Local args[3]; - args[0] = objError; - args[1] = Local::New(Null()); - args[2] = Local::New(False()); - - //Callback immidiately - prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); - - //emit a result event - goto cleanupshutdown; - } //First thing, let's check if the execution of the query returned any errors //in UV_Query if(prep_req->result == SQL_ERROR) { - objError = Object::New(); - - errorCount++; + Local objError = Object::New(); char errorMessage[512]; char errorSQLState[128]; SQLError( self->m_hEnv, self->m_hDBC, - self->m_hStmt, + prep_req->hSTMT, (SQLCHAR *) errorSQLState, NULL, (SQLCHAR *) errorMessage, @@ -399,136 +361,21 @@ void ODBC::UV_AfterQuery(uv_work_t* req) { Local args[1]; args[0] = objError; prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); - - goto cleanupshutdown; } - - //Loop through all result sets - do { - Local rows = Array::New(); - - //Retrieve and store all columns and their attributes - Column *columns = GetColumns(self->m_hStmt, &colCount); - - if (colCount > 0) { - int count = 0; - - //I dont think odbc will tell how many rows are returned, loop until out - while(true) { - - ret = SQLFetch(self->m_hStmt); - - //TODO: Do something to enable/disable dumping these info messages to - //the console. - if (ret == SQL_SUCCESS_WITH_INFO ) { - char errorMessage[512]; - char errorSQLState[128]; - - SQLError( self->m_hEnv, - self->m_hDBC, - self->m_hStmt, - (SQLCHAR *) errorSQLState, - NULL, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - NULL); - - printf("UV_Query => %s\n", errorMessage); - printf("UV_Query => %s\n", errorSQLState); - } - - if (ret == SQL_ERROR) { - objError = Object::New(); - - char errorMessage[512]; - char errorSQLState[128]; - - SQLError( self->m_hEnv, - self->m_hDBC, - self->m_hStmt, - (SQLCHAR *) errorSQLState, - NULL, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - NULL); - - errorCount++; - - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), - String::New("[node-odbc] Error in SQLFetch")); - objError->Set(String::New("message"), String::New(errorMessage)); - objError->Set(String::New("query"), String::New(prep_req->sql)); - - //emit an error event immidiately. - Local args[1]; - args[0] = objError; - prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); - - break; - } - - if (ret == SQL_NO_DATA) { - break; - } - - if (self->mode == MODE_CALLBACK_FOR_EACH) { - Handle args[2]; - - args[0] = Null(); - args[1] = GetRecordTuple( self->m_hStmt, - columns, - &colCount, - buf, - MAX_VALUE_SIZE - 1); - - prep_req->cb->Call(Context::GetCurrent()->Global(), 2, args); - } - else { - rows->Set( Integer::New(count), - GetRecordTuple( self->m_hStmt, - columns, - &colCount, - buf, - MAX_VALUE_SIZE - 1)); - } - - count++; - } - - FreeColumns(columns, &colCount); - } - - //move to the next result set - ret = SQLMoreResults( self->m_hStmt ); + else { + Local args[3]; + args[0] = External::New(self->m_hEnv); + args[1] = External::New(self->m_hDBC); + args[2] = External::New(prep_req->hSTMT); + Persistent js_result(ODBCResult::constructor_template-> + GetFunction()->NewInstance(3, args)); + + args[0] = Local::New(Null()); + args[1] = Local::New(js_result); - //Only trigger an emit if there are columns OR if this is the last result - //and none others have been emitted odbc will process individual statments - //like select @something = 1 as a recordset even though it doesn't have any - //columns. We don't want to emit those unless there are actually columns - if (colCount > 0 || ( ret != SQL_SUCCESS && emitCount == 0 )) { - emitCount++; - - Local args[3]; - - if (errorCount) { - args[0] = objError; - } - else { - args[0] = Local::New(Null()); - } - - args[1] = rows; - - //true or false, are there more result sets to follow this emit? - args[2] = Local::New((ret == SQL_SUCCESS) ? True() : False()); - - prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); - } + prep_req->cb->Call(Context::GetCurrent()->Global(), 2, args); } - while ( self->canHaveMoreResults && ret == SQL_SUCCESS ); - -cleanupshutdown: + TryCatch try_catch; self->Unref(); @@ -537,7 +384,6 @@ void ODBC::UV_AfterQuery(uv_work_t* req) { FatalException(try_catch); } - free(buf); prep_req->cb.Dispose(); free(prep_req->sql); free(prep_req->catalog); @@ -556,33 +402,27 @@ void ODBC::UV_Query(uv_work_t* req) { Parameter prm; SQLRETURN ret; - if(prep_req->dbo->m_hStmt) - { - uv_mutex_lock(&ODBC::g_odbcMutex); - - //free the previously used handle - SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); + uv_mutex_lock(&ODBC::g_odbcMutex); - //allocate a new handle - SQLAllocHandle( SQL_HANDLE_STMT, - prep_req->dbo->m_hDBC, - &prep_req->dbo->m_hStmt ); + //allocate a new statment handle + SQLAllocHandle( SQL_HANDLE_STMT, + prep_req->dbo->m_hDBC, + &prep_req->hSTMT ); - uv_mutex_unlock(&ODBC::g_odbcMutex); - } + uv_mutex_unlock(&ODBC::g_odbcMutex); //check to see if should excute a direct or a parameter bound query if (!prep_req->paramCount) { // execute the query directly - ret = SQLExecDirect( prep_req->dbo->m_hStmt, + ret = SQLExecDirect( prep_req->hSTMT, (SQLCHAR *) prep_req->sql, strlen(prep_req->sql)); } else { // prepare statement, bind parameters and execute statement - ret = SQLPrepare( prep_req->dbo->m_hStmt, + ret = SQLPrepare( prep_req->hSTMT, (SQLCHAR *) prep_req->sql, strlen(prep_req->sql)); @@ -592,7 +432,7 @@ void ODBC::UV_Query(uv_work_t* req) { { prm = prep_req->params[i]; - ret = SQLBindParameter( prep_req->dbo->m_hStmt, + ret = SQLBindParameter( prep_req->hSTMT, i + 1, SQL_PARAM_INPUT, prm.c_type, @@ -607,7 +447,7 @@ void ODBC::UV_Query(uv_work_t* req) { } if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { - ret = SQLExecute(prep_req->dbo->m_hStmt); + ret = SQLExecute(prep_req->hSTMT); } } @@ -762,16 +602,12 @@ Handle ODBC::Query(const Arguments& args) { void ODBC::UV_Tables(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); - if(prep_req->dbo->m_hStmt) - { - uv_mutex_lock(&ODBC::g_odbcMutex); - SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); - SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->dbo->m_hStmt ); - uv_mutex_unlock(&ODBC::g_odbcMutex); - } + uv_mutex_lock(&ODBC::g_odbcMutex); + SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->hSTMT ); + uv_mutex_unlock(&ODBC::g_odbcMutex); SQLRETURN ret = SQLTables( - prep_req->dbo->m_hStmt, + prep_req->hSTMT, (SQLCHAR *) prep_req->catalog, SQL_NTS, (SQLCHAR *) prep_req->schema, SQL_NTS, (SQLCHAR *) prep_req->table, SQL_NTS, @@ -841,14 +677,10 @@ Handle ODBC::Tables(const Arguments& args) { void ODBC::UV_Columns(uv_work_t* req) { query_request* prep_req = (query_request *)(req->data); - if(prep_req->dbo->m_hStmt) - { - SQLFreeHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hStmt ); - SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->dbo->m_hStmt ); - } + SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->hSTMT ); SQLRETURN ret = SQLColumns( - prep_req->dbo->m_hStmt, + prep_req->hSTMT, (SQLCHAR *) prep_req->catalog, SQL_NTS, (SQLCHAR *) prep_req->schema, SQL_NTS, (SQLCHAR *) prep_req->table, SQL_NTS, @@ -970,6 +802,8 @@ void ODBC::FreeColumns(Column* columns, short* colCount) { } delete [] columns; + + *colCount = 0; } Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, @@ -1068,6 +902,7 @@ Persistent ODBC::constructor_template; extern "C" void init (v8::Handle target) { ODBC::Init(target); + ODBCResult::Init(target); } NODE_MODULE(odbc_bindings, init) diff --git a/src/odbc.h b/src/odbc.h index 41ab8b05..59371ad7 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -14,8 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _DATABASE_H -#define _DATABASE_H +#ifndef _SRC_ODBC_H +#define _SRC_ODBC_H #include #include @@ -43,11 +43,17 @@ typedef struct { class ODBC : public node::ObjectWrap { public: - static Persistent constructor_template; - static void Init(v8::Handle target); - static uv_mutex_t g_odbcMutex; - static uv_async_t g_async; - + static Persistent constructor_template; + static uv_mutex_t g_odbcMutex; + static uv_async_t g_async; + + static void Init(v8::Handle target); + static Column* GetColumns(SQLHSTMT hStmt, short* colCount); + static void FreeColumns(Column* columns, short* colCount); + static Handle GetColumnValue(SQLHSTMT hStmt, Column column, char* buffer, int bufferLength); + static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); + static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); + protected: ODBC() {} @@ -77,18 +83,12 @@ class ODBC : public node::ObjectWrap { static Handle Columns(const Arguments& args); static void WatcherCallback(uv_async_t* w, int revents); - static Column* GetColumns(SQLHSTMT hStmt, short* colCount); - static void FreeColumns(Column* columns, short* colCount); - static Handle GetColumnValue(SQLHSTMT hStmt, Column column, char* buffer, int bufferLength); - static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); - static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); ODBC *self(void) { return this; } protected: HENV m_hEnv; HDBC m_hDBC; - HSTMT m_hStmt; SQLUSMALLINT canHaveMoreResults; int mode; }; @@ -119,6 +119,7 @@ typedef struct { struct query_request { Persistent cb; ODBC *dbo; + HSTMT hSTMT; int affectedRows; char *sql; char *catalog; diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp new file mode 100644 index 00000000..8ccc0a60 --- /dev/null +++ b/src/odbc_result.cpp @@ -0,0 +1,493 @@ +/* + Copyright (c) 2012, Dan VerWeire + Copyright (c) 2010, Lee Smith + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "odbc.h" +#include "odbc_result.h" + +using namespace v8; +using namespace node; + +Persistent ODBCResult::constructor_template; + +void ODBCResult::Init(v8::Handle target) { + HandleScope scope; + + Local t = FunctionTemplate::New(New); + + // Constructor Template + constructor_template = Persistent::New(t); + constructor_template->SetClassName(String::NewSymbol("ODBCResult")); + + // Reserve space for one Handle + Local instance_template = constructor_template->InstanceTemplate(); + instance_template->SetInternalFieldCount(1); + + // Prototype Methods + NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAll", FetchAll); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchOne", FetchOne); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "moreResults", MoreResults); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); + + // Attach the Database Constructor to the target object + target->Set( v8::String::NewSymbol("ODBCResult"), + constructor_template->GetFunction()); + + scope.Close(Undefined()); +} + +ODBCResult::~ODBCResult() { + this->Free(); +} + +void ODBCResult::Free() { + if (m_hDBC) { + uv_mutex_lock(&ODBC::g_odbcMutex); + + SQLFreeHandle(SQL_HANDLE_DBC, m_hDBC); + m_hDBC = NULL; + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + if (bufferLength > 0) { + free(buffer); + } + } +} + +Handle ODBCResult::New(const Arguments& args) { + HandleScope scope; + + REQ_EXT_ARG(0, js_henv); + REQ_EXT_ARG(1, js_hdbc); + REQ_EXT_ARG(2, js_hstmt); + + HENV hENV = static_cast(js_henv->Value()); + HDBC hDBC = static_cast(js_hdbc->Value()); + HSTMT hSTMT = static_cast(js_hstmt->Value()); + + //create a new OBCResult object + ODBCResult* objODBCResult = new ODBCResult(hENV, hDBC, hSTMT); + + //specify the buffer length + objODBCResult->bufferLength = MAX_VALUE_SIZE - 1; + + //initialze a buffer for this object + objODBCResult->buffer = (char *) malloc(objODBCResult->bufferLength + 1); + //TODO: make sure the malloc succeeded + + //set the initial colCount to 0 + objODBCResult->colCount = 0; + + objODBCResult->Wrap(args.Holder()); + + return scope.Close(args.Holder()); +} + +Handle ODBCResult::FetchOne(const Arguments& args) { + HandleScope scope; + + ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + Fetch_Request* req_fetch = (Fetch_Request *) calloc(1, sizeof(Fetch_Request)); + + Local cb; + + if (args.Length() == 0 || !args[0]->IsFunction()) { + return ThrowException(Exception::TypeError( + String::New("Argument 0 must be a callback function.")) + ); + } + + cb = Local::Cast(args[0]); + + req_fetch->callback = Persistent::New(cb); + + req_fetch->objResult = objODBCResult; + work_req->data = req_fetch; + + uv_queue_work(uv_default_loop(), work_req, UV_FetchOne, UV_AfterFetchOne); + + objODBCResult->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCResult::UV_FetchOne(uv_work_t* work_req) { + Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); + + ODBCResult* self = req_fetch->objResult->self(); + + req_fetch->result = SQLFetch(self->m_hSTMT); +} + +void ODBCResult::UV_AfterFetchOne(uv_work_t* work_req) { + HandleScope scope; + + Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); + + SQLRETURN ret = req_fetch->result; + + ODBCResult* self = req_fetch->objResult->self(); + + //check to see if there was an error + if (ret == SQL_ERROR) { + Local objError = Object::New(); + + char errorMessage[512]; + char errorSQLState[128]; + + SQLError( self->m_hENV, + self->m_hDBC, + self->m_hSTMT, + (SQLCHAR *) errorSQLState, + NULL, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + NULL); + + objError->Set(String::New("state"), String::New(errorSQLState)); + objError->Set(String::New("error"), + String::New("[node-odbc] Error in SQLFetch")); + objError->Set(String::New("message"), String::New(errorMessage)); + + //emit an error event immidiately. + Local args[1]; + args[0] = objError; + req_fetch->callback->Call(Context::GetCurrent()->Global(), 1, args); + req_fetch->callback.Dispose(); + + free(work_req); + free(req_fetch); + + self->Unref(); + + return; + } + + //check to see if we are at the end of the recordset + if (ret == SQL_NO_DATA) { + ODBC::FreeColumns(self->columns, &self->colCount); + + Handle args[2]; + args[0] = Null(); + args[1] = Null(); + + req_fetch->callback->Call(Context::GetCurrent()->Global(), 2, args); + req_fetch->callback.Dispose(); + + free(work_req); + free(req_fetch); + + self->Unref(); + + return; + } + + if (self->colCount == 0) { + self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); + } + + Handle args[2]; + + args[0] = Null(); + args[1] = ODBC::GetRecordTuple( self->m_hSTMT, + self->columns, + &self->colCount, + self->buffer, + self->bufferLength); + + req_fetch->callback->Call(Context::GetCurrent()->Global(), 2, args); + req_fetch->callback.Dispose(); + + free(work_req); + free(req_fetch); + return; +} + +Handle ODBCResult::FetchAll(const Arguments& args) { + HandleScope scope; + + ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + Fetch_Request* fetch_Request = (Fetch_Request *) calloc(1, sizeof(Fetch_Request)); + + Local cb; + + if (args.Length() == 0 || !args[0]->IsFunction()) { + return ThrowException(Exception::TypeError( + String::New("Argument 0 must be a callback function.")) + ); + } + + cb = Local::Cast(args[0]); + + fetch_Request->callback = Persistent::New(cb); + + fetch_Request->objResult = objODBCResult; + work_req->data = fetch_Request; + + uv_queue_work(uv_default_loop(), work_req, UV_FetchAll, UV_AfterFetchAll); + + objODBCResult->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCResult::UV_FetchAll(uv_work_t* work_req) { + //Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); + + //ODBCResult* self = req_fetch->objResult->self(); + + //req_fetch->result = SQLFetch(self->m_hSTMT); +} + +void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req) { + HandleScope scope; + + Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); + + ODBCResult* self = req_fetch->objResult->self(); + + Local objError = Object::New(); + + int count = 0; + int errorCount = 0; + + if (self->colCount == 0) { + self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); + } + + Local rows = Array::New(); + + //loop through all records + while (true) { + SQLRETURN ret = SQLFetch(self->m_hSTMT); + + ODBCResult* self = req_fetch->objResult->self(); + + //check to see if there was an error + if (ret == SQL_ERROR) { + errorCount++; + + char errorMessage[512]; + char errorSQLState[128]; + + SQLError( self->m_hENV, + self->m_hDBC, + self->m_hSTMT, + (SQLCHAR *) errorSQLState, + NULL, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + NULL); + + objError->Set(String::New("state"), String::New(errorSQLState)); + objError->Set(String::New("error"), + String::New("[node-odbc] Error in SQLFetch")); + objError->Set(String::New("message"), String::New(errorMessage)); + + break; + } + + //check to see if we are at the end of the recordset + if (ret == SQL_NO_DATA) { + ODBC::FreeColumns(self->columns, &self->colCount); + + break; + } + + rows->Set( Integer::New(count), + ODBC::GetRecordTuple( self->m_hSTMT, + self->columns, + &self->colCount, + self->buffer, + self->bufferLength)); + + count++; + } + + Handle args[2]; + args[0] = Null(); + args[1] = rows; + + req_fetch->callback->Call(Context::GetCurrent()->Global(), 2, args); + req_fetch->callback.Dispose(); + + free(work_req); + free(req_fetch); + + self->Unref(); +} + + +Handle ODBCResult::Close(const Arguments& args) { + HandleScope scope; + + ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); + + objODBCResult->Free(); + + return scope.Close(Undefined()); +} + +Handle ODBCResult::MoreResults(const Arguments& args) { + HandleScope scope; + + ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); + objODBCResult->colCount = 0; + + SQLRETURN ret = SQLMoreResults(objODBCResult->m_hSTMT); + + return scope.Close(SQL_SUCCEEDED(ret) ? True() : False()); +} + +/* + * fetchAll + +//Loop through all result sets + do { + Local rows = Array::New(); + + //Retrieve and store all columns and their attributes + Column *columns = GetColumns(self->m_hStmt, &colCount); + + if (colCount > 0) { + int count = 0; + + //I dont think odbc will tell how many rows are returned, loop until out + while(true) { + + ret = SQLFetch(self->m_hStmt); + + //TODO: Do something to enable/disable dumping these info messages to + //the console. + if (ret == SQL_SUCCESS_WITH_INFO ) { + char errorMessage[512]; + char errorSQLState[128]; + + SQLError( self->m_hEnv, + self->m_hDBC, + self->m_hStmt, + (SQLCHAR *) errorSQLState, + NULL, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + NULL); + + printf("UV_Query => %s\n", errorMessage); + printf("UV_Query => %s\n", errorSQLState); + } + + if (ret == SQL_ERROR) { + objError = Object::New(); + + char errorMessage[512]; + char errorSQLState[128]; + + SQLError( self->m_hEnv, + self->m_hDBC, + self->m_hStmt, + (SQLCHAR *) errorSQLState, + NULL, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + NULL); + + errorCount++; + + objError->Set(String::New("state"), String::New(errorSQLState)); + objError->Set(String::New("error"), + String::New("[node-odbc] Error in SQLFetch")); + objError->Set(String::New("message"), String::New(errorMessage)); + objError->Set(String::New("query"), String::New(prep_req->sql)); + + //emit an error event immidiately. + Local args[1]; + args[0] = objError; + prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); + + break; + } + + if (ret == SQL_NO_DATA) { + break; + } + + if (self->mode == MODE_CALLBACK_FOR_EACH) { + Handle args[2]; + + args[0] = Null(); + args[1] = GetRecordTuple( self->m_hStmt, + columns, + &colCount, + buf, + MAX_VALUE_SIZE - 1); + + prep_req->cb->Call(Context::GetCurrent()->Global(), 2, args); + } + else { + rows->Set( Integer::New(count), + GetRecordTuple( self->m_hStmt, + columns, + &colCount, + buf, + MAX_VALUE_SIZE - 1)); + } + + count++; + } + + FreeColumns(columns, &colCount); + } + + //move to the next result set + ret = SQLMoreResults( self->m_hStmt ); + + //Only trigger an emit if there are columns OR if this is the last result + //and none others have been emitted odbc will process individual statments + //like select @something = 1 as a recordset even though it doesn't have any + //columns. We don't want to emit those unless there are actually columns + if (colCount > 0 || ( ret != SQL_SUCCESS && emitCount == 0 )) { + emitCount++; + + Local args[3]; + + if (errorCount) { + args[0] = objError; + } + else { + args[0] = Local::New(Null()); + } + + args[1] = rows; + + //true or false, are there more result sets to follow this emit? + args[2] = Local::New((ret == SQL_SUCCESS) ? True() : False()); + + prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); + } + } + while ( self->canHaveMoreResults && ret == SQL_SUCCESS ); + + */ \ No newline at end of file diff --git a/src/odbc_result.h b/src/odbc_result.h new file mode 100644 index 00000000..d4430756 --- /dev/null +++ b/src/odbc_result.h @@ -0,0 +1,74 @@ +/* + Copyright (c) 2012, Dan VerWeire + Copyright (c) 2010, Lee Smith + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _SRC_ODBC_RESULT_H +#define _SRC_ODBC_RESULT_H + +class ODBCResult : public node::ObjectWrap { + public: + static Persistent constructor_template; + static void Init(v8::Handle target); + + void Free(); + + + protected: + ODBCResult() {}; + + explicit ODBCResult(HENV hENV, HDBC hDBC, HSTMT hSTMT): + ObjectWrap(), + m_hENV(hENV), + m_hDBC(hDBC), + m_hSTMT(hSTMT) {}; + + ~ODBCResult(); + + //constructor + static Handle New(const Arguments& args); + + //async methods + static Handle FetchOne(const Arguments& args); + static void UV_FetchOne(uv_work_t* work_req); + static void UV_AfterFetchOne(uv_work_t* work_req); + + static Handle FetchAll(const Arguments& args); + static void UV_FetchAll(uv_work_t* work_req); + static void UV_AfterFetchAll(uv_work_t* work_req); + + //sync methods + static Handle Close(const Arguments& args); + static Handle MoreResults(const Arguments& args); + + struct Fetch_Request { + Persistent callback; + ODBCResult *objResult; + SQLRETURN result; + }; + + ODBCResult *self(void) { return this; } + + protected: + HENV m_hENV; + HDBC m_hDBC; + HSTMT m_hSTMT; + char *buffer; + int bufferLength; + Column *columns; + short colCount; +}; + +#endif diff --git a/test/bench-query-fetch.js b/test/bench-query-fetch.js new file mode 100644 index 00000000..18334312 --- /dev/null +++ b/test/bench-query-fetch.js @@ -0,0 +1,61 @@ +var common = require("./common") +, odbc = require("../odbc.js") +, db = new odbc.Database(); + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , iterations = 10000 + , time = new Date().getTime(); + + for (var x = 0; x < iterations; x++) { + db.query("select 1 + 1 as test", cb); + } + + function cb (err, result) { + if (err) { + console.error(err); + return finish(); + } + + fetchAll(result); + } + + function fetchAll(rs) { + rs.fetchOne(function (err, data) { + if (err) { + console.error(err); + return finish(); + } + + //if data is null, then no more data + if (!data) { + rs.close(); + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return finish(); + } + } + else { + fetchAll(rs); + } + }); + } + + function finish() { + db.close(function () { + console.log("connection closed"); + }); + } +} \ No newline at end of file diff --git a/test/bench-query-fetchAll.js b/test/bench-query-fetchAll.js new file mode 100644 index 00000000..73d64df0 --- /dev/null +++ b/test/bench-query-fetchAll.js @@ -0,0 +1,51 @@ +var common = require("./common") +, odbc = require("../odbc.js") +, db = new odbc.Database(); + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , iterations = 10000 + , time = new Date().getTime(); + + for (var x = 0; x < iterations; x++) { + db.query("select 1 + 1 as test", cb); + } + + function cb (err, result) { + if (err) { + console.error(err); + return finish(); + } + + result.fetchAll(function (err, data) { + if (err) { + console.error(err); + return finish(); + } + + result.close(); + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return finish(); + } + }); + } + + function finish() { + db.close(function () { + console.log("connection closed"); + }); + } +} \ No newline at end of file From 54e15aadb3858c6fac3250e29db40b593b3d49c8 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 5 Nov 2012 11:59:37 -0500 Subject: [PATCH 058/511] Added QueryAll method which is the old way of querying the database. Uses the most memory but is also the fastest. Also replaced references to args.This() to args.Holder(); Fix connection bug --- src/odbc.cpp | 500 +++++++++++++++++++++++++++++++++++++++++---------- src/odbc.h | 30 +++- 2 files changed, 430 insertions(+), 100 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index b38b5637..d02c5cb2 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -34,6 +34,8 @@ using namespace node; uv_mutex_t ODBC::g_odbcMutex; uv_async_t ODBC::g_async; +Persistent ODBC::constructor_template; + void ODBC::Init(v8::Handle target) { HandleScope scope; @@ -49,11 +51,13 @@ void ODBC::Init(v8::Handle target) { // Properties instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); + instance_template->SetAccessor(String::New("connected"), ConnectedGetter); // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchOpen", Open); NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchClose", Close); NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchQuery", Query); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchQueryAll", QueryAll); NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchTables", Tables); NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchColumns", Columns); @@ -76,10 +80,11 @@ Handle ODBC::New(const Arguments& args) { HandleScope scope; ODBC* dbo = new ODBC(); - dbo->Wrap(args.This()); + dbo->Wrap(args.Holder()); dbo->mode = 1; + dbo->connected = false; - return scope.Close(args.This()); + return scope.Close(args.Holder()); } void ODBC::WatcherCallback(uv_async_t *w, int revents) { @@ -108,10 +113,20 @@ void ODBC::ModeSetter(Local property, Local value, const Accessor } } +Handle ODBC::ConnectedGetter(Local property, const AccessorInfo &info) { + HandleScope scope; + + ODBC *obj = ObjectWrap::Unwrap(info.Holder()); + + return scope.Close(obj->connected ? True() : False()); +} + void ODBC::UV_AfterOpen(uv_work_t* req) { HandleScope scope; open_request* open_req = (open_request *)(req->data); + ODBC* self = open_req->dbo->self(); + Local argv[1]; bool err = false; @@ -148,6 +163,10 @@ void ODBC::UV_AfterOpen(uv_work_t* req) { } while( ret == SQL_SUCCESS ); } + if (!err) { + self->connected = true; + } + TryCatch try_catch; open_req->dbo->Unref(); @@ -224,7 +243,7 @@ Handle ODBC::Open(const Arguments& args) { REQ_STR_ARG(0, connection); REQ_FUN_ARG(1, cb); - ODBC* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); open_request* open_req = (open_request *) calloc(1, sizeof(open_request) + connection.length()); @@ -251,12 +270,18 @@ void ODBC::UV_AfterClose(uv_work_t* req) { close_request* close_req = (close_request *)(req->data); + ODBC* dbo = close_req->dbo; + Local argv[1]; bool err = false; + if (close_req->result) { err = true; argv[0] = Exception::Error(String::New("Error closing database")); } + else { + dbo->connected = false; + } TryCatch try_catch; @@ -298,7 +323,7 @@ Handle ODBC::Close(const Arguments& args) { REQ_FUN_ARG(0, cb); - ODBC* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); close_request* close_req = (close_request *) (calloc(1, sizeof(close_request))); @@ -412,24 +437,20 @@ void ODBC::UV_Query(uv_work_t* req) { uv_mutex_unlock(&ODBC::g_odbcMutex); //check to see if should excute a direct or a parameter bound query - if (!prep_req->paramCount) - { + if (!prep_req->paramCount) { // execute the query directly ret = SQLExecDirect( prep_req->hSTMT, (SQLCHAR *) prep_req->sql, strlen(prep_req->sql)); } - else - { + else { // prepare statement, bind parameters and execute statement ret = SQLPrepare( prep_req->hSTMT, (SQLCHAR *) prep_req->sql, strlen(prep_req->sql)); - if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) - { - for (int i = 0; i < prep_req->paramCount; i++) - { + if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { + for (int i = 0; i < prep_req->paramCount; i++) { prm = prep_req->params[i]; ret = SQLBindParameter( prep_req->hSTMT, @@ -452,12 +473,9 @@ void ODBC::UV_Query(uv_work_t* req) { } // free parameters - for (int i = 0; i < prep_req->paramCount; i++) - { - if (prm = prep_req->params[i], prm.buffer != NULL) - { - switch (prm.c_type) - { + for (int i = 0; i < prep_req->paramCount; i++) { + if (prm = prep_req->params[i], prm.buffer != NULL) { + switch (prm.c_type) { case SQL_C_CHAR: free(prm.buffer); break; case SQL_C_LONG: delete (int64_t *)prm.buffer; break; case SQL_C_DOUBLE: delete (double *)prm.buffer; break; @@ -478,11 +496,8 @@ Handle ODBC::Query(const Arguments& args) { REQ_STR_ARG(0, sql); Local cb; - - int paramCount = 0; - Parameter* params; - ODBC* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); @@ -493,82 +508,335 @@ Handle ODBC::Query(const Arguments& args) { // populate prep_req->params if parameters were supplied // - if (args.Length() > 2) - { - if ( !args[1]->IsArray() ) - { + if (args.Length() > 2) { + if ( !args[1]->IsArray() ) { return ThrowException(Exception::TypeError( String::New("Argument 1 must be an Array")) ); } - else if ( !args[2]->IsFunction() ) - { + else if ( !args[2]->IsFunction() ) { return ThrowException(Exception::TypeError( String::New("Argument 2 must be a Function")) ); } - - Local values = Local::Cast(args[1]); cb = Local::Cast(args[2]); + + prep_req->params = GetParametersFromArray(Local::Cast(args[1]), &prep_req->paramCount); + } + else { + if ( !args[1]->IsFunction() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 1 must be a Function")) + ); + } - prep_req->paramCount = paramCount = values->Length(); - prep_req->params = params = new Parameter[paramCount]; - - for (int i = 0; i < paramCount; i++) - { - Local value = values->Get(i); - - params[i].size = 0; - params[i].length = SQL_NULL_DATA; - params[i].buffer_length = 0; - - if (value->IsString()) - { - String::Utf8Value string(value); - - params[i].c_type = SQL_C_CHAR; - params[i].type = SQL_VARCHAR; - params[i].length = SQL_NTS; - params[i].buffer = malloc(string.length() + 1); - params[i].buffer_length = string.length() + 1; - params[i].size = string.length() + 1; - - strcpy((char*)params[i].buffer, *string); - } - else if (value->IsNull()) - { - params[i].c_type = SQL_C_DEFAULT; - params[i].type = SQL_NULL_DATA; - params[i].length = SQL_NULL_DATA; + cb = Local::Cast(args[1]); + + prep_req->paramCount = 0; + } + + prep_req->sql = (char *) malloc(sql.length() +1); + prep_req->catalog = NULL; + prep_req->schema = NULL; + prep_req->table = NULL; + prep_req->type = NULL; + prep_req->column = NULL; + prep_req->cb = Persistent::New(cb); + + strcpy(prep_req->sql, *sql); + + prep_req->dbo = dbo; + work_req->data = prep_req; + + uv_queue_work(uv_default_loop(), work_req, UV_Query, UV_AfterQuery); + + dbo->Ref(); + + return scope.Close(Undefined()); +} + +void ODBC::UV_AfterQueryAll(uv_work_t* req) { + query_request* prep_req = (query_request *)(req->data); + + HandleScope scope; + + //an easy reference to the Database object + ODBC* self = prep_req->dbo->self(); + + //our error object which we will use if we discover errors while processing the result set + Local objError; + + //used to keep track of the number of columns received in a result set + short colCount = 0; + + //used to keep track of the number of event emittions that have occurred + short emitCount = 0; + + //used to keep track of the number of errors that have been found + short errorCount = 0; + + //used to capture the return value from various SQL function calls + SQLRETURN ret; + + //allocate a buffer for incoming column values + char* buf = (char *) malloc(MAX_VALUE_SIZE); + + //check to make sure malloc succeeded + if (buf == NULL) { + objError = Object::New(); + + //malloc failed, set an error message + objError->Set(String::New("error"), String::New("[node-odbc] Failed Malloc")); + objError->Set(String::New("message"), String::New("An attempt to allocate memory failed. This allocation was for a value buffer of incoming recordset values.")); + + //emit an error event immidiately. + Local args[3]; + args[0] = objError; + args[1] = Local::New(Null()); + args[2] = Local::New(False()); + + //emit an error event + prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); + + //emit a result event + goto cleanupshutdown; + } + //else { + //malloc succeeded so let's continue + + //set the first byte of the buffer to \0 instead of memsetting the entire buffer to 0 + buf[0] = '\0'; + + //First thing, let's check if the execution of the query returned any errors (in UV_Query) + if(prep_req->result == SQL_ERROR) { + objError = Object::New(); + + errorCount++; + + char errorMessage[512]; + char errorSQLState[128]; + SQLError(self->m_hEnv, self->m_hDBC, prep_req->hSTMT,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); + objError->Set(String::New("state"), String::New(errorSQLState)); + objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); + objError->Set(String::New("message"), String::New(errorMessage)); + + //only set the query value of the object if we actually have a query + if (prep_req->sql != NULL) { + objError->Set(String::New("query"), String::New(prep_req->sql)); + } + + //emit an error event immidiately. + Local args[1]; + args[0] = objError; + prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); + goto cleanupshutdown; + } + + //loop through all result sets + do { + Local rows = Array::New(); + + // retrieve and store column attributes to build the row object + Column *columns = GetColumns(prep_req->hSTMT, &colCount); + + if (colCount > 0) { + int count = 0; + + // i dont think odbc will tell how many rows are returned, loop until out... + while(true) { + Local tuple = Object::New(); + ret = SQLFetch(prep_req->hSTMT); + + //TODO: Do something to enable/disable dumping these info messages to the console. + if (ret == SQL_SUCCESS_WITH_INFO ) { + char errorMessage[512]; + char errorSQLState[128]; + SQLError(self->m_hEnv, self->m_hDBC, prep_req->hSTMT,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); + + printf("UV_Query => %s\n", errorMessage); + printf("UV_Query => %s\n", errorSQLState); } - else if (value->IsInt32()) - { - int64_t *number = new int64_t(value->IntegerValue()); - params[i].c_type = SQL_C_LONG; - params[i].type = SQL_INTEGER; - params[i].buffer = number; + + if (ret == SQL_ERROR) { + objError = Object::New(); + + char errorMessage[512]; + char errorSQLState[128]; + SQLError(self->m_hEnv, self->m_hDBC, prep_req->hSTMT,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); + + errorCount++; + objError->Set(String::New("state"), String::New(errorSQLState)); + objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); + objError->Set(String::New("message"), String::New(errorMessage)); + objError->Set(String::New("query"), String::New(prep_req->sql)); + + //emit an error event immidiately. + Local args[1]; + args[0] = objError; + prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); + + break; } - else if (value->IsNumber()) - { - double *number = new double(value->NumberValue()); - params[i].c_type = SQL_C_DOUBLE; - params[i].type = SQL_DECIMAL; - params[i].buffer = number; + + if (ret == SQL_NO_DATA) { + break; } - else if (value->IsBoolean()) - { - bool *boolean = new bool(value->BooleanValue()); - params[i].c_type = SQL_C_BIT; - params[i].type = SQL_BIT; - params[i].buffer = boolean; + + for(int i = 0; i < colCount; i++) { + tuple->Set( String::New((const char *) columns[i].name), + GetColumnValue( prep_req->hSTMT, columns[i], buf, MAX_VALUE_SIZE - 1)); } + + rows->Set(Integer::New(count), tuple); + count++; + } + + for(int i = 0; i < colCount; i++) { + delete [] columns[i].name; + } + + delete [] columns; } + + //move to the next result set + ret = SQLMoreResults( prep_req->hSTMT ); + + //Only trigger an emit if there are columns OR if this is the last result and none others have been emitted + //odbc will process individual statments like select @something = 1 as a recordset even though it doesn't have + //any columns. We don't want to emit those unless there are actually columns + if (colCount > 0 || ( ret != SQL_SUCCESS && emitCount == 0 )) { + emitCount++; + + Local args[3]; + + if (errorCount) { + args[0] = objError; //(objError->IsUndefined()) ? Undefined() : ; + } + else { + args[0] = Local::New(Null()); + } + + args[1] = rows; + //true or false, are there more result sets to follow this emit? + args[2] = Local::New(( ret == SQL_SUCCESS ) ? True() : False() ); + + prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); + } + } + while ( self->canHaveMoreResults && ret == SQL_SUCCESS ); + //} //end of malloc check +cleanupshutdown: + TryCatch try_catch; + + self->Unref(); + + if (try_catch.HasCaught()) { + FatalException(try_catch); } - else - { - if ( !args[1]->IsFunction() ) - { + + SQLFreeHandle(SQL_HANDLE_STMT, prep_req->hSTMT); + + free(buf); + prep_req->cb.Dispose(); + free(prep_req->sql); + free(prep_req->catalog); + free(prep_req->schema); + free(prep_req->table); + free(prep_req->type); + free(prep_req); + free(req); + scope.Close(Undefined()); +} + +void ODBC::UV_QueryAll(uv_work_t* req) { + query_request* prep_req = (query_request *)(req->data); + + Parameter prm; + SQLRETURN ret; + + uv_mutex_lock(&ODBC::g_odbcMutex); + + SQLAllocHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hDBC, &prep_req->hSTMT ); + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + //check to see if should excute a direct or a parameter bound query + if (!prep_req->paramCount) { + // execute the query directly + ret = SQLExecDirect( prep_req->hSTMT,(SQLCHAR *)prep_req->sql, strlen(prep_req->sql) ); + } + else { + // prepare statement, bind parameters and execute statement + ret = SQLPrepare(prep_req->hSTMT, (SQLCHAR *)prep_req->sql, strlen(prep_req->sql)); + + if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { + for (int i = 0; i < prep_req->paramCount; i++) { + prm = prep_req->params[i]; + + ret = SQLBindParameter(prep_req->hSTMT, i + 1, SQL_PARAM_INPUT, prm.c_type, prm.type, prm.size, 0, prm.buffer, prm.buffer_length, &prm.length); + if (ret == SQL_ERROR) {break;} + } + + if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { + ret = SQLExecute(prep_req->hSTMT); + } + } + + // free parameters + // + for (int i = 0; i < prep_req->paramCount; i++) { + if (prm = prep_req->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_LONG: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + free(prep_req->params); + } + + prep_req->result = ret; // this will be checked later in UV_AfterQuery +} + +Handle ODBC::QueryAll(const Arguments& args) { + HandleScope scope; + + REQ_STR_ARG(0, sql); + + Local cb; + + ODBC* dbo = ObjectWrap::Unwrap(args.This()); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); + + if (!prep_req) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + } + + // populate prep_req->params if parameters were supplied + // + if (args.Length() > 2) { + if ( !args[1]->IsArray() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 1 must be an Array")) + ); + } + else if ( !args[2]->IsFunction() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 2 must be a Function")) + ); + } + + cb = Local::Cast(args[2]); + prep_req->params = GetParametersFromArray(Local::Cast(args[1]), &prep_req->paramCount); + } + else { + if ( !args[1]->IsFunction() ) { return ThrowException(Exception::TypeError( String::New("Argument 1 must be a Function")) ); @@ -592,7 +860,7 @@ Handle ODBC::Query(const Arguments& args) { prep_req->dbo = dbo; work_req->data = prep_req; - uv_queue_work(uv_default_loop(), work_req, UV_Query, UV_AfterQuery); + uv_queue_work(uv_default_loop(), work_req, UV_QueryAll, UV_AfterQueryAll); dbo->Ref(); @@ -627,7 +895,7 @@ Handle ODBC::Tables(const Arguments& args) { REQ_STR_OR_NULL_ARG(3, type); Local cb = Local::Cast(args[4]); - ODBC* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); @@ -700,7 +968,7 @@ Handle ODBC::Columns(const Arguments& args) { REQ_STR_OR_NULL_ARG(3, column); Local cb = Local::Cast(args[4]); - ODBC* dbo = ObjectWrap::Unwrap(args.This()); + ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); @@ -869,7 +1137,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, Handle ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength) { - //HandleScope scope; + HandleScope scope; Local tuple = Object::New(); @@ -878,14 +1146,14 @@ Handle ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, GetColumnValue( hStmt, columns[i], buffer, bufferLength)); } - return tuple; - //return scope.Close(tuple); + //return tuple; + return scope.Close(tuple); } Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength) { - //HandleScope scope; + HandleScope scope; Local array = Array::New(); @@ -894,11 +1162,61 @@ Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, GetColumnValue( hStmt, columns[i], buffer, bufferLength)); } - return array; - //return scope.Close(array); + //return array; + return scope.Close(array); } -Persistent ODBC::constructor_template; +Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { + *paramCount = values->Length(); + + Parameter * params = new Parameter[*paramCount]; + + for (int i = 0; i < *paramCount; i++) { + Local value = values->Get(i); + + params[i].size = 0; + params[i].length = SQL_NULL_DATA; + params[i].buffer_length = 0; + + if (value->IsString()) { + String::Utf8Value string(value); + + params[i].c_type = SQL_C_CHAR; + params[i].type = SQL_VARCHAR; + params[i].length = SQL_NTS; + params[i].buffer = malloc(string.length() + 1); + params[i].buffer_length = string.length() + 1; + params[i].size = string.length() + 1; + + strcpy((char*)params[i].buffer, *string); + } + else if (value->IsNull()) { + params[i].c_type = SQL_C_DEFAULT; + params[i].type = SQL_NULL_DATA; + params[i].length = SQL_NULL_DATA; + } + else if (value->IsInt32()) { + int64_t *number = new int64_t(value->IntegerValue()); + params[i].c_type = SQL_C_LONG; + params[i].type = SQL_INTEGER; + params[i].buffer = number; + } + else if (value->IsNumber()) { + double *number = new double(value->NumberValue()); + params[i].c_type = SQL_C_DOUBLE; + params[i].type = SQL_DECIMAL; + params[i].buffer = number; + } + else if (value->IsBoolean()) { + bool *boolean = new bool(value->BooleanValue()); + params[i].c_type = SQL_C_BIT; + params[i].type = SQL_BIT; + params[i].buffer = boolean; + } + } + + return params; +} extern "C" void init (v8::Handle target) { ODBC::Init(target); diff --git a/src/odbc.h b/src/odbc.h index 59371ad7..880e529a 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -41,6 +41,15 @@ typedef struct { SQLUSMALLINT index; } Column; +typedef struct { + SQLSMALLINT c_type; + SQLSMALLINT type; + SQLLEN size; + void *buffer; + SQLLEN buffer_length; + SQLLEN length; +} Parameter; + class ODBC : public node::ObjectWrap { public: static Persistent constructor_template; @@ -53,7 +62,9 @@ class ODBC : public node::ObjectWrap { static Handle GetColumnValue(SQLHSTMT hStmt, Column column, char* buffer, int bufferLength); static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); - + + static Parameter* GetParametersFromArray (Local values, int* paramCount); + protected: ODBC() {} @@ -61,9 +72,12 @@ class ODBC : public node::ObjectWrap { static Handle New(const Arguments& args); + //Property Getter/Setters static Handle ModeGetter(Local property, const AccessorInfo &info); static void ModeSetter(Local property, Local value, const AccessorInfo &info); + static Handle ConnectedGetter(Local property, const AccessorInfo &info); + static void UV_AfterOpen(uv_work_t* req); static void UV_Open(uv_work_t* req); static Handle Open(const Arguments& args); @@ -76,6 +90,10 @@ class ODBC : public node::ObjectWrap { static void UV_Query(uv_work_t* req); static Handle Query(const Arguments& args); + static void UV_AfterQueryAll(uv_work_t* req); + static void UV_QueryAll(uv_work_t* req); + static Handle QueryAll(const Arguments& args); + static void UV_Tables(uv_work_t* req); static Handle Tables(const Arguments& args); @@ -91,6 +109,7 @@ class ODBC : public node::ObjectWrap { HDBC m_hDBC; SQLUSMALLINT canHaveMoreResults; int mode; + bool connected; }; struct open_request { @@ -107,14 +126,7 @@ struct close_request { }; -typedef struct { - SQLSMALLINT c_type; - SQLSMALLINT type; - SQLLEN size; - void *buffer; - SQLLEN buffer_length; - SQLLEN length; -} Parameter; + struct query_request { Persistent cb; From 4d0afc1985b039e1c42cb0568fd6ada9b11386f8 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 5 Nov 2012 12:01:08 -0500 Subject: [PATCH 059/511] Add queryResult() method Internally queryResult() calls dispatchQuery while query() calls dispatchQueryAll(); this is for backwards comatibility --- odbc.js | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/odbc.js b/odbc.js index 35971851..0186a294 100644 --- a/odbc.js +++ b/odbc.js @@ -81,6 +81,47 @@ Database.prototype.query = function(sql, params, callback) { self.processQueue(); }); + self.queue.push({ + context : self, + method : self.dispatchQueryAll, + args : args + }); + + self.processQueue(); +}; + +Database.prototype.queryResult = function(sql, params, callback) { + var self = this, args = []; + + if (callback == null) { + callback = params; // no parameters supplied + params = null; + } + + if (!self.connected) { + return callback( { message : "Connection not open." }, [], false ); + } + + if (!self.queue) self.queue = []; + + args.push(sql); + + if (params) { + args.push(params); + } + + args.push(function (error, rows, morefollowing) { + //check to see if this is the last result set returned + if (!morefollowing) { + self.queue.shift(); + self.executing = false; + } + + if (callback) callback.apply(self, arguments); + + self.processQueue(); + }); + self.queue.push({ context : self, method : self.dispatchQuery, From 6b3f541bd65a2466e3288682d9136b9b283f7e6f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 5 Nov 2012 12:01:44 -0500 Subject: [PATCH 060/511] rename fetchOne to fetch --- src/odbc_result.cpp | 10 +++++----- src/odbc_result.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 8ccc0a60..dd7ddd49 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -45,7 +45,7 @@ void ODBCResult::Init(v8::Handle target) { // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAll", FetchAll); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchOne", FetchOne); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetch", Fetch); NODE_SET_PROTOTYPE_METHOD(constructor_template, "moreResults", MoreResults); NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); @@ -104,7 +104,7 @@ Handle ODBCResult::New(const Arguments& args) { return scope.Close(args.Holder()); } -Handle ODBCResult::FetchOne(const Arguments& args) { +Handle ODBCResult::Fetch(const Arguments& args) { HandleScope scope; ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); @@ -126,14 +126,14 @@ Handle ODBCResult::FetchOne(const Arguments& args) { req_fetch->objResult = objODBCResult; work_req->data = req_fetch; - uv_queue_work(uv_default_loop(), work_req, UV_FetchOne, UV_AfterFetchOne); + uv_queue_work(uv_default_loop(), work_req, UV_Fetch, UV_AfterFetch); objODBCResult->Ref(); return scope.Close(Undefined()); } -void ODBCResult::UV_FetchOne(uv_work_t* work_req) { +void ODBCResult::UV_Fetch(uv_work_t* work_req) { Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); ODBCResult* self = req_fetch->objResult->self(); @@ -141,7 +141,7 @@ void ODBCResult::UV_FetchOne(uv_work_t* work_req) { req_fetch->result = SQLFetch(self->m_hSTMT); } -void ODBCResult::UV_AfterFetchOne(uv_work_t* work_req) { +void ODBCResult::UV_AfterFetch(uv_work_t* work_req) { HandleScope scope; Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); diff --git a/src/odbc_result.h b/src/odbc_result.h index d4430756..11a8faa4 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -41,9 +41,9 @@ class ODBCResult : public node::ObjectWrap { static Handle New(const Arguments& args); //async methods - static Handle FetchOne(const Arguments& args); - static void UV_FetchOne(uv_work_t* work_req); - static void UV_AfterFetchOne(uv_work_t* work_req); + static Handle Fetch(const Arguments& args); + static void UV_Fetch(uv_work_t* work_req); + static void UV_AfterFetch(uv_work_t* work_req); static Handle FetchAll(const Arguments& args); static void UV_FetchAll(uv_work_t* work_req); From 7b8771634e40620947f0ce3b7e6688d98d84eda1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 5 Nov 2012 12:02:21 -0500 Subject: [PATCH 061/511] update benchmarks to query their proper query methods --- test/bench-query-fetch.js | 4 ++-- test/bench-query-fetchAll.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/bench-query-fetch.js b/test/bench-query-fetch.js index 18334312..3f3440ee 100644 --- a/test/bench-query-fetch.js +++ b/test/bench-query-fetch.js @@ -17,7 +17,7 @@ function issueQuery() { , time = new Date().getTime(); for (var x = 0; x < iterations; x++) { - db.query("select 1 + 1 as test", cb); + db.queryResult("select 1 + 1 as test", cb); } function cb (err, result) { @@ -30,7 +30,7 @@ function issueQuery() { } function fetchAll(rs) { - rs.fetchOne(function (err, data) { + rs.fetch(function (err, data) { if (err) { console.error(err); return finish(); diff --git a/test/bench-query-fetchAll.js b/test/bench-query-fetchAll.js index 73d64df0..bc683573 100644 --- a/test/bench-query-fetchAll.js +++ b/test/bench-query-fetchAll.js @@ -17,7 +17,7 @@ function issueQuery() { , time = new Date().getTime(); for (var x = 0; x < iterations; x++) { - db.query("select 1 + 1 as test", cb); + db.queryResult("select 1 + 1 as test", cb); } function cb (err, result) { From 8fb97de66ec6111f709091df4e83dddc2122e8ed Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 5 Nov 2012 12:03:03 -0500 Subject: [PATCH 062/511] fix pool test --- test/test-pool-connect.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test-pool-connect.js b/test/test-pool-connect.js index 7b7154d3..f6de88b7 100644 --- a/test/test-pool-connect.js +++ b/test/test-pool-connect.js @@ -1,9 +1,9 @@ var common = require("./common") , odbc = require("../odbc.js") - , db = new odbc.Database() + , pool = new odbc.Pool() , connectionString = common.connectionString , connections = [] - , connectCount = 500; + , connectCount = 10; openConnectionsUsingPool(connections); @@ -67,4 +67,4 @@ function closeConnections (connections) { //console.error("Closed connection #", idx); }); }); -} \ No newline at end of file +} From bb2f41652ca1fe4f3c733f28285d886dd2d05b40 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 5 Nov 2012 12:03:27 -0500 Subject: [PATCH 063/511] close database after test --- test/test-query-select.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test-query-select.js b/test/test-query-select.js index 8bd1dfbd..f3e39018 100644 --- a/test/test-query-select.js +++ b/test/test-query-select.js @@ -4,12 +4,13 @@ var common = require("./common") db.open(common.connectionString, function(err) { - db.query('select * from test', function (err, data) { + db.query('select \'a\' as test, datetime(\'now\') as nowish, * from test', function (err, data) { if (err) { console.error(err); process.exit(1); } console.error(data); + db.close(function () {}); }); }); From b823837c462c343459dc331c2f10e9f6471733d0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 5 Nov 2012 12:37:49 -0500 Subject: [PATCH 064/511] Use SQL_C_WCHAR when calling SQLGetData() --- src/odbc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index d02c5cb2..bd037c11 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -1088,7 +1088,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //retrieve large fields int ret = SQLGetData( hStmt, column.index, - SQL_CHAR, + SQL_C_WCHAR, (char *) buffer, bufferLength, (SQLLEN *) &len); From 37e815dbf321145cf15841889fdb68a894f85837 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 5 Nov 2012 14:52:03 -0500 Subject: [PATCH 065/511] Change all value buffers to uint16_t* so that v8 can make unicode strings with String::New() --- src/odbc.cpp | 14 +++++++------- src/odbc.h | 6 +++--- src/odbc_result.cpp | 2 +- src/odbc_result.h | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index bd037c11..b6da2b9d 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -580,7 +580,7 @@ void ODBC::UV_AfterQueryAll(uv_work_t* req) { SQLRETURN ret; //allocate a buffer for incoming column values - char* buf = (char *) malloc(MAX_VALUE_SIZE); + uint16_t* buf = (uint16_t *) malloc(MAX_VALUE_SIZE); //check to make sure malloc succeeded if (buf == NULL) { @@ -1075,7 +1075,7 @@ void ODBC::FreeColumns(Column* columns, short* colCount) { } Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, - char* buffer, int bufferLength) { + uint16_t* buffer, int bufferLength) { //HandleScope scope; SQLLEN len; @@ -1089,7 +1089,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, int ret = SQLGetData( hStmt, column.index, SQL_C_WCHAR, - (char *) buffer, + (uint16_t *) buffer, bufferLength, (SQLLEN *) &len); @@ -1108,12 +1108,12 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, case SQL_REAL : case SQL_DOUBLE : //return scope.Close(Number::New(atof(buffer))); - return Number::New(atof(buffer)); + return Number::New(atof((char *) buffer)); case SQL_DATETIME : case SQL_TIMESTAMP : //I am not sure if this is locale-safe or cross database safe, but it //works for me on MSSQL - strptime(buffer, "%Y-%m-%d %H:%M:%S", &timeInfo); + strptime((char *) buffer, "%Y-%m-%d %H:%M:%S", &timeInfo); //a negative value means that mktime() should use timezone information //and system databases to attempt to determine whether DST is in effect @@ -1135,7 +1135,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } Handle ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, - short* colCount, char* buffer, + short* colCount, uint16_t* buffer, int bufferLength) { HandleScope scope; @@ -1151,7 +1151,7 @@ Handle ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, } Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, - short* colCount, char* buffer, + short* colCount, uint16_t* buffer, int bufferLength) { HandleScope scope; diff --git a/src/odbc.h b/src/odbc.h index 880e529a..522bd22e 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -59,9 +59,9 @@ class ODBC : public node::ObjectWrap { static void Init(v8::Handle target); static Column* GetColumns(SQLHSTMT hStmt, short* colCount); static void FreeColumns(Column* columns, short* colCount); - static Handle GetColumnValue(SQLHSTMT hStmt, Column column, char* buffer, int bufferLength); - static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); - static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, char* buffer, int bufferLength); + static Handle GetColumnValue(SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength); + static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); + static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Parameter* GetParametersFromArray (Local values, int* paramCount); diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index dd7ddd49..5aeb3bdc 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -93,7 +93,7 @@ Handle ODBCResult::New(const Arguments& args) { objODBCResult->bufferLength = MAX_VALUE_SIZE - 1; //initialze a buffer for this object - objODBCResult->buffer = (char *) malloc(objODBCResult->bufferLength + 1); + objODBCResult->buffer = (uint16_t *) malloc(objODBCResult->bufferLength + 1); //TODO: make sure the malloc succeeded //set the initial colCount to 0 diff --git a/src/odbc_result.h b/src/odbc_result.h index 11a8faa4..786e77c8 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -65,7 +65,7 @@ class ODBCResult : public node::ObjectWrap { HENV m_hENV; HDBC m_hDBC; HSTMT m_hSTMT; - char *buffer; + uint16_t *buffer; int bufferLength; Column *columns; short colCount; From e8aede646f551e14baf6e8e1a244139965638d99 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 5 Nov 2012 15:29:22 -0500 Subject: [PATCH 066/511] Call SQLGetData() with different TargetTypes depending on the data type --- src/odbc.cpp | 102 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 30 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index b6da2b9d..4b61f578 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "odbc.h" #include "odbc_result.h" @@ -1086,33 +1087,48 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //TODO: SQLGetData can supposedly return multiple chunks, need to do this to //retrieve large fields - int ret = SQLGetData( hStmt, - column.index, - SQL_C_WCHAR, - (uint16_t *) buffer, - bufferLength, - (SQLLEN *) &len); - - if(ret == SQL_NULL_DATA || len < 0) { - //return scope.Close(Null()); - return Null(); - } - else { - switch (column.type) { - case SQL_NUMERIC : - case SQL_DECIMAL : - case SQL_INTEGER : - case SQL_SMALLINT : - case SQL_BIGINT : - case SQL_FLOAT : - case SQL_REAL : - case SQL_DOUBLE : + int ret; + + switch (column.type) { + case SQL_NUMERIC : + case SQL_DECIMAL : + case SQL_INTEGER : + case SQL_SMALLINT : + case SQL_BIGINT : + case SQL_FLOAT : + case SQL_REAL : + case SQL_DOUBLE : + ret = SQLGetData( hStmt, + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + (SQLLEN *) &len); + + if(ret == SQL_NULL_DATA || len < 0) { + //return scope.Close(Null()); + return Null(); + } + else { //return scope.Close(Number::New(atof(buffer))); return Number::New(atof((char *) buffer)); - case SQL_DATETIME : - case SQL_TIMESTAMP : - //I am not sure if this is locale-safe or cross database safe, but it - //works for me on MSSQL + } + case SQL_DATETIME : + case SQL_TIMESTAMP : + //I am not sure if this is locale-safe or cross database safe, but it + //works for me on MSSQL + ret = SQLGetData( hStmt, + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + (SQLLEN *) &len); + + if(ret == SQL_NULL_DATA || len < 0) { + //return scope.Close(Null()); + return Null(); + } + else { strptime((char *) buffer, "%Y-%m-%d %H:%M:%S", &timeInfo); //a negative value means that mktime() should use timezone information @@ -1122,15 +1138,41 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //return scope.Close(Date::New(double(mktime(&timeInfo)) * 1000)); return Date::New(double(mktime(&timeInfo)) * 1000); - case SQL_BIT : - //again, i'm not sure if this is cross database safe, but it works for - //MSSQL + } + case SQL_BIT : + //again, i'm not sure if this is cross database safe, but it works for + //MSSQL + ret = SQLGetData( hStmt, + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + (SQLLEN *) &len); + + if(ret == SQL_NULL_DATA || len < 0) { + //return scope.Close(Null()); + return Null(); + } + else { //return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); return Boolean::New(( *buffer == '0') ? false : true ); - default : + } + default : + ret = SQLGetData( hStmt, + column.index, + SQL_C_WCHAR, + (uint16_t *) buffer, + bufferLength, + (SQLLEN *) &len); + + if(ret == SQL_NULL_DATA || len < 0) { + //return scope.Close(Null()); + return Null(); + } + else { //return scope.Close(String::New(buffer)); return String::New(buffer); - } + } } } From 7693f7ee5b8c81bb552927deecf73bf73335232d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 15:40:26 -0500 Subject: [PATCH 067/511] Make main export a factory method --- odbc.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/odbc.js b/odbc.js index 0186a294..d4f63169 100644 --- a/odbc.js +++ b/odbc.js @@ -16,8 +16,13 @@ var odbc = require("bindings")("odbc_bindings"); -exports.debug = false; -exports.Database = Database; +module.exports = function (options) { + return new Database(options); +} + +module.exports.debug = false; + +module.exports.Database = Database; function Database () { var self = this; @@ -269,7 +274,7 @@ Database.prototype.describe = function(obj, callback) { } }; -exports.Pool = Pool; +module.exports.Pool = Pool; Pool.count = 0; From fea9247c251d470570ec0f508e0a53026c53957c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 15:40:56 -0500 Subject: [PATCH 068/511] Fix describe table/column queue processing --- odbc.js | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/odbc.js b/odbc.js index d4f63169..880a2f99 100644 --- a/odbc.js +++ b/odbc.js @@ -194,15 +194,21 @@ Database.prototype.tables = function(catalog, schema, table, type, callback) { var self = this; if (!self.queue) self.queue = []; + callback = callback || arguments[arguments.length - 1]; + + var args = [catalog, schema, table, type, function () { + self.queue.shift(); + self.executing = false; + + if (callback) callback.apply(self, arguments); + + self.processQueue(); + }]; + self.queue.push({ context : self, method : self.dispatchTables, - catalog : (arguments.length > 1) ? catalog : "", - schema : (arguments.length > 2) ? schema : "", - table : (arguments.length > 3) ? table : "", - type : (arguments.length > 4) ? type : "", - callback : (arguments.length == 5) ? callback : arguments[arguments.length - 1], - args : arguments + args : args }); self.processQueue(); @@ -212,15 +218,21 @@ Database.prototype.columns = function(catalog, schema, table, column, callback) var self = this; if (!self.queue) self.queue = []; + callback = callback || arguments[arguments.length - 1]; + + var args = [catalog, schema, table, column, function () { + self.queue.shift(); + self.executing = false; + + if (callback) callback.apply(self, arguments); + + self.processQueue(); + }]; + self.queue.push({ context : self, method : self.dispatchColumns, - catalog : (arguments.length > 1) ? catalog : "", - schema : (arguments.length > 2) ? schema : "", - table : (arguments.length > 3) ? table : "", - column : (arguments.length > 4) ? column : "", - callback : (arguments.length == 5) ? callback : arguments[arguments.length - 1], - args : arguments + args : args }); self.processQueue(); From 028463e5a59fe8a199efc63710eda268f9da9e9b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 15:41:55 -0500 Subject: [PATCH 069/511] Wrap SQLFreeHandle in mutex lock/unlock --- src/odbc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index 4b61f578..e6a985a0 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -736,7 +736,11 @@ void ODBC::UV_AfterQueryAll(uv_work_t* req) { FatalException(try_catch); } + uv_mutex_lock(&ODBC::g_odbcMutex); + SQLFreeHandle(SQL_HANDLE_STMT, prep_req->hSTMT); + + uv_mutex_unlock(&ODBC::g_odbcMutex); free(buf); prep_req->cb.Dispose(); From 5c7d2428c2862efd5c3e04cedee95de363cc325e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 15:42:38 -0500 Subject: [PATCH 070/511] use UV_AfterQueryAll for Tables and Columns --- src/odbc.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index e6a985a0..8fdae8af 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include "odbc.h" #include "odbc_result.h" @@ -940,7 +939,7 @@ Handle ODBC::Tables(const Arguments& args) { prep_req->dbo = dbo; work_req->data = prep_req; - uv_queue_work(uv_default_loop(), work_req, UV_Tables, UV_AfterQuery); + uv_queue_work(uv_default_loop(), work_req, UV_Tables, UV_AfterQueryAll); dbo->Ref(); @@ -1013,7 +1012,7 @@ Handle ODBC::Columns(const Arguments& args) { prep_req->dbo = dbo; work_req->data = prep_req; - uv_queue_work(uv_default_loop(), work_req, UV_Columns, UV_AfterQuery); + uv_queue_work(uv_default_loop(), work_req, UV_Columns, UV_AfterQueryAll); dbo->Ref(); From e06418dd51458c3051448d20675cc2cca5df6217 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 15:43:23 -0500 Subject: [PATCH 071/511] test: add dropTables and createTables methods to common.js --- test/common.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/common.js b/test/common.js index b73757cf..d1f5889d 100644 --- a/test/common.js +++ b/test/common.js @@ -1,4 +1,6 @@ module.exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; +//module.exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; +//module.exports.connectionString = process.env.ODBC_CONNETION_STRING; module.exports.connectionObject = { DRIVER : "{SQLITE3}", @@ -11,3 +13,11 @@ module.exports.connections = [ DATABASE : "data/sqlite-test.db" } ]; + +module.exports.dropTables = function (db, cb) { + db.query("drop table TEST", cb); +}; + +module.exports.createTables = function (db, cb) { + db.query("create table TEST(COLINT INTEGER, COLDATETIME DATETIME, COLTEXT TEXT)", cb); +}; \ No newline at end of file From 25daedbea5d419d625a7e46fb9f8dec51493a246 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 15:44:58 -0500 Subject: [PATCH 072/511] moved test-issue-13 to disabled --- test/{ => disabled}/test-issue-13.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{ => disabled}/test-issue-13.js (100%) diff --git a/test/test-issue-13.js b/test/disabled/test-issue-13.js similarity index 100% rename from test/test-issue-13.js rename to test/disabled/test-issue-13.js From 412176716c92b7ef478ba5f211b432bbd1c67209 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 15:45:52 -0500 Subject: [PATCH 073/511] tests: re-work tests with assertion testing and prepare for a test runner --- test/test-closed.js | 39 ++++++---- test/test-connection-object.js | 23 +++--- test/test-date.js | 111 ++++++++-------------------- test/test-describe-column.js | 56 ++++++++++---- test/test-describe-database.js | 40 ++++++---- test/test-describe-table.js | 92 +++++++++++++++++++---- test/test-multi-open-close.js | 49 ++++++++++++ test/test-multi-open-query-close.js | 75 +++++++++++++++++++ test/test-open-close.js | 35 ++++++--- test/test-open-dont-close.js | 5 +- test/test-param-select.js | 35 +++++---- test/test-pool-connect.js | 40 +--------- test/test-query-create-table.js | 23 +++--- test/test-query-select-fetch.js | 18 +++++ 14 files changed, 408 insertions(+), 233 deletions(-) create mode 100644 test/test-multi-open-close.js create mode 100644 test/test-multi-open-query-close.js create mode 100644 test/test-query-select-fetch.js diff --git a/test/test-closed.js b/test/test-closed.js index 71511982..a54e3245 100644 --- a/test/test-closed.js +++ b/test/test-closed.js @@ -1,21 +1,30 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; + +assert.equal(db.connected, false); db.query("select * from test", function (err, rs, moreResultSets) { - console.error(arguments); + assert.deepEqual(err, { message: 'Connection not open.' }); + assert.deepEqual(rs, []); + assert.equal(moreResultSets, false); + assert.equal(db.connected, false); }); -db.open(common.connectionString, function(err) -{ - console.error('db.open callback'); - - db.close(function () { - console.error('db.close callback'); - - db.query("select * from test", function (err, rs, moreResultSets) { - console.error('db.query callback'); - console.error(arguments); - }); - }); +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + db.close(function () { + assert.equal(db.connected, false); + + db.query("select * from test", function (err, rs, moreResultSets) { + assert.deepEqual(err, { message: 'Connection not open.' }); + assert.deepEqual(rs, []); + assert.equal(moreResultSets, false); + assert.equal(db.connected, false); + }); + }); }); diff --git a/test/test-connection-object.js b/test/test-connection-object.js index 5cf70d9f..9008ebd0 100644 --- a/test/test-connection-object.js +++ b/test/test-connection-object.js @@ -1,16 +1,13 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; -db.open(common.connectionObject, function(err){ - if (err) { - console.error(err); - process.exit(1); - } - - console.error("Open success"); - - db.close(function () { - console.error("Close Success"); - }); +db.open(common.connectionObject, function(err){ + assert.equal(err, null); + + db.close(function () { + assert.equal(db.connected, false); + }); }); diff --git a/test/test-date.js b/test/test-date.js index 7955f38a..0f05105b 100644 --- a/test/test-date.js +++ b/test/test-date.js @@ -1,83 +1,34 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); - -db.open(common.connectionString, function(err) -{ - if (err) { - console.error(err); - process.exit(1); - } - - createTable(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; + +process.on('uncaughtException', function (err) { + console.log('Failure', err); + + db.close(function () { + process.exit(1); + }); }); -function createTable() { - db.query("CREATE TABLE IF NOT EXISTS date_test ( dt1 datetime )", function (err) { - if (err) { - console.error(err); - return cleanup(1); - } - - insertData(); - }); -} - -function dropTable(exitCode) { - db.query("DROP TABLE IF EXISTS date_test", function (err) { - if (err) { - console.error(err); - return cleanup(1); - } - - cleanup(exitCode || 0); - }); -} - - -function insertData() { - db.query("INSERT INTO date_test (dt1) values ('2012-04-17')", function (err) { - if (err) { - console.error(err); - return dropTable(1); - } - - selectData(); - }); -} - -function selectData() { - db.query("SELECT * FROM date_test", function (err, data) { - if (err) { - console.error(err); - return cleanup(1); - } - - //test selected data - if (data[0].dt1 instanceof Date) { - dropTable(0); - } - else { - console.error("dt1 is instance of: %s", data[0].dt1.constructor.name); - dropTable(1); - } - }); -} - -function cleanup(exitCode) { - db.close(function (err) { - if (exitCode == 0) { - console.error("success"); - } - else { - console.error("failure"); - } - - if (err) { - process.error(err); - process.exit(1); - } - - process.exit(exitCode); - }); -} +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + var dt = new Date(); + + db.query("SELECT cast('" + dt.toISOString().replace('Z','') + "' as datetime) as DT1", function (err, data) { + assert.equal(err, null); + assert.equal(data.length, 1); + + db.close(function () { + assert.equal(db.connected, false); + + //test selected data after the connection + //is closed, in case the assertion fails + assert.equal(data[0].DT1.constructor.name, "Date", "DT1 is not an instance of a Date object"); + assert.equal(data[0].DT1.getTime(), dt.getTime(), "The original time passed to the database is not the same as the time we got back"); + }); + }); +}); diff --git a/test/test-describe-column.js b/test/test-describe-column.js index 7f6af2ce..0fde1936 100644 --- a/test/test-describe-column.js +++ b/test/test-describe-column.js @@ -1,19 +1,43 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; -db.open(common.connectionString, function(err) -{ - db.describe({ - database : 'main', - table : 'test', - column : 'col1' - }, function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - }); +db.open(common.connectionString, function(err) { + assert.equal(err, null); + + common.dropTables(db, function () { + common.createTables(db, function () { + + db.describe({ + database : 'MAIN', + table : 'TEST', + column : 'COLDATETIME' + }, function (err, data) { + db.close(function () { + assert.deepEqual(data, [{ + TABLE_QUALIFIER: '', + TABLE_OWNER: '', + TABLE_NAME: 'TEST', + COLUMN_NAME: 'COLDATETIME', + DATA_TYPE: 11, + TYPE_NAME: 'DATETIME', + PRECISION: 3, + LENGTH: 32, + SCALE: '10', + RADIX: '0', + NULLABLE: 1, + REMARKS: null, + COLUMN_DEF: 'NULL', + SQL_DATA_TYPE: '11', + SQL_DATETIME_SUB: null, + CHAR_OCTET_LENGTH: 16384, + ORDINAL_POSITION: 2, + IS_NULLABLE: 'YES' + }]); + }); + }); + }); + }); }); diff --git a/test/test-describe-database.js b/test/test-describe-database.js index 50ba4e39..81c58555 100644 --- a/test/test-describe-database.js +++ b/test/test-describe-database.js @@ -1,17 +1,27 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; -db.open(common.connectionString, function(err) -{ - db.describe({ - database : 'main' - }, function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - }); -}); +db.open(common.connectionString, function(err) { + assert.equal(err, null); + + common.dropTables(db, function () { + common.createTables(db, function () { + db.describe({ + database : 'MAIN' + }, function (err, data) { + db.close(function () { + assert.deepEqual(data, [{ + TABLE_QUALIFIER: null, + TABLE_OWNER: null, + TABLE_NAME: 'TEST', + TABLE_TYPE: 'TABLE', + REMARKS: null + }]); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/test-describe-table.js b/test/test-describe-table.js index 4c88d18f..c0c5fea2 100644 --- a/test/test-describe-table.js +++ b/test/test-describe-table.js @@ -1,18 +1,78 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; -db.open(common.connectionString, function(err) -{ - db.describe({ - database : 'main', - table : 'test' - }, function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - }); -}); +db.open(common.connectionString, function(err) { + assert.equal(err, null); + + common.dropTables(db, function () { + common.createTables(db, function () { + + db.describe({ + database : 'MAIN' + , table : 'TEST' + }, function (err, data) { + db.close(function () { + assert.deepEqual(data, [{ + TABLE_QUALIFIER: '', + TABLE_OWNER: '', + TABLE_NAME: 'TEST', + COLUMN_NAME: 'COLINT', + DATA_TYPE: 4, + TYPE_NAME: 'INTEGER', + PRECISION: 9, + LENGTH: '10', + SCALE: 10, + RADIX: 0, + NULLABLE: '1', + REMARKS: null, + COLUMN_DEF: 'NULL', + SQL_DATA_TYPE: '4', + SQL_DATETIME_SUB: null, + CHAR_OCTET_LENGTH: 16384, + ORDINAL_POSITION: '1', + IS_NULLABLE: 'YES' }, + { TABLE_QUALIFIER: '', + TABLE_OWNER: '', + TABLE_NAME: 'TEST', + COLUMN_NAME: 'COLDATETIME', + DATA_TYPE: 11, + TYPE_NAME: 'DATETIME', + PRECISION: 3, + LENGTH: '32', + SCALE: 10, + RADIX: 0, + NULLABLE: '1', + REMARKS: null, + COLUMN_DEF: 'NULL', + SQL_DATA_TYPE: '11', + SQL_DATETIME_SUB: null, + CHAR_OCTET_LENGTH: 16384, + ORDINAL_POSITION: '2', + IS_NULLABLE: 'YES' }, + { TABLE_QUALIFIER: '', + TABLE_OWNER: '', + TABLE_NAME: 'TEST', + COLUMN_NAME: 'COLTEXT', + DATA_TYPE: -1, + TYPE_NAME: 'TEXT', + PRECISION: 0, + LENGTH: '65536', + SCALE: 10, + RADIX: 0, + NULLABLE: '1', + REMARKS: null, + COLUMN_DEF: 'NULL', + SQL_DATA_TYPE: '-1', + SQL_DATETIME_SUB: null, + CHAR_OCTET_LENGTH: 16384, + ORDINAL_POSITION: '3', + IS_NULLABLE: 'YES' + }]); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/test-multi-open-close.js b/test/test-multi-open-close.js new file mode 100644 index 00000000..21099973 --- /dev/null +++ b/test/test-multi-open-close.js @@ -0,0 +1,49 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , openCallback = 0 + , closeCallback = 0 + , openCount = 100 + , connections = [] + ; + +for (var x = 0; x < openCount; x++ ) { + (function () { + var db = new odbc.Database(); + connections.push(db); + + db.open(common.connectionString, function(err) { + if (err) { + throw err; + process.exit(1); + } + + openCallback += 1; + + maybeClose(); + }); + })(); +} + +function maybeClose() { + + if (openCount == openCallback) { + doClose(); + } +} + + +function doClose() { + connections.forEach(function (db) { + db.close(function () { + closeCallback += 1; + + maybeFinish(); + }); + }); +} + +function maybeFinish() { + if (openCount == closeCallback) { + console.log('Done'); + } +} diff --git a/test/test-multi-open-query-close.js b/test/test-multi-open-query-close.js new file mode 100644 index 00000000..436aaeb3 --- /dev/null +++ b/test/test-multi-open-query-close.js @@ -0,0 +1,75 @@ +var common = require("./common") +, odbc = require("../odbc.js") +, openCallback = 0 +, closeCallback = 0 +, queryCallback = 0 +, openCount = 10 +, connections = [] +; + +for (var x = 0; x < openCount; x++ ) { + (function (x) { + var db = new odbc.Database(); + connections.push(db); + + db.open(common.connectionString, function(err) { + if (err) { + throw err; + process.exit(); + } + + //console.error("Open: %s %s %s", x, openCount, openCallback); + + openCallback += 1; + + maybeQuery(); + }); + })(x); +} + +function maybeQuery() { + if (openCount == openCallback) { + doQuery(); + } +} + +function doQuery() { + connections.forEach(function (db, ix) { + var seconds = connections.length - ix; + + var query = "WAITFOR DELAY '00:00:0" + seconds + "'; select " + seconds + " as result"; + + db.query(query, function (err, rows, moreResultSets) { + + //console.error("Query: %s %s %s %s", ix, openCount, queryCallback, moreResultSets, rows, err); + + queryCallback += 1; + + maybeClose(); + }); + }); +} + +function maybeClose() { + if (openCount == queryCallback) { + doClose(); + } +} + +function doClose() { + connections.forEach(function (db, ix) { + db.close(function () { + //console.log("Close: %s %s %s", ix, openCount, closeCallback); + + closeCallback += 1; + + maybeFinish(); + }); + }); +} + +function maybeFinish() { + if (openCount == closeCallback) { + console.error('done'); + } +} diff --git a/test/test-open-close.js b/test/test-open-close.js index 402d1703..9f625a85 100644 --- a/test/test-open-close.js +++ b/test/test-open-close.js @@ -1,12 +1,29 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert"); -db.open(common.connectionString, function(err) -{ - console.error('db.open callback'); - - db.close(function () { - console.error('db.close callback'); - }); +assert.equal(db.connected, false); + +db.query("select * from test", function (err, rs, moreResultSets) { + assert.deepEqual(err, { message: 'Connection not open.' }); + assert.deepEqual(rs, []); + assert.equal(moreResultSets, false); + assert.equal(db.connected, false); +}); + +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + db.close(function () { + assert.equal(db.connected, false); + + db.query("select * from test", function (err, rs, moreResultSets) { + assert.deepEqual(err, { message: 'Connection not open.' }); + assert.deepEqual(rs, []); + assert.equal(moreResultSets, false); + assert.equal(db.connected, false); + }); + }); }); diff --git a/test/test-open-dont-close.js b/test/test-open-dont-close.js index b7bc6367..ef503665 100644 --- a/test/test-open-dont-close.js +++ b/test/test-open-dont-close.js @@ -2,8 +2,7 @@ var common = require("./common") , odbc = require("../odbc.js") , db = new odbc.Database(); -db.open(common.connectionString, function(err) -{ +db.open(common.connectionString, function(err) { console.error('db.open callback'); - console.error('node should just sit and wait'); + console.error('node should just sit and wait'); }); diff --git a/test/test-param-select.js b/test/test-param-select.js index 78313679..d6732cee 100644 --- a/test/test-param-select.js +++ b/test/test-param-select.js @@ -1,23 +1,22 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert"); db.open(common.connectionString, function (err) { - if (err) { - console.error(err); - return; - } - - db.query("select * from test where col1 = ? " - , ["fish"] - , function (err, data, more) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - } - ); + assert.equal(err, null); + + db.query("select ? as TEXTCOL, ? as TEXTCOL2, ? as INTCOL " + , ["fish", "asdf", 1] + , function (err, data, more) { + db.close(function () { + assert.equal(err, null); + assert.deepEqual(data, [{ + TEXTCOL: 'fish', + TEXTCOL2: 'asdf', + INTCOL: 1 + }]); + }); + }); }); diff --git a/test/test-pool-connect.js b/test/test-pool-connect.js index f6de88b7..a52d588f 100644 --- a/test/test-pool-connect.js +++ b/test/test-pool-connect.js @@ -11,8 +11,7 @@ function openConnectionsUsingPool(connections) { for (var x = 0; x <= connectCount; x++) { (function (connectionIndex) { - //setTimeout(function () { - //console.error("Opening connection #", connectionIndex); + console.error("Opening connection #", connectionIndex); pool.open(connectionString, function (err, connection) { //console.error("Opened connection #", connectionIndex); @@ -24,34 +23,6 @@ function openConnectionsUsingPool(connections) { connections.push(connection); - if (connectionIndex == connectCount) { - //closeConnections(connections); - } - }); - - //}, x * 50); - })(x); - } -} - -function openConnectionsUsingDB(connections) { - for (var x = 0; x <= connectCount; x++) { - - (function (connectionIndex) { - //console.error("Opening connection #", connectionIndex); - var db = new Database(); - - db.open(connectionString, function (err, connection) { - //console.error("Opened connection #", connectionIndex); - - if (err) { - console.error("error: ", err.message); - return false; - } - - connections.push(db); - //connections.push(connection); - if (connectionIndex == connectCount) { closeConnections(connections); } @@ -61,10 +32,7 @@ function openConnectionsUsingDB(connections) { } function closeConnections (connections) { - connections.forEach(function (connection, idx) { - //console.error("Closing connection #", idx); - connection.close(function () { - //console.error("Closed connection #", idx); - }); - }); + pool.close(function () { + console.error("pool closed"); + }); } diff --git a/test/test-query-create-table.js b/test/test-query-create-table.js index 39597046..0eec3c9f 100644 --- a/test/test-query-create-table.js +++ b/test/test-query-create-table.js @@ -1,15 +1,14 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; -db.open(common.connectionString, function(err) -{ - db.query("create table test (col1 varchar(50), col2 varchar(20))", function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - }); +db.open(common.connectionString, function(err) { + common.createTables(db, function (err, data, morefollowing) { + console.log(arguments); + db.close(function () { + + }); + }); }); diff --git a/test/test-query-select-fetch.js b/test/test-query-select-fetch.js new file mode 100644 index 00000000..d15c30c6 --- /dev/null +++ b/test/test-query-select-fetch.js @@ -0,0 +1,18 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database(); + +db.open(common.connectionString, function(err) +{ + db.queryResult('select \'a\' as test, datetime(\'now\') as nowish', function (err, result) { + if (err) { + console.error(err); + process.exit(1); + } + + result.fetch(function (err, data) { + console.error(data); + db.close(function () {}); + }); + }); +}); From 6d566344999b5d3db8413b7aa66d4a80cf361f91 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 15:48:33 -0500 Subject: [PATCH 074/511] add .gitignore in test data so folder stays --- test/data/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/data/.gitignore diff --git a/test/data/.gitignore b/test/data/.gitignore new file mode 100644 index 00000000..f1450df8 --- /dev/null +++ b/test/data/.gitignore @@ -0,0 +1 @@ +sqlite-test.db From f7c7a1541a167d98678eebac295604a208c5b4bb Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 16:41:45 -0500 Subject: [PATCH 075/511] test: add test runner --- test/run-tests.js | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 test/run-tests.js diff --git a/test/run-tests.js b/test/run-tests.js new file mode 100644 index 00000000..9a71ee84 --- /dev/null +++ b/test/run-tests.js @@ -0,0 +1,58 @@ +var fs = require("fs") + , spawn = require("child_process").spawn + , errorCount = 0 + , testCount = 0; + ; + +var files = fs.readdirSync("./"); + +files = files.filter(function (file) { + return (/^test-/.test(file)) ? true : false; +}); + +files.sort(); + +doNextTest(); + +function doTest(file) { + var test = spawn("node", [file]); + + process.stdout.write("Running test: " + file.replace(/\.js$/, "")); + process.stdout.write(" ... "); + + testCount += 1; + + test.on("exit", function (code, signal) { + if (code != 0) { + errorCount += 1; + + process.stdout.write("\033[01;31mfail \033[01;0m "); + } + else { + process.stdout.write("\033[01;32msuccess \033[01;0m "); + } + + process.stdout.write("\n"); + + doNextTest(); + }); +} + +function doNextTest() { + if (files.length) { + var testFile = files.shift(); + + doTest(testFile); + } + else { + //we're done, display results and exit accordingly + + if (errorCount) { + console.log("%s of %s tests failed", errorCount, testCount); + process.exit(errorCount); + } + else { + console.log("All tests were successful"); + } + } +} \ No newline at end of file From 20703d76ab1bbf12f59af488c2d3bbb4d85ca53e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 16:42:37 -0500 Subject: [PATCH 076/511] test: disable test-open-dont-close --- test/{ => disabled}/test-open-dont-close.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{ => disabled}/test-open-dont-close.js (100%) diff --git a/test/test-open-dont-close.js b/test/disabled/test-open-dont-close.js similarity index 100% rename from test/test-open-dont-close.js rename to test/disabled/test-open-dont-close.js From da0d2609d3ed848b6224fae88d780e7d88ee4712 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 16:43:32 -0500 Subject: [PATCH 077/511] test: test-query-drop-table: properly close the connection --- test/test-query-drop-table.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/test-query-drop-table.js b/test/test-query-drop-table.js index b2638c50..3e8c3166 100644 --- a/test/test-query-drop-table.js +++ b/test/test-query-drop-table.js @@ -1,15 +1,17 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database(); -db.open(common.connectionString, function(err) -{ - db.query("drop table test", function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - }); +db.open(common.connectionString, function(err) { + db.query("drop table test", function (err, data) { + if (err) { + console.error(err); + process.exit(1); + } + + console.error(data); + + db.close(function () { + }); + }); }); From 22afd1bc05d66b83c4417b517a3c36979552dd39 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 16:45:27 -0500 Subject: [PATCH 078/511] Add test script property to package.json for npm test --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index de11ab70..01658754 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "node": ">=0.7.0" }, "scripts": { - "preinstall": "node-gyp configure build" + "preinstall": "node-gyp configure build", + "test" : "cd test && node run-tests.js" }, "dependencies": { "bindings": "~1.0.0" From b54c703e69c0fd7fac52bb73b2063748c369d569 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 16:46:32 -0500 Subject: [PATCH 079/511] working on v0.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01658754..d0276b25 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.4.3", + "version": "0.5.0-pre", "main": "odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 0e381bbe506a57fcbf95e6ab94766a57d5631272 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 16:55:53 -0500 Subject: [PATCH 080/511] test: display warning for disabled tests --- test/run-tests.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/run-tests.js b/test/run-tests.js index 9a71ee84..ac924b91 100644 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -4,6 +4,12 @@ var fs = require("fs") , testCount = 0; ; +var filesDisabled = fs.readdirSync("./disabled"); + +if (filesDisabled.length) { + console.log("\033[01;31mWarning\033[01;0m : there are %s disabled tests\n", filesDisabled.length); +} + var files = fs.readdirSync("./"); files = files.filter(function (file) { @@ -17,7 +23,7 @@ doNextTest(); function doTest(file) { var test = spawn("node", [file]); - process.stdout.write("Running test: " + file.replace(/\.js$/, "")); + process.stdout.write("Running test : " + file.replace(/\.js$/, "")); process.stdout.write(" ... "); testCount += 1; From 47f86cfd54290b000de96f32ff052a97139a0236 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 17:35:04 -0500 Subject: [PATCH 081/511] test: enable timeout on test runner and more test assertions --- test/run-tests.js | 19 +++++++--- test/test-query-drop-table.js | 15 +++----- test/test-query-insert.js | 67 ++++++++++++++++++--------------- test/test-query-select-fetch.js | 39 ++++++++++++------- test/test-query-select.js | 24 ++++++------ 5 files changed, 93 insertions(+), 71 deletions(-) diff --git a/test/run-tests.js b/test/run-tests.js index ac924b91..e717aff6 100644 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -1,13 +1,14 @@ var fs = require("fs") , spawn = require("child_process").spawn , errorCount = 0 - , testCount = 0; + , testCount = 0 + , testTimeout = 5000 ; var filesDisabled = fs.readdirSync("./disabled"); if (filesDisabled.length) { - console.log("\033[01;31mWarning\033[01;0m : there are %s disabled tests\n", filesDisabled.length); + console.log("\n\033[01;31mWarning\033[01;0m : there are %s disabled tests\n", filesDisabled.length); } var files = fs.readdirSync("./"); @@ -21,7 +22,9 @@ files.sort(); doNextTest(); function doTest(file) { - var test = spawn("node", [file]); + var test = spawn("node", [file]) + , timer = null + ; process.stdout.write("Running test : " + file.replace(/\.js$/, "")); process.stdout.write(" ... "); @@ -29,6 +32,8 @@ function doTest(file) { testCount += 1; test.on("exit", function (code, signal) { + clearTimeout(timer); + if (code != 0) { errorCount += 1; @@ -42,6 +47,10 @@ function doTest(file) { doNextTest(); }); + + var timer = setTimeout(function () { + test.kill(); + },testTimeout); } function doNextTest() { @@ -54,11 +63,11 @@ function doNextTest() { //we're done, display results and exit accordingly if (errorCount) { - console.log("%s of %s tests failed", errorCount, testCount); + console.log("\nResults : %s of %s tests failed.\n", errorCount, testCount); process.exit(errorCount); } else { - console.log("All tests were successful"); + console.log("Results : All tests were successful."); } } } \ No newline at end of file diff --git a/test/test-query-drop-table.js b/test/test-query-drop-table.js index 3e8c3166..2e283ddf 100644 --- a/test/test-query-drop-table.js +++ b/test/test-query-drop-table.js @@ -1,17 +1,14 @@ var common = require("./common") , odbc = require("../odbc.js") - , db = new odbc.Database(); + , db = new odbc.Database() + , assert = require("assert") + ; db.open(common.connectionString, function(err) { - db.query("drop table test", function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - + common.dropTables(db, function (err, data) { db.close(function () { + assert.equal(err, null); + assert.deepEqual(data, []); }); }); }); diff --git a/test/test-query-insert.js b/test/test-query-insert.js index ab10dfe3..63d768e1 100644 --- a/test/test-query-insert.js +++ b/test/test-query-insert.js @@ -1,33 +1,40 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + , insertCount = 0; + ; -db.open(common.connectionString, function(err) -{ - db.query("insert into test (col1) values ('sandwich')", function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - }); - - db.query("insert into test (col1) values ('fish')", function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - }); - - db.query("insert into test (col1) values ('scarf')", function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - }); +process.on("uncaughtException", function (err) { + console.log("here", err); + + process.exit(1); }); + +db.open(common.connectionString, function(err) { + common.dropTables(db, function () { + common.createTables(db, function (err) { + assert.equal(err, null); + + db.query("insert into TEST (COLTEXT) values ('sandwich')", insertCallback); + db.query("insert into TEST (COLTEXT) values ('fish')", insertCallback); + db.query("insert into TEST (COLTEXT) values ('scarf')", insertCallback); + + }); + }); +}); + +function insertCallback(err, data) { + assert.equal(err, null); + assert.deepEqual(data, []); + + insertCount += 1; + + if (insertCount === 3) { + common.dropTables(db, function () { + db.close(function () { + console.error("Done"); + }); + }); + } +} \ No newline at end of file diff --git a/test/test-query-select-fetch.js b/test/test-query-select-fetch.js index d15c30c6..25e914a4 100644 --- a/test/test-query-select-fetch.js +++ b/test/test-query-select-fetch.js @@ -1,18 +1,29 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; -db.open(common.connectionString, function(err) -{ - db.queryResult('select \'a\' as test, datetime(\'now\') as nowish', function (err, result) { - if (err) { - console.error(err); - process.exit(1); - } - - result.fetch(function (err, data) { - console.error(data); - db.close(function () {}); - }); +process.on("uncaughtException", function (err) { + console.log(err); + + db.close(function () { + process.exit(1); + }); +}); + +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + db.queryResult("select 1 as COLINT, 'some test' as COLTEXT ", function (err, result) { + assert.equal(err, null); + assert.equal(result.constructor.name, "ODBCResult"); + + result.fetch(function (err, data) { + db.close(function () { + assert.deepEqual(data, { COLINT: '1', COLTEXT: 'some test' }); + }); }); + }); }); diff --git a/test/test-query-select.js b/test/test-query-select.js index f3e39018..7677911d 100644 --- a/test/test-query-select.js +++ b/test/test-query-select.js @@ -1,16 +1,14 @@ var common = require("./common") - , odbc = require("../odbc.js") - , db = new odbc.Database(); + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; -db.open(common.connectionString, function(err) -{ - db.query('select \'a\' as test, datetime(\'now\') as nowish, * from test', function (err, data) { - if (err) { - console.error(err); - process.exit(1); - } - - console.error(data); - db.close(function () {}); - }); +db.open(common.connectionString, function(err) { + db.query("select 1 as COLINT, 'some test' as COLTEXT", function (err, data) { + db.close(function () { + assert.equal(err, null); + assert.deepEqual(data, [{ COLINT: '1', COLTEXT: 'some test' }]); + }); + }); }); From 41dc9bfbcad4fe22a69b4137c7032e7bc1549403 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 17:39:21 -0500 Subject: [PATCH 082/511] test: added test-query-select-fetchAll --- test/test-query-select-fetchAll.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/test-query-select-fetchAll.js diff --git a/test/test-query-select-fetchAll.js b/test/test-query-select-fetchAll.js new file mode 100644 index 00000000..a8430536 --- /dev/null +++ b/test/test-query-select-fetchAll.js @@ -0,0 +1,29 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; + +process.on("uncaughtException", function (err) { + console.log(err); + + db.close(function () { + process.exit(1); + }); +}); + +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + db.queryResult("select 1 as COLINT, 'some test' as COLTEXT ", function (err, result) { + assert.equal(err, null); + assert.equal(result.constructor.name, "ODBCResult"); + + result.fetchAll(function (err, data) { + db.close(function () { + assert.deepEqual(data, [{ COLINT: '1', COLTEXT: 'some test' }]); + }); + }); + }); +}); From ceaaa3f7ec307632f1baecef0f4f990c6c9a1b21 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 17:45:34 -0500 Subject: [PATCH 083/511] test: return more than one result for the fetchAll test --- test/test-query-select-fetchAll.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/test/test-query-select-fetchAll.js b/test/test-query-select-fetchAll.js index a8430536..6f9aeba9 100644 --- a/test/test-query-select-fetchAll.js +++ b/test/test-query-select-fetchAll.js @@ -4,25 +4,20 @@ var common = require("./common") , assert = require("assert") ; -process.on("uncaughtException", function (err) { - console.log(err); - - db.close(function () { - process.exit(1); - }); -}); - db.open(common.connectionString, function(err) { assert.equal(err, null); assert.equal(db.connected, true); - db.queryResult("select 1 as COLINT, 'some test' as COLTEXT ", function (err, result) { + db.queryResult("select 1 as COLINT, 'some test' as COLTEXT union select 2, 'something else' ", function (err, result) { assert.equal(err, null); assert.equal(result.constructor.name, "ODBCResult"); result.fetchAll(function (err, data) { db.close(function () { - assert.deepEqual(data, [{ COLINT: '1', COLTEXT: 'some test' }]); + assert.deepEqual(data, [ + {"COLINT":1,"COLTEXT":"some test"} + ,{"COLINT":2,"COLTEXT":"something else"} + ]); }); }); }); From 53595ecc757a4d2f3397385582017043f354424f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 17:45:55 -0500 Subject: [PATCH 084/511] test: remove proces.on('uncaughtException'...) --- test/test-date.js | 8 -------- test/test-query-insert.js | 6 ------ test/test-query-select-fetch.js | 8 -------- 3 files changed, 22 deletions(-) diff --git a/test/test-date.js b/test/test-date.js index 0f05105b..f82bd727 100644 --- a/test/test-date.js +++ b/test/test-date.js @@ -4,14 +4,6 @@ var common = require("./common") , assert = require("assert") ; -process.on('uncaughtException', function (err) { - console.log('Failure', err); - - db.close(function () { - process.exit(1); - }); -}); - db.open(common.connectionString, function(err) { assert.equal(err, null); assert.equal(db.connected, true); diff --git a/test/test-query-insert.js b/test/test-query-insert.js index 63d768e1..c762684e 100644 --- a/test/test-query-insert.js +++ b/test/test-query-insert.js @@ -5,12 +5,6 @@ var common = require("./common") , insertCount = 0; ; -process.on("uncaughtException", function (err) { - console.log("here", err); - - process.exit(1); -}); - db.open(common.connectionString, function(err) { common.dropTables(db, function () { common.createTables(db, function (err) { diff --git a/test/test-query-select-fetch.js b/test/test-query-select-fetch.js index 25e914a4..090a8b27 100644 --- a/test/test-query-select-fetch.js +++ b/test/test-query-select-fetch.js @@ -4,14 +4,6 @@ var common = require("./common") , assert = require("assert") ; -process.on("uncaughtException", function (err) { - console.log(err); - - db.close(function () { - process.exit(1); - }); -}); - db.open(common.connectionString, function(err) { assert.equal(err, null); assert.equal(db.connected, true); From 7f5a194ab534d63172c603a01e97c35353a53ccd Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 6 Nov 2012 18:49:50 -0500 Subject: [PATCH 085/511] test: more generically test database descriptions, make tableName and databaseName configurable in common --- test/common.js | 21 +++++++----- test/test-describe-column.js | 27 +++------------ test/test-describe-database.js | 11 ++---- test/test-describe-table.js | 62 +++------------------------------- test/test-open-close.js | 4 +-- test/test-query-insert.js | 6 ++-- 6 files changed, 28 insertions(+), 103 deletions(-) diff --git a/test/common.js b/test/common.js index d1f5889d..1a8c9c30 100644 --- a/test/common.js +++ b/test/common.js @@ -1,23 +1,26 @@ -module.exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; -//module.exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; -//module.exports.connectionString = process.env.ODBC_CONNETION_STRING; +exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; +//exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; +//exports.connectionString = process.env.ODBC_CONNETION_STRING; -module.exports.connectionObject = { +exports.connectionObject = { DRIVER : "{SQLITE3}", DATABASE : "data/sqlite-test.db" }; -module.exports.connections = [ +exports.connections = [ { DRIVER : "{SQLITE3}", DATABASE : "data/sqlite-test.db" } ]; -module.exports.dropTables = function (db, cb) { - db.query("drop table TEST", cb); +exports.databaseName = "MAIN"; +exports.tableName = "NODE_ODBC_TEST_TABLE"; + +exports.dropTables = function (db, cb) { + db.query("drop table " + exports.tableName, cb); }; -module.exports.createTables = function (db, cb) { - db.query("create table TEST(COLINT INTEGER, COLDATETIME DATETIME, COLTEXT TEXT)", cb); +exports.createTables = function (db, cb) { + db.query("create table " + exports.tableName + " (COLINT INTEGER, COLDATETIME DATETIME, COLTEXT TEXT)", cb); }; \ No newline at end of file diff --git a/test/test-describe-column.js b/test/test-describe-column.js index 0fde1936..583ae9e7 100644 --- a/test/test-describe-column.js +++ b/test/test-describe-column.js @@ -9,33 +9,14 @@ db.open(common.connectionString, function(err) { common.dropTables(db, function () { common.createTables(db, function () { - db.describe({ - database : 'MAIN', - table : 'TEST', + database : common.databaseName, + table : common.tableName, column : 'COLDATETIME' }, function (err, data) { db.close(function () { - assert.deepEqual(data, [{ - TABLE_QUALIFIER: '', - TABLE_OWNER: '', - TABLE_NAME: 'TEST', - COLUMN_NAME: 'COLDATETIME', - DATA_TYPE: 11, - TYPE_NAME: 'DATETIME', - PRECISION: 3, - LENGTH: 32, - SCALE: '10', - RADIX: '0', - NULLABLE: 1, - REMARKS: null, - COLUMN_DEF: 'NULL', - SQL_DATA_TYPE: '11', - SQL_DATETIME_SUB: null, - CHAR_OCTET_LENGTH: 16384, - ORDINAL_POSITION: 2, - IS_NULLABLE: 'YES' - }]); + assert.equal(err, null); + assert.ok(data.length, "No records returned when attempting to describe the column COLDATETIME"); }); }); }); diff --git a/test/test-describe-database.js b/test/test-describe-database.js index 81c58555..cf24f657 100644 --- a/test/test-describe-database.js +++ b/test/test-describe-database.js @@ -10,16 +10,11 @@ db.open(common.connectionString, function(err) { common.dropTables(db, function () { common.createTables(db, function () { db.describe({ - database : 'MAIN' + database : common.databaseName }, function (err, data) { db.close(function () { - assert.deepEqual(data, [{ - TABLE_QUALIFIER: null, - TABLE_OWNER: null, - TABLE_NAME: 'TEST', - TABLE_TYPE: 'TABLE', - REMARKS: null - }]); + assert.equal(err, null); + assert.ok(data.length, "No records returned when attempting to describe the database"); }); }); }); diff --git a/test/test-describe-table.js b/test/test-describe-table.js index c0c5fea2..5e464be5 100644 --- a/test/test-describe-table.js +++ b/test/test-describe-table.js @@ -11,66 +11,12 @@ db.open(common.connectionString, function(err) { common.createTables(db, function () { db.describe({ - database : 'MAIN' - , table : 'TEST' + database : common.databaseName + , table : common.tableName }, function (err, data) { db.close(function () { - assert.deepEqual(data, [{ - TABLE_QUALIFIER: '', - TABLE_OWNER: '', - TABLE_NAME: 'TEST', - COLUMN_NAME: 'COLINT', - DATA_TYPE: 4, - TYPE_NAME: 'INTEGER', - PRECISION: 9, - LENGTH: '10', - SCALE: 10, - RADIX: 0, - NULLABLE: '1', - REMARKS: null, - COLUMN_DEF: 'NULL', - SQL_DATA_TYPE: '4', - SQL_DATETIME_SUB: null, - CHAR_OCTET_LENGTH: 16384, - ORDINAL_POSITION: '1', - IS_NULLABLE: 'YES' }, - { TABLE_QUALIFIER: '', - TABLE_OWNER: '', - TABLE_NAME: 'TEST', - COLUMN_NAME: 'COLDATETIME', - DATA_TYPE: 11, - TYPE_NAME: 'DATETIME', - PRECISION: 3, - LENGTH: '32', - SCALE: 10, - RADIX: 0, - NULLABLE: '1', - REMARKS: null, - COLUMN_DEF: 'NULL', - SQL_DATA_TYPE: '11', - SQL_DATETIME_SUB: null, - CHAR_OCTET_LENGTH: 16384, - ORDINAL_POSITION: '2', - IS_NULLABLE: 'YES' }, - { TABLE_QUALIFIER: '', - TABLE_OWNER: '', - TABLE_NAME: 'TEST', - COLUMN_NAME: 'COLTEXT', - DATA_TYPE: -1, - TYPE_NAME: 'TEXT', - PRECISION: 0, - LENGTH: '65536', - SCALE: 10, - RADIX: 0, - NULLABLE: '1', - REMARKS: null, - COLUMN_DEF: 'NULL', - SQL_DATA_TYPE: '-1', - SQL_DATETIME_SUB: null, - CHAR_OCTET_LENGTH: 16384, - ORDINAL_POSITION: '3', - IS_NULLABLE: 'YES' - }]); + assert.equal(err, null); + assert.ok(data.length, "No records returned when attempting to describe the tabe " + common.tableName); }); }); }); diff --git a/test/test-open-close.js b/test/test-open-close.js index 9f625a85..da88b51f 100644 --- a/test/test-open-close.js +++ b/test/test-open-close.js @@ -5,7 +5,7 @@ var common = require("./common") assert.equal(db.connected, false); -db.query("select * from test", function (err, rs, moreResultSets) { +db.query("select * from " + common.tableName, function (err, rs, moreResultSets) { assert.deepEqual(err, { message: 'Connection not open.' }); assert.deepEqual(rs, []); assert.equal(moreResultSets, false); @@ -19,7 +19,7 @@ db.open(common.connectionString, function(err) { db.close(function () { assert.equal(db.connected, false); - db.query("select * from test", function (err, rs, moreResultSets) { + db.query("select * from " + common.tableName, function (err, rs, moreResultSets) { assert.deepEqual(err, { message: 'Connection not open.' }); assert.deepEqual(rs, []); assert.equal(moreResultSets, false); diff --git a/test/test-query-insert.js b/test/test-query-insert.js index c762684e..bc67b8b1 100644 --- a/test/test-query-insert.js +++ b/test/test-query-insert.js @@ -10,9 +10,9 @@ db.open(common.connectionString, function(err) { common.createTables(db, function (err) { assert.equal(err, null); - db.query("insert into TEST (COLTEXT) values ('sandwich')", insertCallback); - db.query("insert into TEST (COLTEXT) values ('fish')", insertCallback); - db.query("insert into TEST (COLTEXT) values ('scarf')", insertCallback); + db.query("insert into " + common.tableName + " (COLTEXT) values ('sandwich')", insertCallback); + db.query("insert into " + common.tableName + " (COLTEXT) values ('fish')", insertCallback); + db.query("insert into " + common.tableName + " (COLTEXT) values ('scarf')", insertCallback); }); }); From 8b0205aebf763b2621f90cb3fce7186386a2709a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 7 Nov 2012 12:16:39 -0500 Subject: [PATCH 086/511] test: add unicode test --- test/test-query-select-unicode.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 test/test-query-select-unicode.js diff --git a/test/test-query-select-unicode.js b/test/test-query-select-unicode.js new file mode 100644 index 00000000..9224cc55 --- /dev/null +++ b/test/test-query-select-unicode.js @@ -0,0 +1,15 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + db.query("select 'ף צ ץ ק ר ש תכ ך ל מ ם נ ן ס ע פ 電电電買买買開开開東东東車车車' as UNICODETEXT", function (err, data) { + db.close(function () { + console.log(data); + assert.equal(err, null); + assert.deepEqual(data, [{ UNICODETEXT: 'ף צ ץ ק ר ש תכ ך ל מ ם נ ן ס ע פ 電电電買买買開开開東东東車车車' }]); + }); + }); +}); From 31b44077ebbe689599acf849a86697964d5da4de Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 7 Nov 2012 12:51:20 -0500 Subject: [PATCH 087/511] Cast the uint16_t* buffer to char* buffer and don't specify wide character in SQLGetData --- src/odbc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 8fdae8af..6e0cc14c 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -1163,8 +1163,8 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, default : ret = SQLGetData( hStmt, column.index, - SQL_C_WCHAR, - (uint16_t *) buffer, + SQL_C_CHAR, + (char *) buffer, bufferLength, (SQLLEN *) &len); @@ -1174,7 +1174,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } else { //return scope.Close(String::New(buffer)); - return String::New(buffer); + return String::New((char*) buffer); } } } From b81e6f72a151dd9749499fd6a50cbd12806fe6e3 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 7 Nov 2012 16:15:49 -0500 Subject: [PATCH 088/511] Add ODBC::Free() which is used to close and free handles; called from ODBC::Close() and the destructor --- src/odbc.cpp | 31 ++++++++++++++++++++++++------- src/odbc.h | 4 +++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 6e0cc14c..739e1ed5 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -76,6 +76,29 @@ void ODBC::Init(v8::Handle target) { uv_mutex_init(&ODBC::g_odbcMutex); } +ODBC::~ODBC() { + this->Free(); +} + +void ODBC::Free() { + if (m_hDBC || m_hEnv) { + uv_mutex_lock(&ODBC::g_odbcMutex); + + if (m_hDBC) { + SQLDisconnect(m_hDBC); + SQLFreeHandle(SQL_HANDLE_DBC, m_hDBC); + m_hDBC = NULL; + } + + if (m_hEnv) { + SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv); + m_hEnv = NULL; + } + + uv_mutex_unlock(&ODBC::g_odbcMutex); + } +} + Handle ODBC::New(const Arguments& args) { HandleScope scope; ODBC* dbo = new ODBC(); @@ -309,13 +332,7 @@ void ODBC::UV_Close(uv_work_t* req) { close_request* close_req = (close_request *)(req->data); ODBC* dbo = close_req->dbo; - uv_mutex_lock(&ODBC::g_odbcMutex); - - SQLDisconnect(dbo->m_hDBC); - SQLFreeHandle(SQL_HANDLE_ENV, dbo->m_hEnv); - SQLFreeHandle(SQL_HANDLE_DBC, dbo->m_hDBC); - - uv_mutex_unlock(&ODBC::g_odbcMutex); + dbo->Free(); } Handle ODBC::Close(const Arguments& args) { diff --git a/src/odbc.h b/src/odbc.h index 522bd22e..984bdf67 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -65,10 +65,12 @@ class ODBC : public node::ObjectWrap { static Parameter* GetParametersFromArray (Local values, int* paramCount); + void Free(); + protected: ODBC() {} - ~ODBC() {} + ~ODBC(); static Handle New(const Arguments& args); From 73e92aea11dbfcf353bba020bb9b8a2023fca4a2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 7 Nov 2012 16:16:11 -0500 Subject: [PATCH 089/511] Free the Statement, not the connection. Remove commented code --- src/odbc_result.cpp | 137 +------------------------------------------- src/odbc_result.h | 1 - 2 files changed, 3 insertions(+), 135 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 5aeb3bdc..57b3e7fd 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -61,11 +61,11 @@ ODBCResult::~ODBCResult() { } void ODBCResult::Free() { - if (m_hDBC) { + if (m_hSTMT) { uv_mutex_lock(&ODBC::g_odbcMutex); - SQLFreeHandle(SQL_HANDLE_DBC, m_hDBC); - m_hDBC = NULL; + SQLFreeHandle(SQL_HANDLE_STMT, m_hSTMT); + m_hSTMT = NULL; uv_mutex_unlock(&ODBC::g_odbcMutex); @@ -340,7 +340,6 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req) { self->Unref(); } - Handle ODBCResult::Close(const Arguments& args) { HandleScope scope; @@ -361,133 +360,3 @@ Handle ODBCResult::MoreResults(const Arguments& args) { return scope.Close(SQL_SUCCEEDED(ret) ? True() : False()); } - -/* - * fetchAll - -//Loop through all result sets - do { - Local rows = Array::New(); - - //Retrieve and store all columns and their attributes - Column *columns = GetColumns(self->m_hStmt, &colCount); - - if (colCount > 0) { - int count = 0; - - //I dont think odbc will tell how many rows are returned, loop until out - while(true) { - - ret = SQLFetch(self->m_hStmt); - - //TODO: Do something to enable/disable dumping these info messages to - //the console. - if (ret == SQL_SUCCESS_WITH_INFO ) { - char errorMessage[512]; - char errorSQLState[128]; - - SQLError( self->m_hEnv, - self->m_hDBC, - self->m_hStmt, - (SQLCHAR *) errorSQLState, - NULL, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - NULL); - - printf("UV_Query => %s\n", errorMessage); - printf("UV_Query => %s\n", errorSQLState); - } - - if (ret == SQL_ERROR) { - objError = Object::New(); - - char errorMessage[512]; - char errorSQLState[128]; - - SQLError( self->m_hEnv, - self->m_hDBC, - self->m_hStmt, - (SQLCHAR *) errorSQLState, - NULL, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - NULL); - - errorCount++; - - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), - String::New("[node-odbc] Error in SQLFetch")); - objError->Set(String::New("message"), String::New(errorMessage)); - objError->Set(String::New("query"), String::New(prep_req->sql)); - - //emit an error event immidiately. - Local args[1]; - args[0] = objError; - prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); - - break; - } - - if (ret == SQL_NO_DATA) { - break; - } - - if (self->mode == MODE_CALLBACK_FOR_EACH) { - Handle args[2]; - - args[0] = Null(); - args[1] = GetRecordTuple( self->m_hStmt, - columns, - &colCount, - buf, - MAX_VALUE_SIZE - 1); - - prep_req->cb->Call(Context::GetCurrent()->Global(), 2, args); - } - else { - rows->Set( Integer::New(count), - GetRecordTuple( self->m_hStmt, - columns, - &colCount, - buf, - MAX_VALUE_SIZE - 1)); - } - - count++; - } - - FreeColumns(columns, &colCount); - } - - //move to the next result set - ret = SQLMoreResults( self->m_hStmt ); - - //Only trigger an emit if there are columns OR if this is the last result - //and none others have been emitted odbc will process individual statments - //like select @something = 1 as a recordset even though it doesn't have any - //columns. We don't want to emit those unless there are actually columns - if (colCount > 0 || ( ret != SQL_SUCCESS && emitCount == 0 )) { - emitCount++; - - Local args[3]; - - if (errorCount) { - args[0] = objError; - } - else { - args[0] = Local::New(Null()); - } - - args[1] = rows; - - //true or false, are there more result sets to follow this emit? - args[2] = Local::New((ret == SQL_SUCCESS) ? True() : False()); - - prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); - } - } - while ( self->canHaveMoreResults && ret == SQL_SUCCESS ); - - */ \ No newline at end of file diff --git a/src/odbc_result.h b/src/odbc_result.h index 786e77c8..60dbab78 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -25,7 +25,6 @@ class ODBCResult : public node::ObjectWrap { void Free(); - protected: ODBCResult() {}; From 8f740569f99b9c8b5e945802045e404c748d712e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 7 Nov 2012 18:23:47 -0500 Subject: [PATCH 090/511] test: add failing tests for parameters, only strings are currently working --- test/test-param-select-with-booleans-only.js | 21 ++++++++++++++++++ test/test-param-select-with-decimals-only.js | 20 +++++++++++++++++ test/test-param-select-with-numbers-only.js | 22 +++++++++++++++++++ ...t.js => test-param-select-with-numbers.js} | 0 test/test-param-select-with-strings-only.js | 22 +++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 test/test-param-select-with-booleans-only.js create mode 100644 test/test-param-select-with-decimals-only.js create mode 100644 test/test-param-select-with-numbers-only.js rename test/{test-param-select.js => test-param-select-with-numbers.js} (100%) create mode 100644 test/test-param-select-with-strings-only.js diff --git a/test/test-param-select-with-booleans-only.js b/test/test-param-select-with-booleans-only.js new file mode 100644 index 00000000..eb045aba --- /dev/null +++ b/test/test-param-select-with-booleans-only.js @@ -0,0 +1,21 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert"); + + +db.open(common.connectionString, function (err) { + assert.equal(err, null); + + db.query("select ? as TRUECOL, ? as FALSECOL " + , [true, false] + , function (err, data, more) { + db.close(function () { + assert.equal(err, null); + assert.deepEqual(data, [{ + TRUECOL: true, + FALSECOL: false + }]); + }); + }); +}); diff --git a/test/test-param-select-with-decimals-only.js b/test/test-param-select-with-decimals-only.js new file mode 100644 index 00000000..dc015935 --- /dev/null +++ b/test/test-param-select-with-decimals-only.js @@ -0,0 +1,20 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert"); + + +db.open(common.connectionString, function (err) { + assert.equal(err, null); + + db.query("select ? as DECCOL1 " + , [5.5] + , function (err, data, more) { + db.close(function () { + assert.equal(err, null); + assert.deepEqual(data, [{ + DECCOL1: 5.5 + }]); + }); + }); +}); diff --git a/test/test-param-select-with-numbers-only.js b/test/test-param-select-with-numbers-only.js new file mode 100644 index 00000000..6b4409d6 --- /dev/null +++ b/test/test-param-select-with-numbers-only.js @@ -0,0 +1,22 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert"); + + +db.open(common.connectionString, function (err) { + assert.equal(err, null); + + db.query("select ? as INTCOL1, ? as INTCOL2, ? as INTCOL3 " + , [5, 3, 1] + , function (err, data, more) { + db.close(function () { + assert.equal(err, null); + assert.deepEqual(data, [{ + INTCOL1: 5, + INTCOL2: 3, + INTCOL3: 1 + }]); + }); + }); +}); diff --git a/test/test-param-select.js b/test/test-param-select-with-numbers.js similarity index 100% rename from test/test-param-select.js rename to test/test-param-select-with-numbers.js diff --git a/test/test-param-select-with-strings-only.js b/test/test-param-select-with-strings-only.js new file mode 100644 index 00000000..104aa8a8 --- /dev/null +++ b/test/test-param-select-with-strings-only.js @@ -0,0 +1,22 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert"); + + +db.open(common.connectionString, function (err) { + assert.equal(err, null); + + db.query("select ? as TEXTCOL, ? as TEXTCOL2, ? as TEXTCOL3" + , ["fish", "asdf", "1234"] + , function (err, data, more) { + db.close(function () { + assert.equal(err, null); + assert.deepEqual(data, [{ + TEXTCOL: 'fish', + TEXTCOL2: 'asdf', + TEXTCOL3: '1234' + }]); + }); + }); +}); From 5e363d255d64e63c353ef803d39505f4626d2a5d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 7 Nov 2012 18:25:57 -0500 Subject: [PATCH 091/511] Reformat SQLBindParameter call --- src/odbc.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 739e1ed5..c4432d0f 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -795,8 +795,20 @@ void ODBC::UV_QueryAll(uv_work_t* req) { for (int i = 0; i < prep_req->paramCount; i++) { prm = prep_req->params[i]; - ret = SQLBindParameter(prep_req->hSTMT, i + 1, SQL_PARAM_INPUT, prm.c_type, prm.type, prm.size, 0, prm.buffer, prm.buffer_length, &prm.length); - if (ret == SQL_ERROR) {break;} + ret = SQLBindParameter( prep_req->hSTMT, //StatementHandle + i + 1, //ParameterNumber + SQL_PARAM_INPUT, //InputOutputType + prm.c_type, //ValueType + prm.type, //ParameterType + prm.size, //ColumnSize + 0, //DecimalDigits + prm.buffer, //ParameterValuePtr + prm.buffer_length, //BufferLength + &prm.length); //StrLen_or_IndPtr + + if (ret == SQL_ERROR) { + break; + } } if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { From 029cec637f326b67595fa50e6e06d71248cc9f0f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 11:36:58 -0500 Subject: [PATCH 092/511] test: added test for binding parameters with a null value --- test/{disabled => }/test-open-dont-close.js | 0 test/test-param-select-with-nulls-mixed.js | 22 +++++++++++++++++++++ 2 files changed, 22 insertions(+) rename test/{disabled => }/test-open-dont-close.js (100%) create mode 100644 test/test-param-select-with-nulls-mixed.js diff --git a/test/disabled/test-open-dont-close.js b/test/test-open-dont-close.js similarity index 100% rename from test/disabled/test-open-dont-close.js rename to test/test-open-dont-close.js diff --git a/test/test-param-select-with-nulls-mixed.js b/test/test-param-select-with-nulls-mixed.js new file mode 100644 index 00000000..c8ec2484 --- /dev/null +++ b/test/test-param-select-with-nulls-mixed.js @@ -0,0 +1,22 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert"); + + +db.open(common.connectionString, function (err) { + assert.equal(err, null); + + db.query("select ? as TEXTCOL1, ? as TEXTCOL2, ? as NULLCOL1 " + , ["something", "something", null] + , function (err, data, more) { + db.close(function () { + assert.equal(err, null); + assert.deepEqual(data, [{ + TEXTCOL1: "something", + TEXTCOL2: "something", + NULLCOL1: null + }]); + }); + }); +}); From 713a203fd30583ce5bd0594ab2e994e33ceb1330 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 11:44:11 -0500 Subject: [PATCH 093/511] test: added memleak test, and tests to check that you can require and it will exit --- test/test-instantiate-one-and-end.js | 9 +++++++++ test/test-memory-leaks-new-objects.js | 22 ++++++++++++++++++++++ test/test-require-and-end.js | 8 ++++++++ 3 files changed, 39 insertions(+) create mode 100644 test/test-instantiate-one-and-end.js create mode 100644 test/test-memory-leaks-new-objects.js create mode 100644 test/test-require-and-end.js diff --git a/test/test-instantiate-one-and-end.js b/test/test-instantiate-one-and-end.js new file mode 100644 index 00000000..315bd95c --- /dev/null +++ b/test/test-instantiate-one-and-end.js @@ -0,0 +1,9 @@ +var odbc = require("../") + , db = new odbc.Database() + ; + +//This test should just exit. The only reason it should stay open is if a +//connection has been established. But all we have done here is instantiate +//the object. + +console.log("done"); \ No newline at end of file diff --git a/test/test-memory-leaks-new-objects.js b/test/test-memory-leaks-new-objects.js new file mode 100644 index 00000000..79ade181 --- /dev/null +++ b/test/test-memory-leaks-new-objects.js @@ -0,0 +1,22 @@ +var odbc = require("../") + , openCount = 100 + , start = process.memoryUsage() + , stop = process.memoryUsage() + , x = 100 + ; + +gc(); + +start = process.memoryUsage(); + +for (x = 0; x < openCount; x++ ) { + (function () { + var db = new odbc.Database(); + })(); +} + +gc(); + +stop = process.memoryUsage(); + +console.log(start.heapUsed, stop.heapUsed); \ No newline at end of file diff --git a/test/test-require-and-end.js b/test/test-require-and-end.js new file mode 100644 index 00000000..37153945 --- /dev/null +++ b/test/test-require-and-end.js @@ -0,0 +1,8 @@ +var odbc = require("../") + ; + +//This test should just exit. This tests an issue where +//the C++ ODBC::Init function was causing the event loop to +//stay alive + +console.log("done"); \ No newline at end of file From e9832603082ecce4347be2e4a5af48615ada5923 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 12:00:18 -0500 Subject: [PATCH 094/511] Added DEBUG_PRINTF() and used it --- src/odbc.cpp | 11 +++++++++++ src/odbc.h | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index c4432d0f..64e73d35 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -37,6 +37,7 @@ uv_async_t ODBC::g_async; Persistent ODBC::constructor_template; void ODBC::Init(v8::Handle target) { + DEBUG_PRINTF("ODBC::Init\n"); HandleScope scope; Local t = FunctionTemplate::New(New); @@ -77,10 +78,12 @@ void ODBC::Init(v8::Handle target) { } ODBC::~ODBC() { + DEBUG_PRINTF("ODBC::~ODBC\n"); this->Free(); } void ODBC::Free() { + DEBUG_PRINTF("ODBC::Free\n"); if (m_hDBC || m_hEnv) { uv_mutex_lock(&ODBC::g_odbcMutex); @@ -100,6 +103,7 @@ void ODBC::Free() { } Handle ODBC::New(const Arguments& args) { + DEBUG_PRINTF("ODBC::New\n"); HandleScope scope; ODBC* dbo = new ODBC(); @@ -111,6 +115,7 @@ Handle ODBC::New(const Arguments& args) { } void ODBC::WatcherCallback(uv_async_t *w, int revents) { + DEBUG_PRINTF("ODBC::WatcherCallback\n"); //i don't know if we need to do anything here } @@ -145,6 +150,7 @@ Handle ODBC::ConnectedGetter(Local property, const AccessorInfo & } void ODBC::UV_AfterOpen(uv_work_t* req) { + DEBUG_PRINTF("ODBC::UV_AfterOpen\n"); HandleScope scope; open_request* open_req = (open_request *)(req->data); @@ -213,6 +219,7 @@ void ODBC::UV_AfterOpen(uv_work_t* req) { } void ODBC::UV_Open(uv_work_t* req) { + DEBUG_PRINTF("ODBC::UV_Open\n"); open_request* open_req = (open_request *)(req->data); ODBC* self = open_req->dbo->self(); @@ -261,6 +268,7 @@ void ODBC::UV_Open(uv_work_t* req) { } Handle ODBC::Open(const Arguments& args) { + DEBUG_PRINTF("ODBC::Open\n"); HandleScope scope; REQ_STR_ARG(0, connection); @@ -289,6 +297,7 @@ Handle ODBC::Open(const Arguments& args) { } void ODBC::UV_AfterClose(uv_work_t* req) { + DEBUG_PRINTF("ODBC::UV_AfterClose\n"); HandleScope scope; close_request* close_req = (close_request *)(req->data); @@ -329,6 +338,7 @@ void ODBC::UV_AfterClose(uv_work_t* req) { } void ODBC::UV_Close(uv_work_t* req) { + DEBUG_PRINTF("ODBC::UV_Close\n"); close_request* close_req = (close_request *)(req->data); ODBC* dbo = close_req->dbo; @@ -336,6 +346,7 @@ void ODBC::UV_Close(uv_work_t* req) { } Handle ODBC::Close(const Arguments& args) { + DEBUG_PRINTF("ODBC::Close\n"); HandleScope scope; REQ_FUN_ARG(0, cb); diff --git a/src/odbc.h b/src/odbc.h index 984bdf67..0a9f087c 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -146,6 +146,12 @@ struct query_request { int result; }; +#ifdef DEBUG + #define DEBUG_PRINTF(...) fprintf(stdout, __VA_ARGS__) +#else + #define DEBUG_PRINTF(...) (void)0 +#endif + #define REQ_ARGS(N) \ if (args.Length() < (N)) \ return ThrowException(Exception::TypeError( \ From 390fb7dec0a9d9ada6ffc5d83874da2c303826f2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 12:01:42 -0500 Subject: [PATCH 095/511] test: expose gc() when running tests --- test/run-tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/run-tests.js b/test/run-tests.js index e717aff6..3602b1ad 100644 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -22,7 +22,7 @@ files.sort(); doNextTest(); function doTest(file) { - var test = spawn("node", [file]) + var test = spawn("node", ['--expose_gc',file]) , timer = null ; From e899e072b0147aecb47f8feccb31b9a5f5cb6ac6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 12:02:22 -0500 Subject: [PATCH 096/511] test: keep track of when tests failed becuase of a timeout --- test/run-tests.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/run-tests.js b/test/run-tests.js index 3602b1ad..743f2a7a 100644 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -24,6 +24,7 @@ doNextTest(); function doTest(file) { var test = spawn("node", ['--expose_gc',file]) , timer = null + , timedOut = false; ; process.stdout.write("Running test : " + file.replace(/\.js$/, "")); @@ -38,6 +39,10 @@ function doTest(file) { errorCount += 1; process.stdout.write("\033[01;31mfail \033[01;0m "); + + if (timedOut) { + process.stdout.write("(Timed Out)"); + } } else { process.stdout.write("\033[01;32msuccess \033[01;0m "); @@ -49,6 +54,7 @@ function doTest(file) { }); var timer = setTimeout(function () { + timedOut = true; test.kill(); },testTimeout); } @@ -70,4 +76,4 @@ function doNextTest() { console.log("Results : All tests were successful."); } } -} \ No newline at end of file +} From d3b71644d4729276c6353e0d83556bb3027a5a43 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 15:19:14 -0500 Subject: [PATCH 097/511] Fix ref/unref, initialize m_hDBC and m_hEnv in New() to Null --- src/odbc.cpp | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 64e73d35..bb3b24a5 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -68,11 +68,22 @@ void ODBC::Init(v8::Handle target) { scope.Close(Undefined()); +#if NODE_VERSION_AT_LEAST(0, 7, 9) // Initialize uv_async so that we can prevent node from exiting uv_async_init( uv_default_loop(), &ODBC::g_async, ODBC::WatcherCallback); + // Not sure if the init automatically calls uv_ref() because there is weird + // behavior going on. When ODBC::Init is called which initializes the + // uv_async_t g_async above, there seems to be a ref which will keep it alive + // but we only want this available so that we can uv_ref() later on when + // we have a connection. + // so to work around this, I am possibly mistakenly calling uv_unref() once + // so that there are no references on the loop. + uv_unref((uv_handle_t *)&ODBC::g_async); +#endif + // Initialize the cross platform mutex provided by libuv uv_mutex_init(&ODBC::g_odbcMutex); } @@ -110,6 +121,8 @@ Handle ODBC::New(const Arguments& args) { dbo->Wrap(args.Holder()); dbo->mode = 1; dbo->connected = false; + dbo->m_hDBC = NULL; + dbo->m_hEnv = NULL; return scope.Close(args.Holder()); } @@ -194,6 +207,13 @@ void ODBC::UV_AfterOpen(uv_work_t* req) { if (!err) { self->connected = true; + + //only uv_ref if the connection was successful +#if NODE_VERSION_AT_LEAST(0, 7, 9) + uv_ref((uv_handle_t *)&ODBC::g_async); +#else + uv_ref(uv_default_loop()); +#endif } TryCatch try_catch; @@ -206,12 +226,6 @@ void ODBC::UV_AfterOpen(uv_work_t* req) { } open_req->cb.Dispose(); - -#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_ref((uv_handle_t *)&ODBC::g_async); -#else - uv_ref(uv_default_loop()); -#endif free(open_req); free(req); @@ -293,7 +307,7 @@ Handle ODBC::Open(const Arguments& args) { dbo->Ref(); - return scope.Close(Undefined()); + return scope.Close(args.Holder()); } void ODBC::UV_AfterClose(uv_work_t* req) { @@ -313,6 +327,13 @@ void ODBC::UV_AfterClose(uv_work_t* req) { } else { dbo->connected = false; + + //only unref if the connection was closed +#if NODE_VERSION_AT_LEAST(0, 7, 9) + uv_unref((uv_handle_t *)&ODBC::g_async); +#else + uv_unref(uv_default_loop()); +#endif } TryCatch try_catch; @@ -326,12 +347,6 @@ void ODBC::UV_AfterClose(uv_work_t* req) { close_req->cb.Dispose(); -#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_unref((uv_handle_t *)&ODBC::g_async); -#else - uv_unref(uv_default_loop()); -#endif - free(close_req); free(req); scope.Close(Undefined()); @@ -549,7 +564,6 @@ Handle ODBC::Query(const Arguments& args) { } cb = Local::Cast(args[2]); - prep_req->params = GetParametersFromArray(Local::Cast(args[1]), &prep_req->paramCount); } else { From 4ce7247e1928df88fed8d577af675b9d8c65b4f2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 15:20:52 -0500 Subject: [PATCH 098/511] reset buffer using \0 not \n --- src/odbc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index bb3b24a5..e6c13e9d 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -1140,7 +1140,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, struct tm timeInfo = { 0 }; //reset the buffer - buffer[0] = '\n'; + buffer[0] = '\0'; //TODO: SQLGetData can supposedly return multiple chunks, need to do this to //retrieve large fields From 98f5f12a4fb8e2d32e5195eed38a642e5d47a59b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 15:30:42 -0500 Subject: [PATCH 099/511] test: rename a test --- ...ct-with-numbers.js => test-param-select-with-numbers-mixed.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{test-param-select-with-numbers.js => test-param-select-with-numbers-mixed.js} (100%) diff --git a/test/test-param-select-with-numbers.js b/test/test-param-select-with-numbers-mixed.js similarity index 100% rename from test/test-param-select-with-numbers.js rename to test/test-param-select-with-numbers-mixed.js From 998ae441e99b4b14327f512049e06eb26b64ff5b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 17:16:08 -0500 Subject: [PATCH 100/511] Fixes for bind parameters bug. Should fix #30 --- src/odbc.cpp | 187 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 137 insertions(+), 50 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index e6c13e9d..4c492a84 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -796,6 +796,7 @@ void ODBC::UV_AfterQueryAll(uv_work_t* req) { } void ODBC::UV_QueryAll(uv_work_t* req) { + DEBUG_PRINTF("ODBC::UV_QueryAll\n"); query_request* prep_req = (query_request *)(req->data); Parameter prm; @@ -820,6 +821,10 @@ void ODBC::UV_QueryAll(uv_work_t* req) { for (int i = 0; i < prep_req->paramCount; i++) { prm = prep_req->params[i]; + DEBUG_PRINTF("ODBC::UV_QueryAll - param[%i]: c_type=%i type=%i " + "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, + prm.buffer_length, prm.size, prm.length, &prep_req->params[i].length); + ret = SQLBindParameter( prep_req->hSTMT, //StatementHandle i + 1, //ParameterNumber SQL_PARAM_INPUT, //InputOutputType @@ -829,7 +834,8 @@ void ODBC::UV_QueryAll(uv_work_t* req) { 0, //DecimalDigits prm.buffer, //ParameterValuePtr prm.buffer_length, //BufferLength - &prm.length); //StrLen_or_IndPtr + //using &prm.length did not work here... + &prep_req->params[i].length); //StrLen_or_IndPtr if (ret == SQL_ERROR) { break; @@ -847,7 +853,7 @@ void ODBC::UV_QueryAll(uv_work_t* req) { if (prm = prep_req->params[i], prm.buffer != NULL) { switch (prm.c_type) { case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_LONG: delete (int64_t *)prm.buffer; break; + case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; case SQL_C_DOUBLE: delete (double *)prm.buffer; break; case SQL_C_BIT: delete (bool *)prm.buffer; break; } @@ -1095,7 +1101,7 @@ Column* ODBC::GetColumns(SQLHSTMT hStmt, short* colCount) { columns[i].name = new unsigned char[MAX_FIELD_SIZE]; //set the first byte of name to \0 instead of memsetting the entire buffer - columns[i].name[0] = '\n'; + columns[i].name[0] = '\0'; //get the column name ret = SQLColAttribute( hStmt, @@ -1134,8 +1140,8 @@ void ODBC::FreeColumns(Column* columns, short* colCount) { Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength) { - //HandleScope scope; - SQLLEN len; + HandleScope scope; + SQLLEN len = 0; struct tm timeInfo = { 0 }; @@ -1146,7 +1152,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //retrieve large fields int ret; - switch (column.type) { + switch ((int) column.type) { case SQL_NUMERIC : case SQL_DECIMAL : case SQL_INTEGER : @@ -1156,30 +1162,37 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, case SQL_REAL : case SQL_DOUBLE : ret = SQLGetData( hStmt, - column.index, - SQL_C_CHAR, - (char *) buffer, - bufferLength, - (SQLLEN *) &len); + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + &len); + + DEBUG_PRINTF("ODBC::GetColumnValue - Numeric: index=%i name=%s type=%i len=%i ret=%i\n", + column.index, column.name, column.type, len, ret); if(ret == SQL_NULL_DATA || len < 0) { - //return scope.Close(Null()); - return Null(); + return scope.Close(Null()); + //return Null(); } else { - //return scope.Close(Number::New(atof(buffer))); - return Number::New(atof((char *) buffer)); + return scope.Close(Number::New(atof((char *) buffer))); + //return Number::New(atof((char *) buffer)); } case SQL_DATETIME : case SQL_TIMESTAMP : //I am not sure if this is locale-safe or cross database safe, but it //works for me on MSSQL +#ifdef _WIN32 ret = SQLGetData( hStmt, - column.index, - SQL_C_CHAR, - (char *) buffer, - bufferLength, - (SQLLEN *) &len); + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + &len); + + DEBUG_PRINTF("ODBC::GetColumnValue - W32 Timestamp: index=%i name=%s type=%i len=%i\n", + column.index, column.name, column.type, len); if(ret == SQL_NULL_DATA || len < 0) { //return scope.Close(Null()); @@ -1191,44 +1204,86 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //a negative value means that mktime() should use timezone information //and system databases to attempt to determine whether DST is in effect //at the specified time. - timeInfo.tm_isdst = -1; + timeInfo.tm_isdst = -1; //return scope.Close(Date::New(double(mktime(&timeInfo)) * 1000)); - return Date::New(double(mktime(&timeInfo)) * 1000); + return Date::New((double(mktime(&timeInfo)) * 1000)); + } +#else + SQL_TIMESTAMP_STRUCT odbcTime; + + ret = SQLGetData( hStmt, + column.index, + SQL_C_TYPE_TIMESTAMP, + &odbcTime, + bufferLength, + &len); + + DEBUG_PRINTF("ODBC::GetColumnValue - Unix Timestamp: index=%i name=%s type=%i len=%i\n", + column.index, column.name, column.type, len); + + if(ret == SQL_NULL_DATA || len < 0) { + return scope.Close(Null()); + //return Null(); + } + else { + timeInfo.tm_year = odbcTime.year - 1900; + timeInfo.tm_mon = odbcTime.month - 1; + timeInfo.tm_mday = odbcTime.day; + timeInfo.tm_hour = odbcTime.hour; + timeInfo.tm_min = odbcTime.minute; + timeInfo.tm_sec = odbcTime.second; + + //a negative value means that mktime() should use timezone information + //and system databases to attempt to determine whether DST is in effect + //at the specified time. + timeInfo.tm_isdst = -1; + + return scope.Close(Date::New((double(timegm(&timeInfo)) * 1000) + + (odbcTime.fraction / 1000000))); +// return Date::New((double(timegm(&timeInfo)) * 1000) +// + (odbcTime.fraction / 1000000)); } +#endif case SQL_BIT : //again, i'm not sure if this is cross database safe, but it works for //MSSQL ret = SQLGetData( hStmt, - column.index, - SQL_C_CHAR, - (char *) buffer, - bufferLength, - (SQLLEN *) &len); + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + &len); + + DEBUG_PRINTF("ODBC::GetColumnValue - Bit: index=%i name=%s type=%i len=%i\n", + column.index, column.name, column.type, len); if(ret == SQL_NULL_DATA || len < 0) { - //return scope.Close(Null()); - return Null(); + return scope.Close(Null()); + //return Null(); } else { - //return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); - return Boolean::New(( *buffer == '0') ? false : true ); + return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); + //return Boolean::New(( *buffer == '0') ? false : true ); } default : - ret = SQLGetData( hStmt, - column.index, - SQL_C_CHAR, - (char *) buffer, - bufferLength, - (SQLLEN *) &len); + ret = SQLGetData( hStmt, + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + &len); + + DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", + column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); if(ret == SQL_NULL_DATA || len < 0) { - //return scope.Close(Null()); - return Null(); + return scope.Close(Null()); + //return Null(); } else { - //return scope.Close(String::New(buffer)); - return String::New((char*) buffer); + return scope.Close(String::New((char*) buffer)); + //return String::New((char*) buffer); } } } @@ -1266,6 +1321,7 @@ Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, } Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { + DEBUG_PRINTF("ODBC::GetParametersFromArray\n"); *paramCount = values->Length(); Parameter * params = new Parameter[*paramCount]; @@ -1277,40 +1333,71 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].length = SQL_NULL_DATA; params[i].buffer_length = 0; + DEBUG_PRINTF("ODBC::GetParametersFromArray - ¶m[%i].length = %X\n", i, ¶ms[i].length); + if (value->IsString()) { String::Utf8Value string(value); - + params[i].c_type = SQL_C_CHAR; params[i].type = SQL_VARCHAR; - params[i].length = SQL_NTS; - params[i].buffer = malloc(string.length() + 1); params[i].buffer_length = string.length() + 1; - params[i].size = string.length() + 1; - + params[i].buffer = malloc(params[i].buffer_length); + params[i].size = params[i].buffer_length; + params[i].length = SQL_NTS;//params[i].buffer_length; + strcpy((char*)params[i].buffer, *string); + + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsString(): params[%i] " + "c_type=%i type=%i buffer_length=%i size=%i length=%i " + "value=%s\n", i, params[i].c_type, params[i].type, + params[i].buffer_length, params[i].size, params[i].length, + (char*) params[i].buffer); } else if (value->IsNull()) { params[i].c_type = SQL_C_DEFAULT; - params[i].type = SQL_NULL_DATA; + params[i].type = SQL_UNKNOWN_TYPE; params[i].length = SQL_NULL_DATA; + + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNull(): params[%i] " + "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + i, params[i].c_type, params[i].type, + params[i].buffer_length, params[i].size, params[i].length); } else if (value->IsInt32()) { int64_t *number = new int64_t(value->IntegerValue()); - params[i].c_type = SQL_C_LONG; - params[i].type = SQL_INTEGER; - params[i].buffer = number; + params[i].c_type = SQL_C_SBIGINT; + params[i].type = SQL_BIGINT; + params[i].buffer = number; + params[i].length = 0; + + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsInt32(): params[%i] " + "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + i, params[i].c_type, params[i].type, + params[i].buffer_length, params[i].size, params[i].length); } else if (value->IsNumber()) { double *number = new double(value->NumberValue()); params[i].c_type = SQL_C_DOUBLE; params[i].type = SQL_DECIMAL; params[i].buffer = number; + params[i].length = 0; + + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] " + "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + i, params[i].c_type, params[i].type, + params[i].buffer_length, params[i].size, params[i].length); } else if (value->IsBoolean()) { bool *boolean = new bool(value->BooleanValue()); params[i].c_type = SQL_C_BIT; params[i].type = SQL_BIT; params[i].buffer = boolean; + params[i].length = 0; + + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsBoolean(): params[%i] " + "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + i, params[i].c_type, params[i].type, + params[i].buffer_length, params[i].size, params[i].length); } } From 165914636f1068cb90d60acdc50a1199db30769a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 17:16:49 -0500 Subject: [PATCH 101/511] test: log some things in the date test --- test/test-date.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/test-date.js b/test/test-date.js index f82bd727..9d65cc10 100644 --- a/test/test-date.js +++ b/test/test-date.js @@ -9,18 +9,23 @@ db.open(common.connectionString, function(err) { assert.equal(db.connected, true); var dt = new Date(); + var sql = "SELECT cast('" + dt.toISOString().replace('Z','') + "' as datetime) as DT1"; - db.query("SELECT cast('" + dt.toISOString().replace('Z','') + "' as datetime) as DT1", function (err, data) { + console.log(sql); + + db.query(sql, function (err, data) { assert.equal(err, null); assert.equal(data.length, 1); db.close(function () { assert.equal(db.connected, false); + console.log(dt); + console.log(data); //test selected data after the connection //is closed, in case the assertion fails assert.equal(data[0].DT1.constructor.name, "Date", "DT1 is not an instance of a Date object"); - assert.equal(data[0].DT1.getTime(), dt.getTime(), "The original time passed to the database is not the same as the time we got back"); + assert.equal(data[0].DT1.getTime(), dt.getTime()); }); }); }); From f654e6acb24357676bc3bc34f84e6d33e09f2fc6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 17:17:18 -0500 Subject: [PATCH 102/511] test: attempt to use less memory in the memory leak --- test/test-memory-leaks-new-objects.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/test-memory-leaks-new-objects.js b/test/test-memory-leaks-new-objects.js index 79ade181..9ab5c551 100644 --- a/test/test-memory-leaks-new-objects.js +++ b/test/test-memory-leaks-new-objects.js @@ -1,13 +1,12 @@ var odbc = require("../") , openCount = 100 - , start = process.memoryUsage() - , stop = process.memoryUsage() + , start = process.memoryUsage().heapUsed , x = 100 ; gc(); -start = process.memoryUsage(); +start = process.memoryUsage().heapUsed; for (x = 0; x < openCount; x++ ) { (function () { @@ -17,6 +16,4 @@ for (x = 0; x < openCount; x++ ) { gc(); -stop = process.memoryUsage(); - -console.log(start.heapUsed, stop.heapUsed); \ No newline at end of file +console.log(process.memoryUsage().heapUsed - start); From e6fe9c7fcc8c8f17188b6d1a69161409631ede8c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 17:18:24 -0500 Subject: [PATCH 103/511] test: modify don't close require path and log err --- test/test-open-dont-close.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test-open-dont-close.js b/test/test-open-dont-close.js index ef503665..c1967b38 100644 --- a/test/test-open-dont-close.js +++ b/test/test-open-dont-close.js @@ -1,8 +1,12 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database(); db.open(common.connectionString, function(err) { console.error('db.open callback'); console.error('node should just sit and wait'); + console.log(err); + //reference db here so it isn't garbage collected: + + console.log(db.connected); }); From d11499bd507402d332f4b3f77edb2d6d33f97f03 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 17:18:49 -0500 Subject: [PATCH 104/511] straighten out formatting and leave empty array for `defines`. Enter 'DEBUG' in the defines array for debugging information --- binding.gyp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/binding.gyp b/binding.gyp index bc0196a0..d3e85f4b 100644 --- a/binding.gyp +++ b/binding.gyp @@ -4,7 +4,9 @@ 'target_name' : 'odbc_bindings', 'sources' : [ 'src/odbc.cpp', - 'src/odbc_result.cpp' + 'src/odbc_result.cpp' + ], + 'defines' : [ ], 'conditions' : [ [ 'OS == "linux"', { From 4786fb81ed54b193a35b145e2820fc4e961eb42d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 Nov 2012 17:32:05 -0500 Subject: [PATCH 105/511] copy UV_QueryAll's SQLBindParameter Call to UV_Query. --- src/odbc.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 4c492a84..2ff38d7a 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -496,16 +496,21 @@ void ODBC::UV_Query(uv_work_t* req) { for (int i = 0; i < prep_req->paramCount; i++) { prm = prep_req->params[i]; - ret = SQLBindParameter( prep_req->hSTMT, - i + 1, - SQL_PARAM_INPUT, - prm.c_type, - prm.type, - prm.size, - 0, - prm.buffer, - prm.buffer_length, - &prm.length); + DEBUG_PRINTF("ODBC::UV_Query - param[%i]: c_type=%i type=%i " + "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, + prm.buffer_length, prm.size, prm.length, &prep_req->params[i].length); + + ret = SQLBindParameter( prep_req->hSTMT, //StatementHandle + i + 1, //ParameterNumber + SQL_PARAM_INPUT, //InputOutputType + prm.c_type, //ValueType + prm.type, //ParameterType + prm.size, //ColumnSize + 0, //DecimalDigits + prm.buffer, //ParameterValuePtr + prm.buffer_length, //BufferLength + //using &prm.length did not work here... + &prep_req->params[i].length); //StrLen_or_IndPtr if (ret == SQL_ERROR) {break;} } From 08895c82ccd60704bad0432057a4b2fdcbfb0550 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 26 Nov 2012 21:43:11 -0500 Subject: [PATCH 106/511] Fixes for #30; Attempt number two: Decimals and VarChars. *Add `decimals` member to the Parameter struct and use it for SQLBindParameters *Don't use SQL_UNKNOWN_TYPE for Null parmeters, instead use SQL_VARCHAR This should fix #30, however the values I have chosen for `.decimals` and `.size` need to be addressed for the `IsNumber()` Parameter case. We may have to do something like count the digits to the left of the decimal point to get `.size` and count the digits to the right to get `.decimals`. --- src/odbc.cpp | 25 ++++++++++++++----------- src/odbc.h | 1 + test/common.js | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 2ff38d7a..5bb615d5 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -827,8 +827,8 @@ void ODBC::UV_QueryAll(uv_work_t* req) { prm = prep_req->params[i]; DEBUG_PRINTF("ODBC::UV_QueryAll - param[%i]: c_type=%i type=%i " - "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, - prm.buffer_length, prm.size, prm.length, &prep_req->params[i].length); + "buffer_length=%i size=%i length=%i &length=%X decimals=%i\n", i, prm.c_type, prm.type, + prm.buffer_length, prm.size, prm.length, &prep_req->params[i].length, prm.decimals); ret = SQLBindParameter( prep_req->hSTMT, //StatementHandle i + 1, //ParameterNumber @@ -836,7 +836,7 @@ void ODBC::UV_QueryAll(uv_work_t* req) { prm.c_type, //ValueType prm.type, //ParameterType prm.size, //ColumnSize - 0, //DecimalDigits + prm.decimals, //DecimalDigits prm.buffer, //ParameterValuePtr prm.buffer_length, //BufferLength //using &prm.length did not work here... @@ -1337,7 +1337,8 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].size = 0; params[i].length = SQL_NULL_DATA; params[i].buffer_length = 0; - + params[i].decimals = 0; + DEBUG_PRINTF("ODBC::GetParametersFromArray - ¶m[%i].length = %X\n", i, ¶ms[i].length); if (value->IsString()) { @@ -1360,7 +1361,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { } else if (value->IsNull()) { params[i].c_type = SQL_C_DEFAULT; - params[i].type = SQL_UNKNOWN_TYPE; + params[i].type = SQL_VARCHAR; params[i].length = SQL_NULL_DATA; DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNull(): params[%i] " @@ -1381,12 +1382,14 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].buffer_length, params[i].size, params[i].length); } else if (value->IsNumber()) { - double *number = new double(value->NumberValue()); - params[i].c_type = SQL_C_DOUBLE; - params[i].type = SQL_DECIMAL; - params[i].buffer = number; - params[i].length = 0; - + double *number = new double(value->NumberValue()); + params[i].c_type = SQL_C_DOUBLE; + params[i].type = SQL_DECIMAL; + params[i].buffer = number; + params[i].length = 0; + params[i].decimals = 6; //idk, i just chose this randomly. + params[i].size = 10; //also just a guess + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", i, params[i].c_type, params[i].type, diff --git a/src/odbc.h b/src/odbc.h index 0a9f087c..c3e178fa 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -48,6 +48,7 @@ typedef struct { void *buffer; SQLLEN buffer_length; SQLLEN length; + SQLSMALLINT decimals; } Parameter; class ODBC : public node::ObjectWrap { diff --git a/test/common.js b/test/common.js index 1a8c9c30..962ea529 100644 --- a/test/common.js +++ b/test/common.js @@ -23,4 +23,4 @@ exports.dropTables = function (db, cb) { exports.createTables = function (db, cb) { db.query("create table " + exports.tableName + " (COLINT INTEGER, COLDATETIME DATETIME, COLTEXT TEXT)", cb); -}; \ No newline at end of file +}; From 419b5610fdfd7e3e173c61e455c2832027994f6e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Apr 2013 12:58:51 -0400 Subject: [PATCH 107/511] Fix `uv_queue_work` calls by explicitly casting uv_after_work_cb Quoting from https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10: >The uv_after_work_cb signature has changed to take a second integer argument >indicating status. For backwards compatibility, explicitly cast the 4th argument to >uv_queue_work. This should fix #34 --- src/odbc.cpp | 12 ++++++------ src/odbc_result.cpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 5bb615d5..ff788fc4 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -303,7 +303,7 @@ Handle ODBC::Open(const Arguments& args) { work_req->data = open_req; - uv_queue_work(uv_default_loop(), work_req, UV_Open, UV_AfterOpen); + uv_queue_work(uv_default_loop(), work_req, UV_Open, (uv_after_work_cb)UV_AfterOpen); dbo->Ref(); @@ -380,7 +380,7 @@ Handle ODBC::Close(const Arguments& args) { work_req->data = close_req; - uv_queue_work(uv_default_loop(), work_req, UV_Close, UV_AfterClose); + uv_queue_work(uv_default_loop(), work_req, UV_Close, (uv_after_work_cb)UV_AfterClose); dbo->Ref(); @@ -596,7 +596,7 @@ Handle ODBC::Query(const Arguments& args) { prep_req->dbo = dbo; work_req->data = prep_req; - uv_queue_work(uv_default_loop(), work_req, UV_Query, UV_AfterQuery); + uv_queue_work(uv_default_loop(), work_req, UV_Query, (uv_after_work_cb)UV_AfterQuery); dbo->Ref(); @@ -929,7 +929,7 @@ Handle ODBC::QueryAll(const Arguments& args) { prep_req->dbo = dbo; work_req->data = prep_req; - uv_queue_work(uv_default_loop(), work_req, UV_QueryAll, UV_AfterQueryAll); + uv_queue_work(uv_default_loop(), work_req, UV_QueryAll, (uv_after_work_cb)UV_AfterQueryAll); dbo->Ref(); @@ -1004,7 +1004,7 @@ Handle ODBC::Tables(const Arguments& args) { prep_req->dbo = dbo; work_req->data = prep_req; - uv_queue_work(uv_default_loop(), work_req, UV_Tables, UV_AfterQueryAll); + uv_queue_work(uv_default_loop(), work_req, UV_Tables, (uv_after_work_cb)UV_AfterQueryAll); dbo->Ref(); @@ -1077,7 +1077,7 @@ Handle ODBC::Columns(const Arguments& args) { prep_req->dbo = dbo; work_req->data = prep_req; - uv_queue_work(uv_default_loop(), work_req, UV_Columns, UV_AfterQueryAll); + uv_queue_work(uv_default_loop(), work_req, UV_Columns, (uv_after_work_cb)UV_AfterQueryAll); dbo->Ref(); diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 57b3e7fd..a102218a 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -126,7 +126,7 @@ Handle ODBCResult::Fetch(const Arguments& args) { req_fetch->objResult = objODBCResult; work_req->data = req_fetch; - uv_queue_work(uv_default_loop(), work_req, UV_Fetch, UV_AfterFetch); + uv_queue_work(uv_default_loop(), work_req, UV_Fetch, (uv_after_work_cb)UV_AfterFetch); objODBCResult->Ref(); @@ -247,7 +247,7 @@ Handle ODBCResult::FetchAll(const Arguments& args) { fetch_Request->objResult = objODBCResult; work_req->data = fetch_Request; - uv_queue_work(uv_default_loop(), work_req, UV_FetchAll, UV_AfterFetchAll); + uv_queue_work(uv_default_loop(), work_req, UV_FetchAll, (uv_after_work_cb)UV_AfterFetchAll); objODBCResult->Ref(); From 8ea2196d52e5382e9b84a52541f65794cda3b1e0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Apr 2013 14:58:55 -0400 Subject: [PATCH 108/511] require node.js >= v0.10 in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d0276b25..63d4b334 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "lib": "." }, "engines": { - "node": ">=0.7.0" + "node": ">=0.10.0" }, "scripts": { "preinstall": "node-gyp configure build", From ed774db7f2de9f1f316293c7bf6f4e24be27d02f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Apr 2013 15:13:19 -0400 Subject: [PATCH 109/511] Update all after work callback function signatures to include 'int status' --- src/odbc.cpp | 8 ++++---- src/odbc.h | 8 ++++---- src/odbc_result.cpp | 4 ++-- src/odbc_result.h | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index ff788fc4..0b8b8f0f 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -162,7 +162,7 @@ Handle ODBC::ConnectedGetter(Local property, const AccessorInfo & return scope.Close(obj->connected ? True() : False()); } -void ODBC::UV_AfterOpen(uv_work_t* req) { +void ODBC::UV_AfterOpen(uv_work_t* req, int status) { DEBUG_PRINTF("ODBC::UV_AfterOpen\n"); HandleScope scope; open_request* open_req = (open_request *)(req->data); @@ -310,7 +310,7 @@ Handle ODBC::Open(const Arguments& args) { return scope.Close(args.Holder()); } -void ODBC::UV_AfterClose(uv_work_t* req) { +void ODBC::UV_AfterClose(uv_work_t* req, int status) { DEBUG_PRINTF("ODBC::UV_AfterClose\n"); HandleScope scope; @@ -387,7 +387,7 @@ Handle ODBC::Close(const Arguments& args) { return scope.Close(Undefined()); } -void ODBC::UV_AfterQuery(uv_work_t* req) { +void ODBC::UV_AfterQuery(uv_work_t* req, int status) { query_request* prep_req = (query_request *)(req->data); HandleScope scope; @@ -603,7 +603,7 @@ Handle ODBC::Query(const Arguments& args) { return scope.Close(Undefined()); } -void ODBC::UV_AfterQueryAll(uv_work_t* req) { +void ODBC::UV_AfterQueryAll(uv_work_t* req, int status) { query_request* prep_req = (query_request *)(req->data); HandleScope scope; diff --git a/src/odbc.h b/src/odbc.h index c3e178fa..f720a9e5 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -81,19 +81,19 @@ class ODBC : public node::ObjectWrap { static Handle ConnectedGetter(Local property, const AccessorInfo &info); - static void UV_AfterOpen(uv_work_t* req); + static void UV_AfterOpen(uv_work_t* req, int status); static void UV_Open(uv_work_t* req); static Handle Open(const Arguments& args); - static void UV_AfterClose(uv_work_t* req); + static void UV_AfterClose(uv_work_t* req, int status); static void UV_Close(uv_work_t* req); static Handle Close(const Arguments& args); - static void UV_AfterQuery(uv_work_t* req); + static void UV_AfterQuery(uv_work_t* req, int status); static void UV_Query(uv_work_t* req); static Handle Query(const Arguments& args); - static void UV_AfterQueryAll(uv_work_t* req); + static void UV_AfterQueryAll(uv_work_t* req, int status); static void UV_QueryAll(uv_work_t* req); static Handle QueryAll(const Arguments& args); diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index a102218a..afe490c3 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -141,7 +141,7 @@ void ODBCResult::UV_Fetch(uv_work_t* work_req) { req_fetch->result = SQLFetch(self->m_hSTMT); } -void ODBCResult::UV_AfterFetch(uv_work_t* work_req) { +void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { HandleScope scope; Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); @@ -262,7 +262,7 @@ void ODBCResult::UV_FetchAll(uv_work_t* work_req) { //req_fetch->result = SQLFetch(self->m_hSTMT); } -void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req) { +void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { HandleScope scope; Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); diff --git a/src/odbc_result.h b/src/odbc_result.h index 60dbab78..9e3e4cef 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -42,11 +42,11 @@ class ODBCResult : public node::ObjectWrap { //async methods static Handle Fetch(const Arguments& args); static void UV_Fetch(uv_work_t* work_req); - static void UV_AfterFetch(uv_work_t* work_req); + static void UV_AfterFetch(uv_work_t* work_req, int status); static Handle FetchAll(const Arguments& args); static void UV_FetchAll(uv_work_t* work_req); - static void UV_AfterFetchAll(uv_work_t* work_req); + static void UV_AfterFetchAll(uv_work_t* work_req, int status); //sync methods static Handle Close(const Arguments& args); From 8bee10df15e65c6421ebfcddab1fcdf56d9d3f37 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Apr 2013 15:18:40 -0400 Subject: [PATCH 110/511] test: use gc() to try to clear objects when testing for leaks --- test/test-memory-leaks-new-objects.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/test-memory-leaks-new-objects.js b/test/test-memory-leaks-new-objects.js index 9ab5c551..1be00a43 100644 --- a/test/test-memory-leaks-new-objects.js +++ b/test/test-memory-leaks-new-objects.js @@ -11,9 +11,23 @@ start = process.memoryUsage().heapUsed; for (x = 0; x < openCount; x++ ) { (function () { var db = new odbc.Database(); + db = null; })(); } gc(); console.log(process.memoryUsage().heapUsed - start); + +gc(); + +for (x = 0; x < openCount; x++ ) { + (function () { + var db = new odbc.Database(); + db = null; + })(); +} + +gc(); + +console.log(process.memoryUsage().heapUsed - start); \ No newline at end of file From 56213e58ed4cd032bb309b2c92ae2ec543422ac7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Apr 2013 15:19:06 -0400 Subject: [PATCH 111/511] test: output error message if there is one --- test/test-param-select-with-nulls-mixed.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-param-select-with-nulls-mixed.js b/test/test-param-select-with-nulls-mixed.js index c8ec2484..fdcc61fa 100644 --- a/test/test-param-select-with-nulls-mixed.js +++ b/test/test-param-select-with-nulls-mixed.js @@ -10,6 +10,7 @@ db.open(common.connectionString, function (err) { db.query("select ? as TEXTCOL1, ? as TEXTCOL2, ? as NULLCOL1 " , ["something", "something", null] , function (err, data, more) { + if (err) { console.error(err) } db.close(function () { assert.equal(err, null); assert.deepEqual(data, [{ From 75deabdbe5a5940a2254f61a602fdf248d274d2a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Apr 2013 15:19:37 -0400 Subject: [PATCH 112/511] test: add test for params with null --- test/test-param-select-with-null.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/test-param-select-with-null.js diff --git a/test/test-param-select-with-null.js b/test/test-param-select-with-null.js new file mode 100644 index 00000000..888334de --- /dev/null +++ b/test/test-param-select-with-null.js @@ -0,0 +1,21 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert"); + + +db.open(common.connectionString, function (err) { + assert.equal(err, null); + + db.query("select ? as NULLCOL1 " + , [null] + , function (err, data, more) { + if (err) { console.error(err) } + db.close(function () { + assert.equal(err, null); + assert.deepEqual(data, [{ + NULLCOL1: null + }]); + }); + }); +}); From 23e74df3dfd32670191d9dc99f7d9f0808c760df Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Apr 2013 15:20:17 -0400 Subject: [PATCH 113/511] 0.5.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 63d4b334..dda49f8b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.0-pre", + "version": "0.5.1", "main": "odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { @@ -17,7 +17,7 @@ }, "scripts": { "preinstall": "node-gyp configure build", - "test" : "cd test && node run-tests.js" + "test": "cd test && node run-tests.js" }, "dependencies": { "bindings": "~1.0.0" From c487270bbbfe6f3f793b5b7f7d45da30c02a96eb Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Apr 2013 15:48:54 -0400 Subject: [PATCH 114/511] Initial work for splitting ODBC/Connection/Statment/Result --- binding.gyp | 67 ++-- src/odbc.cpp | 299 ++++++++--------- src/odbc.h | 22 +- src/odbc_connection.cpp | 433 +++++++++++++++++++++++++ src/odbc_connection.h | 103 ++++++ src/odbc_result.cpp | 29 +- src/odbc_result.h | 3 +- src/odbc_statement.cpp | 698 ++++++++++++++++++++++++++++++++++++++++ src/odbc_statement.h | 109 +++++++ 9 files changed, 1549 insertions(+), 214 deletions(-) create mode 100644 src/odbc_connection.cpp create mode 100644 src/odbc_connection.h create mode 100644 src/odbc_statement.cpp create mode 100644 src/odbc_statement.h diff --git a/binding.gyp b/binding.gyp index d3e85f4b..d1cae68f 100644 --- a/binding.gyp +++ b/binding.gyp @@ -1,34 +1,37 @@ { - 'targets' : [ - { - 'target_name' : 'odbc_bindings', - 'sources' : [ - 'src/odbc.cpp', - 'src/odbc_result.cpp' - ], - 'defines' : [ - ], - 'conditions' : [ - [ 'OS == "linux"', { - 'libraries' : [ - '-lodbc' - ] - }], - [ 'OS == "mac"', { - 'libraries' : [ - '-lodbc' - ] - }], - [ 'OS=="win"', { - 'sources' : [ - 'src/strptime.c', - 'src/odbc.cpp' - ], - 'libraries' : [ - '-lodbccp32.lib' - ] - }] - ] - } - ] + 'targets' : [ + { + 'target_name' : 'odbc_bindings', + 'sources' : [ + 'src/odbc.cpp', + 'src/odbc_connection.cpp', + 'src/odbc_statement.cpp', + 'src/odbc_result.cpp' + ], + 'defines' : [ + + ], + 'conditions' : [ + [ 'OS == "linux"', { + 'libraries' : [ + '-lodbc' + ] + }], + [ 'OS == "mac"', { + 'libraries' : [ + '-lodbc' + ] + }], + [ 'OS=="win"', { + 'sources' : [ + 'src/strptime.c', + 'src/odbc.cpp' + ], + 'libraries' : [ + '-lodbccp32.lib' + ] + }] + ] + } + ] } diff --git a/src/odbc.cpp b/src/odbc.cpp index 0b8b8f0f..1f4a352e 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -1,4 +1,5 @@ /* + Copyright (c) 2013, Dan VerWeire Copyright (c) 2010, Lee Smith Permission to use, copy, modify, and/or distribute this software for any @@ -22,7 +23,9 @@ #include #include "odbc.h" +#include "odbc_connection.h" #include "odbc_result.h" +#include "odbc_statement.h" #ifdef _WIN32 #include "strptime.h" @@ -44,26 +47,27 @@ void ODBC::Init(v8::Handle target) { // Constructor Template constructor_template = Persistent::New(t); - constructor_template->SetClassName(String::NewSymbol("Database")); + constructor_template->SetClassName(String::NewSymbol("ODBC")); // Reserve space for one Handle Local instance_template = constructor_template->InstanceTemplate(); instance_template->SetInternalFieldCount(1); // Properties - instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); - instance_template->SetAccessor(String::New("connected"), ConnectedGetter); +// instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); +// instance_template->SetAccessor(String::New("connected"), ConnectedGetter); // Prototype Methods - NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchOpen", Open); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchClose", Close); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchQuery", Query); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchQueryAll", QueryAll); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchTables", Tables); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchColumns", Columns); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnection", CreateConnection); +// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchOpen", Open); +// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchClose", Close); +// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchQuery", Query); +// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchQueryAll", QueryAll); +// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchTables", Tables); +// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchColumns", Columns); // Attach the Database Constructor to the target object - target->Set( v8::String::NewSymbol("Database"), + target->Set( v8::String::NewSymbol("ODBC"), constructor_template->GetFunction()); scope.Close(Undefined()); @@ -95,15 +99,9 @@ ODBC::~ODBC() { void ODBC::Free() { DEBUG_PRINTF("ODBC::Free\n"); - if (m_hDBC || m_hEnv) { + if (m_hEnv) { uv_mutex_lock(&ODBC::g_odbcMutex); - if (m_hDBC) { - SQLDisconnect(m_hDBC); - SQLFreeHandle(SQL_HANDLE_DBC, m_hDBC); - m_hDBC = NULL; - } - if (m_hEnv) { SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv); m_hEnv = NULL; @@ -119,11 +117,12 @@ Handle ODBC::New(const Arguments& args) { ODBC* dbo = new ODBC(); dbo->Wrap(args.Holder()); - dbo->mode = 1; - dbo->connected = false; - dbo->m_hDBC = NULL; dbo->m_hEnv = NULL; + int ret = SQLAllocEnv( &dbo->m_hEnv ); + + //TODO: check if ret succeeded, if not, throw error to javascript land + return scope.Close(args.Holder()); } @@ -132,6 +131,79 @@ void ODBC::WatcherCallback(uv_async_t *w, int revents) { //i don't know if we need to do anything here } +/* + * CreateConnection + * + */ + +Handle ODBC::CreateConnection(const Arguments& args) { + DEBUG_PRINTF("ODBC::CreateConnection\n"); + HandleScope scope; + + REQ_FUN_ARG(0, cb); + + ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); + + //initialize work request + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + //initialize our data + create_connection_work_data* data = + (create_connection_work_data *) (calloc(1, sizeof(create_connection_work_data))); + + data->cb = Persistent::New(cb); + data->dbo = dbo; + + work_req->data = data; + + uv_queue_work(uv_default_loop(), work_req, UV_CreateConnection, (uv_after_work_cb)UV_AfterCreateConnection); + + dbo->Ref(); + + return scope.Close(Undefined()); +} + +void ODBC::UV_CreateConnection(uv_work_t* req) { + DEBUG_PRINTF("ODBC::UV_CreateConnection\n"); + + //get our work data + create_connection_work_data* data = (create_connection_work_data *)(req->data); + + uv_mutex_lock(&ODBC::g_odbcMutex); + + //allocate a new connection handle + int ret = SQLAllocConnect(data->dbo->m_hEnv, &data->hDBC); + + uv_mutex_unlock(&ODBC::g_odbcMutex); +} + +void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBC::UV_AfterCreateConnection\n"); + HandleScope scope; + + create_connection_work_data* data = (create_connection_work_data *)(req->data); + + Local args[2]; + args[0] = External::New(data->dbo->m_hEnv); + args[1] = External::New(data->hDBC); + + Persistent js_result(ODBCConnection::constructor_template-> + GetFunction()->NewInstance(2, args)); + + args[0] = Local::New(Null()); + args[1] = Local::New(js_result); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + + data->dbo->Unref(); + data->cb.Dispose(); + + free(data); + free(req); + + scope.Close(Undefined()); +} +/* Handle ODBC::ModeGetter(Local property, const AccessorInfo &info) { HandleScope scope; @@ -935,154 +1007,7 @@ Handle ODBC::QueryAll(const Arguments& args) { return scope.Close(Undefined()); } - -void ODBC::UV_Tables(uv_work_t* req) { - query_request* prep_req = (query_request *)(req->data); - - uv_mutex_lock(&ODBC::g_odbcMutex); - SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->hSTMT ); - uv_mutex_unlock(&ODBC::g_odbcMutex); - - SQLRETURN ret = SQLTables( - prep_req->hSTMT, - (SQLCHAR *) prep_req->catalog, SQL_NTS, - (SQLCHAR *) prep_req->schema, SQL_NTS, - (SQLCHAR *) prep_req->table, SQL_NTS, - (SQLCHAR *) prep_req->type, SQL_NTS - ); - - // this will be checked later in UV_AfterQuery - prep_req->result = ret; -} - -Handle ODBC::Tables(const Arguments& args) { - HandleScope scope; - - REQ_STR_OR_NULL_ARG(0, catalog); - REQ_STR_OR_NULL_ARG(1, schema); - REQ_STR_OR_NULL_ARG(2, table); - REQ_STR_OR_NULL_ARG(3, type); - Local cb = Local::Cast(args[4]); - - ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); - uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); - - if (!prep_req) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); - } - - prep_req->sql = NULL; - prep_req->catalog = NULL; - prep_req->schema = NULL; - prep_req->table = NULL; - prep_req->type = NULL; - prep_req->column = NULL; - prep_req->cb = Persistent::New(cb); - - if (!String::New(*catalog)->Equals(String::New("null"))) { - prep_req->catalog = (char *) malloc(catalog.length() +1); - strcpy(prep_req->catalog, *catalog); - } - - if (!String::New(*schema)->Equals(String::New("null"))) { - prep_req->schema = (char *) malloc(schema.length() +1); - strcpy(prep_req->schema, *schema); - } - - if (!String::New(*table)->Equals(String::New("null"))) { - prep_req->table = (char *) malloc(table.length() +1); - strcpy(prep_req->table, *table); - } - - if (!String::New(*type)->Equals(String::New("null"))) { - prep_req->type = (char *) malloc(type.length() +1); - strcpy(prep_req->type, *type); - } - - prep_req->dbo = dbo; - work_req->data = prep_req; - - uv_queue_work(uv_default_loop(), work_req, UV_Tables, (uv_after_work_cb)UV_AfterQueryAll); - - dbo->Ref(); - - return scope.Close(Undefined()); -} - -void ODBC::UV_Columns(uv_work_t* req) { - query_request* prep_req = (query_request *)(req->data); - - SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->hSTMT ); - - SQLRETURN ret = SQLColumns( - prep_req->hSTMT, - (SQLCHAR *) prep_req->catalog, SQL_NTS, - (SQLCHAR *) prep_req->schema, SQL_NTS, - (SQLCHAR *) prep_req->table, SQL_NTS, - (SQLCHAR *) prep_req->column, SQL_NTS - ); - - // this will be checked later in UV_AfterQuery - prep_req->result = ret; -} - -Handle ODBC::Columns(const Arguments& args) { - HandleScope scope; - - REQ_STR_OR_NULL_ARG(0, catalog); - REQ_STR_OR_NULL_ARG(1, schema); - REQ_STR_OR_NULL_ARG(2, table); - REQ_STR_OR_NULL_ARG(3, column); - Local cb = Local::Cast(args[4]); - - ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); - uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); - - if (!prep_req) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); - } - - prep_req->sql = NULL; - prep_req->catalog = NULL; - prep_req->schema = NULL; - prep_req->table = NULL; - prep_req->type = NULL; - prep_req->column = NULL; - prep_req->cb = Persistent::New(cb); - - if (!String::New(*catalog)->Equals(String::New("null"))) { - prep_req->catalog = (char *) malloc(catalog.length() +1); - strcpy(prep_req->catalog, *catalog); - } - - if (!String::New(*schema)->Equals(String::New("null"))) { - prep_req->schema = (char *) malloc(schema.length() +1); - strcpy(prep_req->schema, *schema); - } - - if (!String::New(*table)->Equals(String::New("null"))) { - prep_req->table = (char *) malloc(table.length() +1); - strcpy(prep_req->table, *table); - } - - if (!String::New(*column)->Equals(String::New("null"))) { - prep_req->column = (char *) malloc(column.length() +1); - strcpy(prep_req->column, *column); - } - - prep_req->dbo = dbo; - work_req->data = prep_req; - - uv_queue_work(uv_default_loop(), work_req, UV_Columns, (uv_after_work_cb)UV_AfterQueryAll); - - dbo->Ref(); - - return scope.Close(Undefined()); -} +*/ Column* ODBC::GetColumns(SQLHSTMT hStmt, short* colCount) { SQLRETURN ret; @@ -1377,9 +1302,10 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].length = 0; DEBUG_PRINTF("ODBC::GetParametersFromArray - IsInt32(): params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", - i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length); + "c_type=%i type=%i buffer_length=%i size=%i length=%i " + "value=%i\n", i, params[i].c_type, params[i].type, + params[i].buffer_length, params[i].size, params[i].length, + params[i].buffer); } else if (value->IsNumber()) { double *number = new double(value->NumberValue()); @@ -1412,9 +1338,40 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { return params; } +Handle ODBC::CallbackSQLError (HENV hENV, + HDBC hDBC, + HSTMT hSTMT, + Persistent cb) { + + Local objError = Object::New(); + + char errorMessage[512]; + char errorSQLState[128]; + + SQLError( hENV, + hDBC, + hSTMT, + (SQLCHAR *) errorSQLState, + NULL, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + NULL); + + objError->Set(String::New("state"), String::New(errorSQLState)); + objError->Set(String::New("error"), String::New("[node-odbc] Error in " + "__Make This Dynamic__")); + objError->Set(String::New("message"), String::New(errorMessage)); + + Local args[1]; + args[0] = objError; + cb->Call(Context::GetCurrent()->Global(), 1, args); +} + extern "C" void init (v8::Handle target) { ODBC::Init(target); ODBCResult::Init(target); + ODBCConnection::Init(target); + ODBCStatement::Init(target); } NODE_MODULE(odbc_bindings, init) diff --git a/src/odbc.h b/src/odbc.h index f720a9e5..8bc7d3f4 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -1,4 +1,5 @@ /* + Copyright (c) 2013, Dan VerWeire Copyright (c) 2010, Lee Smith Permission to use, copy, modify, and/or distribute this software for any @@ -63,6 +64,7 @@ class ODBC : public node::ObjectWrap { static Handle GetColumnValue(SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength); static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); + static Handle CallbackSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, Persistent cb); static Parameter* GetParametersFromArray (Local values, int* paramCount); @@ -75,8 +77,13 @@ class ODBC : public node::ObjectWrap { static Handle New(const Arguments& args); + //async methods + static Handle CreateConnection(const Arguments& args); + static void UV_CreateConnection(uv_work_t* work_req); + static void UV_AfterCreateConnection(uv_work_t* work_req, int status); + //Property Getter/Setters - static Handle ModeGetter(Local property, const AccessorInfo &info); +/* static Handle ModeGetter(Local property, const AccessorInfo &info); static void ModeSetter(Local property, Local value, const AccessorInfo &info); static Handle ConnectedGetter(Local property, const AccessorInfo &info); @@ -102,17 +109,20 @@ class ODBC : public node::ObjectWrap { static void UV_Columns(uv_work_t* req); static Handle Columns(const Arguments& args); - +*/ static void WatcherCallback(uv_async_t* w, int revents); ODBC *self(void) { return this; } protected: HENV m_hEnv; - HDBC m_hDBC; - SQLUSMALLINT canHaveMoreResults; - int mode; - bool connected; +}; + +struct create_connection_work_data { + Persistent cb; + ODBC *dbo; + HDBC hDBC; + int result; }; struct open_request { diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp new file mode 100644 index 00000000..1ce99499 --- /dev/null +++ b/src/odbc_connection.cpp @@ -0,0 +1,433 @@ +/* + Copyright (c) 2013, Dan VerWeire + Copyright (c) 2010, Lee Smith + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "odbc.h" +#include "odbc_connection.h" +#include "odbc_result.h" +#include "odbc_statement.h" + +using namespace v8; +using namespace node; + +Persistent ODBCConnection::constructor_template; + +void ODBCConnection::Init(v8::Handle target) { + DEBUG_PRINTF("ODBCConnection::Init\n"); + HandleScope scope; + + Local t = FunctionTemplate::New(New); + + // Constructor Template + constructor_template = Persistent::New(t); + constructor_template->SetClassName(String::NewSymbol("ODBCConnection")); + + // Reserve space for one Handle + Local instance_template = constructor_template->InstanceTemplate(); + instance_template->SetInternalFieldCount(1); + + // Properties + //instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); + //instance_template->SetAccessor(String::New("connected"), ConnectedGetter); + + // Prototype Methods + NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatement", CreateStatement); + + // Attach the Database Constructor to the target object + target->Set( v8::String::NewSymbol("ODBCConnection"), + constructor_template->GetFunction()); + + scope.Close(Undefined()); +} + +ODBCConnection::~ODBCConnection() { + DEBUG_PRINTF("ODBCConnection::~ODBCConnection\n"); + this->Free(); +} + +void ODBCConnection::Free() { + DEBUG_PRINTF("ODBCConnection::Free\n"); + if (m_hDBC) { + uv_mutex_lock(&ODBC::g_odbcMutex); + + if (m_hDBC) { + SQLDisconnect(m_hDBC); + SQLFreeHandle(SQL_HANDLE_DBC, m_hDBC); + m_hDBC = NULL; + } + + uv_mutex_unlock(&ODBC::g_odbcMutex); + } +} + +Handle ODBCConnection::New(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::New\n"); + HandleScope scope; + + REQ_EXT_ARG(0, js_henv); + REQ_EXT_ARG(1, js_hdbc); + + HENV hENV = static_cast(js_henv->Value()); + HDBC hDBC = static_cast(js_hdbc->Value()); + + ODBCConnection* conn = new ODBCConnection(hENV, hDBC); + + conn->Wrap(args.Holder()); + + return scope.Close(args.Holder()); +} + +Handle ODBCConnection::ConnectedGetter(Local property, const AccessorInfo &info) { + HandleScope scope; + + ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + + return scope.Close(obj->connected ? True() : False()); +} + +/* + * Open + * + */ + +Handle ODBCConnection::Open(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::Open\n"); + HandleScope scope; + + REQ_STR_ARG(0, connection); + REQ_FUN_ARG(1, cb); + + //get reference to the connection object + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + //create a uv work request + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + //allocate our worker data + open_connection_work_data* data = (open_connection_work_data *) + calloc(1, sizeof(open_connection_work_data) + connection.length()); + + //copy the connection string to the work data + strcpy(data->connection, *connection); + data->cb = Persistent::New(cb); + data->conn = conn; + + work_req->data = data; + + //queue the work + uv_queue_work(uv_default_loop(), + work_req, + UV_Open, + (uv_after_work_cb)UV_AfterOpen); + + conn->Ref(); + + return scope.Close(args.Holder()); +} + +void ODBCConnection::UV_Open(uv_work_t* req) { + DEBUG_PRINTF("ODBCConnection::UV_Open\n"); + open_connection_work_data* data = (open_connection_work_data *)(req->data); + + ODBCConnection* self = data->conn->self(); + + uv_mutex_lock(&ODBC::g_odbcMutex); + + //TODO: make this configurable + SQLSetConnectOption( self->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); + + char connstr[1024]; + + //Attempt to connect + int ret = SQLDriverConnect( + self->m_hDBC, + NULL, + (SQLCHAR*) data->connection, + strlen(data->connection), + (SQLCHAR*) connstr, + 1024, + NULL, + SQL_DRIVER_NOPROMPT); + + if (SQL_SUCCEEDED(ret)) { + HSTMT hStmt; + + //allocate a temporary statment + ret = SQLAllocStmt(self->m_hDBC, &hStmt); + + //try to determine if the driver can handle + //multiple recordsets + ret = SQLGetFunctions( + self->m_hDBC, + SQL_API_SQLMORERESULTS, + &self->canHaveMoreResults); + + if (!SQL_SUCCEEDED(ret)) { + self->canHaveMoreResults = 0; + } + + //free the handle + ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt); + } + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + data->result = ret; +} + +void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCConnection::UV_AfterOpen\n"); + HandleScope scope; + open_connection_work_data* data = (open_connection_work_data *)(req->data); + + Local argv[1]; + + bool err = false; + + if (data->result) { + err = true; + + SQLINTEGER i = 0; + SQLINTEGER native; + SQLSMALLINT len; + SQLRETURN ret; + char errorSQLState[7]; + char errorMessage[256]; + + do { + ret = SQLGetDiagRec( + SQL_HANDLE_DBC, + data->conn->self()->m_hDBC, + ++i, + (SQLCHAR *) errorSQLState, + &native, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + &len); + + if (SQL_SUCCEEDED(ret)) { + Local objError = Object::New(); + + objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); + objError->Set(String::New("message"), String::New(errorMessage)); + objError->Set(String::New("state"), String::New(errorSQLState)); + + argv[0] = objError; + } + } while( ret == SQL_SUCCESS ); + } + + if (!err) { + data->conn->self()->connected = true; + + //only uv_ref if the connection was successful +#if NODE_VERSION_AT_LEAST(0, 7, 9) + uv_ref((uv_handle_t *)&ODBC::g_async); +#else + uv_ref(uv_default_loop()); +#endif + } + + TryCatch try_catch; + + data->conn->Unref(); + data->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + + free(data); + free(req); + scope.Close(Undefined()); +} + +/* + * Close + * + */ + +Handle ODBCConnection::Close(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::Close\n"); + HandleScope scope; + + REQ_FUN_ARG(0, cb); + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + close_connection_work_data* data = (close_connection_work_data *) + (calloc(1, sizeof(close_connection_work_data))); + + data->cb = Persistent::New(cb); + data->conn = conn; + + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_Close, + (uv_after_work_cb)UV_AfterClose); + + conn->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCConnection::UV_Close(uv_work_t* req) { + DEBUG_PRINTF("ODBCConnection::UV_Close\n"); + close_connection_work_data* data = (close_connection_work_data *)(req->data); + ODBCConnection* conn = data->conn; + + //TODO: check to see if there are any open statements + //on this connection + + conn->Free(); + + data->result = 0; +} + +void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCConnection::UV_AfterClose\n"); + HandleScope scope; + + close_connection_work_data* data = (close_connection_work_data *)(req->data); + + ODBCConnection* conn = data->conn; + + Local argv[1]; + bool err = false; + + if (data->result) { + err = true; + argv[0] = Exception::Error(String::New("Error closing database")); + } + else { + conn->connected = false; + + //only unref if the connection was closed +#if NODE_VERSION_AT_LEAST(0, 7, 9) + uv_unref((uv_handle_t *)&ODBC::g_async); +#else + uv_unref(uv_default_loop()); +#endif + } + + TryCatch try_catch; + + data->conn->Unref(); + data->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + + free(data); + free(req); + scope.Close(Undefined()); +} + + +/* + * CreateStatement + * + */ + +Handle ODBCConnection::CreateStatement(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::CreateStatement\n"); + HandleScope scope; + + REQ_FUN_ARG(0, cb); + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + //initialize work request + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + //initialize our data + create_statement_work_data* data = + (create_statement_work_data *) (calloc(1, sizeof(create_statement_work_data))); + + data->cb = Persistent::New(cb); + data->conn = conn; + + work_req->data = data; + + uv_queue_work(uv_default_loop(), work_req, UV_CreateStatement, (uv_after_work_cb)UV_AfterCreateStatement); + + conn->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCConnection::UV_CreateStatement(uv_work_t* req) { + DEBUG_PRINTF("ODBCConnection::UV_CreateStatement\n"); + + //get our work data + create_statement_work_data* data = (create_statement_work_data *)(req->data); + + uv_mutex_lock(&ODBC::g_odbcMutex); + + //allocate a new statment handle + SQLAllocHandle( SQL_HANDLE_STMT, + data->conn->m_hDBC, + &data->hSTMT ); + + uv_mutex_unlock(&ODBC::g_odbcMutex); +} + +void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCConnection::UV_AfterCreateStatement\n"); + HandleScope scope; + + create_statement_work_data* data = (create_statement_work_data *)(req->data); + + Local args[3]; + args[0] = External::New(data->conn->m_hENV); + args[1] = External::New(data->conn->m_hDBC); + args[2] = External::New(data->hSTMT); + + Persistent js_result(ODBCStatement::constructor_template-> + GetFunction()->NewInstance(3, args)); + + args[0] = Local::New(Null()); + args[1] = Local::New(js_result); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + + data->conn->Unref(); + data->cb.Dispose(); + + free(data); + free(req); + + scope.Close(Undefined()); +} diff --git a/src/odbc_connection.h b/src/odbc_connection.h new file mode 100644 index 00000000..94ee773e --- /dev/null +++ b/src/odbc_connection.h @@ -0,0 +1,103 @@ +/* + Copyright (c) 2013, Dan VerWeire + Copyright (c) 2010, Lee Smith + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _SRC_ODBC_CONNECTION_H +#define _SRC_ODBC_CONNECTION_H + +class ODBCConnection : public node::ObjectWrap { + public: + static Persistent constructor_template; + static void Init(v8::Handle target); + + void Free(); + + protected: + ODBCConnection() {}; + + explicit ODBCConnection(HENV hENV, HDBC hDBC): + ObjectWrap(), + m_hENV(hENV), + m_hDBC(hDBC) {}; + + ~ODBCConnection(); + + //constructor + static Handle New(const Arguments& args); + + //Property Getter/Setters + static Handle ConnectedGetter(Local property, const AccessorInfo &info); + + //async methods +// static Handle BeginTransaction(const Arguments& args); +// static void UV_BeginTransaction(uv_work_t* work_req); +// static void UV_AfterBeginTransaction(uv_work_t* work_req, int status); +// +// static Handle EndTransaction(const Arguments& args); +// static void UV_EndTransaction(uv_work_t* work_req); +// static void UV_AfterEndTransaction(uv_work_t* work_req, int status); + + static Handle Open(const Arguments& args); + static void UV_Open(uv_work_t* work_req); + static void UV_AfterOpen(uv_work_t* work_req, int status); + + static Handle Close(const Arguments& args); + static void UV_Close(uv_work_t* work_req); + static void UV_AfterClose(uv_work_t* work_req, int status); + + static Handle CreateStatement(const Arguments& args); + static void UV_CreateStatement(uv_work_t* work_req); + static void UV_AfterCreateStatement(uv_work_t* work_req, int status); + + struct Fetch_Request { + Persistent callback; + ODBCConnection *objResult; + SQLRETURN result; + }; + + ODBCConnection *self(void) { return this; } + + protected: + HENV m_hENV; + HDBC m_hDBC; + SQLUSMALLINT canHaveMoreResults; + bool connected; + int statements; +}; + +struct create_statement_work_data { + Persistent cb; + ODBCConnection *conn; + HSTMT hSTMT; + int result; +}; + +struct open_connection_work_data { + Persistent cb; + ODBCConnection *conn; + int result; + char connection[1]; +}; + +struct close_connection_work_data { + Persistent cb; + ODBCConnection *conn; + int result; +}; + + + +#endif diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index afe490c3..1fffb7ce 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -1,6 +1,5 @@ /* - Copyright (c) 2012, Dan VerWeire - Copyright (c) 2010, Lee Smith + Copyright (c) 2013, Dan VerWeire Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -23,7 +22,9 @@ #include #include "odbc.h" +#include "odbc_connection.h" #include "odbc_result.h" +#include "odbc_statement.h" using namespace v8; using namespace node; @@ -61,10 +62,15 @@ ODBCResult::~ODBCResult() { } void ODBCResult::Free() { + DEBUG_PRINTF("ODBCResult::Free\n"); + if (m_hSTMT) { uv_mutex_lock(&ODBC::g_odbcMutex); - SQLFreeHandle(SQL_HANDLE_STMT, m_hSTMT); + //This doesn't actually deallocate the statement handle + //that should not be done by the result object; that should + //be done by the statement object + SQLFreeStmt(m_hSTMT, SQL_CLOSE); m_hSTMT = NULL; uv_mutex_unlock(&ODBC::g_odbcMutex); @@ -76,6 +82,8 @@ void ODBCResult::Free() { } Handle ODBCResult::New(const Arguments& args) { + DEBUG_PRINTF("ODBCResult::New\n"); + HandleScope scope; REQ_EXT_ARG(0, js_henv); @@ -105,6 +113,8 @@ Handle ODBCResult::New(const Arguments& args) { } Handle ODBCResult::Fetch(const Arguments& args) { + DEBUG_PRINTF("ODBCResult::Fetch\n"); + HandleScope scope; ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); @@ -134,6 +144,8 @@ Handle ODBCResult::Fetch(const Arguments& args) { } void ODBCResult::UV_Fetch(uv_work_t* work_req) { + DEBUG_PRINTF("ODBCResult::UV_Fetch\n"); + Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); ODBCResult* self = req_fetch->objResult->self(); @@ -142,6 +154,8 @@ void ODBCResult::UV_Fetch(uv_work_t* work_req) { } void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { + DEBUG_PRINTF("ODBCResult::UV_AfterFetch\n"); + HandleScope scope; Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); @@ -226,6 +240,8 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { } Handle ODBCResult::FetchAll(const Arguments& args) { + DEBUG_PRINTF("ODBCResult::FetchAll\n"); + HandleScope scope; ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); @@ -255,6 +271,7 @@ Handle ODBCResult::FetchAll(const Arguments& args) { } void ODBCResult::UV_FetchAll(uv_work_t* work_req) { + DEBUG_PRINTF("ODBCResult::UV_FetchAll\n"); //Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); //ODBCResult* self = req_fetch->objResult->self(); @@ -263,6 +280,8 @@ void ODBCResult::UV_FetchAll(uv_work_t* work_req) { } void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { + DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll\n"); + HandleScope scope; Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); @@ -341,6 +360,8 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { } Handle ODBCResult::Close(const Arguments& args) { + DEBUG_PRINTF("ODBCResult::Close\n"); + HandleScope scope; ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); @@ -351,6 +372,8 @@ Handle ODBCResult::Close(const Arguments& args) { } Handle ODBCResult::MoreResults(const Arguments& args) { + DEBUG_PRINTF("ODBCResult::MoreResults\n"); + HandleScope scope; ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); diff --git a/src/odbc_result.h b/src/odbc_result.h index 9e3e4cef..ae378871 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -1,6 +1,5 @@ /* - Copyright (c) 2012, Dan VerWeire - Copyright (c) 2010, Lee Smith + Copyright (c) 2013, Dan VerWeire Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp new file mode 100644 index 00000000..0eebbc26 --- /dev/null +++ b/src/odbc_statement.cpp @@ -0,0 +1,698 @@ +/* + Copyright (c) 2013, Dan VerWeire + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "odbc.h" +#include "odbc_connection.h" +#include "odbc_result.h" +#include "odbc_statement.h" + +using namespace v8; +using namespace node; + +Persistent ODBCStatement::constructor_template; + +void ODBCStatement::Init(v8::Handle target) { + HandleScope scope; + + Local t = FunctionTemplate::New(New); + + // Constructor Template + constructor_template = Persistent::New(t); + constructor_template->SetClassName(String::NewSymbol("ODBCStatement")); + + // Reserve space for one Handle + Local instance_template = constructor_template->InstanceTemplate(); + instance_template->SetInternalFieldCount(1); + + // Prototype Methods + NODE_SET_PROTOTYPE_METHOD(constructor_template, "execute", Execute); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirect", ExecuteDirect); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "bind", Bind); + + // Attach the Database Constructor to the target object + target->Set( v8::String::NewSymbol("ODBCStatement"), + constructor_template->GetFunction()); + + scope.Close(Undefined()); +} + +ODBCStatement::~ODBCStatement() { + this->Free(); +} + +void ODBCStatement::Free() { + if (m_hSTMT) { + uv_mutex_lock(&ODBC::g_odbcMutex); + + SQLFreeHandle(SQL_HANDLE_STMT, m_hSTMT); + m_hSTMT = NULL; + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + if (bufferLength > 0) { + free(buffer); + } + } +} + +Handle ODBCStatement::New(const Arguments& args) { + HandleScope scope; + + REQ_EXT_ARG(0, js_henv); + REQ_EXT_ARG(1, js_hdbc); + REQ_EXT_ARG(2, js_hstmt); + + HENV hENV = static_cast(js_henv->Value()); + HDBC hDBC = static_cast(js_hdbc->Value()); + HSTMT hSTMT = static_cast(js_hstmt->Value()); + + //create a new OBCResult object + ODBCStatement* objODBCResult = new ODBCStatement(hENV, hDBC, hSTMT); + + //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; + + objODBCResult->Wrap(args.Holder()); + + return scope.Close(args.Holder()); +} + +/* + * Execute + * + */ + +Handle ODBCStatement::Execute(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::Execute\n"); + + HandleScope scope; + + REQ_FUN_ARG(0, cb); + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + execute_work_data* data = + (execute_work_data *) calloc(1, sizeof(execute_work_data)); + + data->cb = Persistent::New(cb); + + data->stmt = stmt; + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_Execute, + (uv_after_work_cb)UV_AfterExecute); + + stmt->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCStatement::UV_Execute(uv_work_t* req) { + DEBUG_PRINTF("ODBCStatement::UV_Execute\n"); + + execute_work_data* data = (execute_work_data *)(req->data); + + SQLRETURN ret; + + ret = SQLExecute(data->stmt->m_hSTMT); + + data->result = ret; +} + +void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCStatement::UV_AfterExecute\n"); + + execute_work_data* data = (execute_work_data *)(req->data); + + HandleScope scope; + + //an easy reference to the statment object + ODBCStatement* self = data->stmt->self(); + + //First thing, let's check if the execution of the query returned any errors + if(data->result == SQL_ERROR) { + ODBC::CallbackSQLError( + self->m_hENV, + self->m_hDBC, + self->m_hSTMT, + data->cb); + } + else { + Local args[3]; + args[0] = External::New(self->m_hENV); + args[1] = External::New(self->m_hDBC); + args[2] = External::New(self->m_hSTMT); + + Persistent js_result(ODBCResult::constructor_template-> + GetFunction()->NewInstance(3, args)); + + args[0] = Local::New(Null()); + args[1] = Local::New(js_result); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + } + + TryCatch try_catch; + + self->Unref(); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + + free(data); + free(req); + + scope.Close(Undefined()); +} + +/* + * ExecuteDirect + * + */ + +Handle ODBCStatement::ExecuteDirect(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::ExecuteDirect\n"); + + HandleScope scope; + + REQ_STR_ARG(0, sql); + REQ_FUN_ARG(1, cb); + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + execute_direct_work_data* data = + (execute_direct_work_data *) calloc(1, sizeof(execute_direct_work_data)); + + data->sql = (char *) malloc(sql.length() +1); + data->cb = Persistent::New(cb); + + strcpy(data->sql, *sql); + + data->stmt = stmt; + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_ExecuteDirect, + (uv_after_work_cb)UV_AfterExecuteDirect); + + stmt->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCStatement::UV_ExecuteDirect(uv_work_t* req) { + DEBUG_PRINTF("ODBCStatement::UV_ExecuteDirect\n"); + + execute_direct_work_data* data = (execute_direct_work_data *)(req->data); + + SQLRETURN ret; + + ret = SQLExecDirect( + data->stmt->m_hSTMT, + (SQLCHAR *) data->sql, + strlen(data->sql)); + + data->result = ret; +} + +void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCStatement::UV_AfterExecuteDirect\n"); + + execute_direct_work_data* data = (execute_direct_work_data *)(req->data); + + HandleScope scope; + + //an easy reference to the statment object + ODBCStatement* self = data->stmt->self(); + + //First thing, let's check if the execution of the query returned any errors + if(data->result == SQL_ERROR) { + ODBC::CallbackSQLError( + self->m_hENV, + self->m_hDBC, + self->m_hSTMT, + data->cb); + } + else { + Local args[3]; + args[0] = External::New(self->m_hENV); + args[1] = External::New(self->m_hDBC); + args[2] = External::New(self->m_hSTMT); + + Persistent js_result(ODBCResult::constructor_template-> + GetFunction()->NewInstance(3, args)); + + args[0] = Local::New(Null()); + args[1] = Local::New(js_result); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + } + + TryCatch try_catch; + + self->Unref(); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + + free(data->sql); + free(data); + free(req); + + scope.Close(Undefined()); +} + +/* + * Prepare + * + */ + +Handle ODBCStatement::Prepare(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::Prepare\n"); + + HandleScope scope; + + REQ_STR_ARG(0, sql); + REQ_FUN_ARG(1, cb); + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + prepare_work_data* data = + (prepare_work_data *) calloc(1, sizeof(prepare_work_data)); + + data->sql = (char *) malloc(sql.length() +1); + data->cb = Persistent::New(cb); + + strcpy(data->sql, *sql); + + data->stmt = stmt; + + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_Prepare, + (uv_after_work_cb)UV_AfterPrepare); + + stmt->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCStatement::UV_Prepare(uv_work_t* req) { + DEBUG_PRINTF("ODBCStatement::UV_Prepare\n"); + + prepare_work_data* data = (prepare_work_data *)(req->data); + + SQLRETURN ret; + + ret = SQLPrepare( + data->stmt->m_hSTMT, + (SQLCHAR *) data->sql, + strlen(data->sql)); + + data->result = ret; +} + +void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCStatement::UV_AfterPrepare\n"); + + prepare_work_data* data = (prepare_work_data *)(req->data); + + HandleScope scope; + + //an easy reference to the statment object + ODBCStatement* self = data->stmt->self(); + + //First thing, let's check if the execution of the query returned any errors + if(data->result == SQL_ERROR) { + ODBC::CallbackSQLError( + self->m_hENV, + self->m_hDBC, + self->m_hSTMT, + data->cb); + } + else { + Local args[2]; + + args[0] = Local::New(Null()); + args[1] = Local::New(True()); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + } + + TryCatch try_catch; + + self->Unref(); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + + free(data->sql); + free(data); + free(req); + + scope.Close(Undefined()); +} + +/* + * Bind + * + */ + +Handle ODBCStatement::Bind(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::Bind\n"); + + HandleScope scope; + + if ( !args[0]->IsArray() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 1 must be an Array")) + ); + } + + REQ_FUN_ARG(1, cb); + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + bind_work_data* data = + (bind_work_data *) calloc(1, sizeof(bind_work_data)); + + data->stmt = stmt; + + data->cb = Persistent::New(cb); + + data->params = ODBC::GetParametersFromArray( + Local::Cast(args[0]), + &data->paramCount); + + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_Bind, + (uv_after_work_cb)UV_AfterBind); + + stmt->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCStatement::UV_Bind(uv_work_t* req) { + DEBUG_PRINTF("ODBCStatement::UV_Bind\n"); + + bind_work_data* data = (bind_work_data *)(req->data); + + SQLRETURN ret; + Parameter prm; + + for (int i = 0; i < data->paramCount; i++) { + prm = data->params[i]; + + DEBUG_PRINTF( + "ODBC::UV_Bind - param[%i]: c_type=%i type=%i " + "buffer_length=%i size=%i length=%i &length=%X decimals=%i\n", + i, prm.c_type, prm.type, prm.buffer_length, prm.size, prm.length, + &data->params[i].length, prm.decimals + ); + + ret = SQLBindParameter( + data->stmt->m_hSTMT, //StatementHandle + i + 1, //ParameterNumber + SQL_PARAM_INPUT, //InputOutputType + prm.c_type, //ValueType + prm.type, //ParameterType + prm.size, //ColumnSize + prm.decimals, //DecimalDigits + prm.buffer, //ParameterValuePtr + prm.buffer_length, //BufferLength + //using &prm.length did not work here... + &data->params[i].length); //StrLen_or_IndPtr + + if (ret == SQL_ERROR) { + break; + } + } + + data->result = ret; + + //free memory + for (int i = 0; i < data->paramCount; i++) { + if (prm = data->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + free(data->params); +} + +void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCStatement::UV_AfterBind\n"); + + bind_work_data* data = (bind_work_data *)(req->data); + + HandleScope scope; + + //an easy reference to the statment object + ODBCStatement* self = data->stmt->self(); + + //Check if there were errors + if(data->result == SQL_ERROR) { + ODBC::CallbackSQLError( + self->m_hENV, + self->m_hDBC, + self->m_hSTMT, + data->cb); + } + else { + Local args[2]; + + args[0] = Local::New(Null()); + args[1] = Local::New(True()); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + } + + TryCatch try_catch; + + self->Unref(); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + + free(data); + free(req); + + scope.Close(Undefined()); +} + + +/* +void ODBC::UV_Tables(uv_work_t* req) { + query_request* prep_req = (query_request *)(req->data); + + uv_mutex_lock(&ODBC::g_odbcMutex); + SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->hSTMT ); + uv_mutex_unlock(&ODBC::g_odbcMutex); + + SQLRETURN ret = SQLTables( + prep_req->hSTMT, + (SQLCHAR *) prep_req->catalog, SQL_NTS, + (SQLCHAR *) prep_req->schema, SQL_NTS, + (SQLCHAR *) prep_req->table, SQL_NTS, + (SQLCHAR *) prep_req->type, SQL_NTS + ); + + // this will be checked later in UV_AfterQuery + prep_req->result = ret; +} + +Handle ODBC::Tables(const Arguments& args) { + HandleScope scope; + + REQ_STR_OR_NULL_ARG(0, catalog); + REQ_STR_OR_NULL_ARG(1, schema); + REQ_STR_OR_NULL_ARG(2, table); + REQ_STR_OR_NULL_ARG(3, type); + Local cb = Local::Cast(args[4]); + + ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); + + if (!prep_req) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + } + + prep_req->sql = NULL; + prep_req->catalog = NULL; + prep_req->schema = NULL; + prep_req->table = NULL; + prep_req->type = NULL; + prep_req->column = NULL; + prep_req->cb = Persistent::New(cb); + + if (!String::New(*catalog)->Equals(String::New("null"))) { + prep_req->catalog = (char *) malloc(catalog.length() +1); + strcpy(prep_req->catalog, *catalog); + } + + if (!String::New(*schema)->Equals(String::New("null"))) { + prep_req->schema = (char *) malloc(schema.length() +1); + strcpy(prep_req->schema, *schema); + } + + if (!String::New(*table)->Equals(String::New("null"))) { + prep_req->table = (char *) malloc(table.length() +1); + strcpy(prep_req->table, *table); + } + + if (!String::New(*type)->Equals(String::New("null"))) { + prep_req->type = (char *) malloc(type.length() +1); + strcpy(prep_req->type, *type); + } + + prep_req->dbo = dbo; + work_req->data = prep_req; + + uv_queue_work(uv_default_loop(), work_req, UV_Tables, (uv_after_work_cb)UV_AfterQueryAll); + + dbo->Ref(); + + return scope.Close(Undefined()); +} + +void ODBC::UV_Columns(uv_work_t* req) { + query_request* prep_req = (query_request *)(req->data); + + SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->hSTMT ); + + SQLRETURN ret = SQLColumns( + prep_req->hSTMT, + (SQLCHAR *) prep_req->catalog, SQL_NTS, + (SQLCHAR *) prep_req->schema, SQL_NTS, + (SQLCHAR *) prep_req->table, SQL_NTS, + (SQLCHAR *) prep_req->column, SQL_NTS + ); + + // this will be checked later in UV_AfterQuery + prep_req->result = ret; +} + +Handle ODBC::Columns(const Arguments& args) { + HandleScope scope; + + REQ_STR_OR_NULL_ARG(0, catalog); + REQ_STR_OR_NULL_ARG(1, schema); + REQ_STR_OR_NULL_ARG(2, table); + REQ_STR_OR_NULL_ARG(3, column); + Local cb = Local::Cast(args[4]); + + ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); + + if (!prep_req) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + } + + prep_req->sql = NULL; + prep_req->catalog = NULL; + prep_req->schema = NULL; + prep_req->table = NULL; + prep_req->type = NULL; + prep_req->column = NULL; + prep_req->cb = Persistent::New(cb); + + if (!String::New(*catalog)->Equals(String::New("null"))) { + prep_req->catalog = (char *) malloc(catalog.length() +1); + strcpy(prep_req->catalog, *catalog); + } + + if (!String::New(*schema)->Equals(String::New("null"))) { + prep_req->schema = (char *) malloc(schema.length() +1); + strcpy(prep_req->schema, *schema); + } + + if (!String::New(*table)->Equals(String::New("null"))) { + prep_req->table = (char *) malloc(table.length() +1); + strcpy(prep_req->table, *table); + } + + if (!String::New(*column)->Equals(String::New("null"))) { + prep_req->column = (char *) malloc(column.length() +1); + strcpy(prep_req->column, *column); + } + + prep_req->dbo = dbo; + work_req->data = prep_req; + + uv_queue_work(uv_default_loop(), work_req, UV_Columns, (uv_after_work_cb)UV_AfterQueryAll); + + dbo->Ref(); + + return scope.Close(Undefined()); +} +*/ \ No newline at end of file diff --git a/src/odbc_statement.h b/src/odbc_statement.h new file mode 100644 index 00000000..b53223a6 --- /dev/null +++ b/src/odbc_statement.h @@ -0,0 +1,109 @@ +/* + Copyright (c) 2013, Dan VerWeire + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _SRC_ODBC_STATEMENT_H +#define _SRC_ODBC_STATEMENT_H + +class ODBCStatement : public node::ObjectWrap { + public: + static Persistent constructor_template; + static void Init(v8::Handle target); + + void Free(); + + protected: + ODBCStatement() {}; + + explicit ODBCStatement(HENV hENV, HDBC hDBC, HSTMT hSTMT): + ObjectWrap(), + m_hENV(hENV), + m_hDBC(hDBC), + m_hSTMT(hSTMT) {}; + + ~ODBCStatement(); + + //constructor + static Handle New(const Arguments& args); + + //async methods + static Handle Execute(const Arguments& args); + static void UV_Execute(uv_work_t* work_req); + static void UV_AfterExecute(uv_work_t* work_req, int status); + + static Handle ExecuteDirect(const Arguments& args); + static void UV_ExecuteDirect(uv_work_t* work_req); + static void UV_AfterExecuteDirect(uv_work_t* work_req, int status); + + static Handle Prepare(const Arguments& args); + static void UV_Prepare(uv_work_t* work_req); + static void UV_AfterPrepare(uv_work_t* work_req, int status); + + static Handle Bind(const Arguments& args); + static void UV_Bind(uv_work_t* work_req); + static void UV_AfterBind(uv_work_t* work_req, int status); + + //sync methods +// static Handle Close(const Arguments& args); + + struct Fetch_Request { + Persistent callback; + ODBCStatement *objResult; + SQLRETURN result; + }; + + ODBCStatement *self(void) { return this; } + + protected: + HENV m_hENV; + HDBC m_hDBC; + HSTMT m_hSTMT; + int results; + + uint16_t *buffer; + int bufferLength; + Column *columns; + short colCount; +}; + +struct execute_direct_work_data { + Persistent cb; + ODBCStatement *stmt; + int result; + char *sql; +}; + +struct execute_work_data { + Persistent cb; + ODBCStatement *stmt; + int result; +}; + +struct prepare_work_data { + Persistent cb; + ODBCStatement *stmt; + int result; + char *sql; +}; + +struct bind_work_data { + Persistent cb; + ODBCStatement *stmt; + Parameter *params; + int paramCount; + int result; +}; + +#endif From 7c74f2528e3dcad83ff559a101f7c545682b30b5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Apr 2013 19:18:28 -0400 Subject: [PATCH 115/511] Backwards compatability in the main library, Sync methods, other progress.. still slow --- odbc.js | 204 +++++++++++++++++++++++++++++++++++++-- src/odbc.cpp | 7 +- src/odbc_connection.cpp | 52 +++++++++- src/odbc_connection.h | 2 + src/odbc_statement.cpp | 208 ++++++++++++++++++++++++++++++++++------ src/odbc_statement.h | 10 +- test/bench-query.js | 2 +- test/common.js | 2 +- 8 files changed, 439 insertions(+), 48 deletions(-) diff --git a/odbc.js b/odbc.js index 880a2f99..5570d32b 100644 --- a/odbc.js +++ b/odbc.js @@ -1,4 +1,5 @@ /* + Copyright (c) 2013, Dan VerWeire Copyright (c) 2010, Lee Smith Permission to use, copy, modify, and/or distribute this software for any @@ -14,7 +15,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -var odbc = require("bindings")("odbc_bindings"); +var odbc = require("bindings")("odbc_bindings") + ; module.exports = function (options) { return new Database(options); @@ -23,7 +25,191 @@ module.exports = function (options) { module.exports.debug = false; module.exports.Database = Database; +module.exports.ODBC = odbc.ODBC; +module.exports.ODBCConnection = odbc.ODBCConnection; +module.exports.ODBCStatement = odbc.ODBCStatement; +module.exports.ODBCResult = odbc.ODBCResult; +function Database() { + var self = this; + + self.odbc = new odbc.ODBC(); + self.queue = new SimpleQueue(); + + self.__defineGetter__("connected", function () { + if (!self.conn) { + return false; + } + + return self.conn.connected; + }); +} + +Database.prototype.open = function (connectionString, cb) { + var self = this; + + self.odbc.createConnection(function (err, conn) { + if (err) return cb(err); + + self.conn = conn; + + self.conn.open(connectionString, cb); + }); +}; + +Database.prototype.close = function (cb) { + var self = this; + + self.conn.close(cb); +}; + +Database.prototype.query = function (sql, params, cb) { + var self = this; + + if (typeof(params) == 'function') { + cb = params; + params = null; + } + + if (!self.connected) { + return cb({ message : "Connection not open."}, [], false); + } + + self.queue.push(function (next) { + var stmt = self.conn.createStatementSync(); + if (params) { + stmt.prepare(sql, function (err, result) { + if (err) { + cb(err); + + return next(); + } + + stmt.bind(params, function (err, result) { + if (err) { + cb(err); + + return next(); + } + + stmt.execute(function (err, result) { + if (err) { + cb(err); + + return next(); + } + + result.fetchAll(function (err, data) { + stmt.closeSync(); + + cb(err, data); + + return next(); + }); + }); + }); + }); + } + else { + stmt.executeDirect(sql, function (err, result) { + if (err) { + cb(err); + + return next(); + } + + result.fetchAll(function (err, data) { + stmt.closeSync(); + + cb(err, data); + + return next(); + }); + }); + } + }); +}; + +Database.prototype.queryResult = function (sql, params, cb) { + var self = this; + + if (typeof(params) == 'function') { + cb = params; + params = null; + } + + if (!self.connected) { + return cb({ message : "Connection not open."}, [], false); + } + + self.queue.push(function (next) { + self.conn.createStatement(function (err, stmt) { + if (err) { + cb(err); + + return next(); + } + + if (params) { + stmt.prepare(sql, function (err, result) { + if (err) { + cb(err); + + return next(); + } + + stmt.bind(params, function (err, result) { + if (err) { + cb(err); + + return next(); + } + + stmt.execute(cb); + + return next(); + }); + }); + } + else { + stmt.executeDirect(sql, cb); + + return next(); + } + }); + }); +}; + +function SimpleQueue() { + var self = this; + + self.fifo = []; + self.executing = false; +} + +SimpleQueue.prototype.push = function (fn) { + var self = this; + + self.fifo.push(fn); + + self.maybeNext(); +}; + +SimpleQueue.prototype.maybeNext = function () { + var self = this; + + if (!self.executing && self.fifo.length) { + var fn = self.fifo.shift(); + + fn(function () { + self.executing = false; + + self.maybeNext(); + }); + } +}; + +/* function Database () { var self = this; var db = new odbc.Database(); @@ -162,12 +348,12 @@ Database.prototype.open = function(connectionString, callback) { }); }; -/** - * - * We must queue the close. If we don't then we may close during the middle of a query which - * could cause a segfault or other madness - * - **/ +// +// * +// * We must queue the close. If we don't then we may close during the middle of a query which +// * could cause a segfault or other madness +// * +// Database.prototype.close = function(callback) { var self = this; @@ -285,7 +471,7 @@ Database.prototype.describe = function(obj, callback) { self.tables(obj.database, obj.schema, null, obj.type || "table", callback); } }; - +*/ module.exports.Pool = Pool; Pool.count = 0; @@ -416,4 +602,4 @@ Pool.prototype.close = function (callback) { } } }, 2000); -}; \ No newline at end of file +}; diff --git a/src/odbc.cpp b/src/odbc.cpp index 1f4a352e..9796fc70 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -1264,7 +1264,8 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].buffer_length = 0; params[i].decimals = 0; - DEBUG_PRINTF("ODBC::GetParametersFromArray - ¶m[%i].length = %X\n", i, ¶ms[i].length); + DEBUG_PRINTF("ODBC::GetParametersFromArray - ¶m[%i].length = %X\n", + i, ¶ms[i].length); if (value->IsString()) { String::Utf8Value string(value); @@ -1303,9 +1304,9 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { DEBUG_PRINTF("ODBC::GetParametersFromArray - IsInt32(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i " - "value=%i\n", i, params[i].c_type, params[i].type, + "value=%lld\n", i, params[i].c_type, params[i].type, params[i].buffer_length, params[i].size, params[i].length, - params[i].buffer); + *number); } else if (value->IsNumber()) { double *number = new double(value->NumberValue()); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 1ce99499..cce4d9ab 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -48,12 +48,13 @@ void ODBCConnection::Init(v8::Handle target) { // Properties //instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); - //instance_template->SetAccessor(String::New("connected"), ConnectedGetter); + instance_template->SetAccessor(String::New("connected"), ConnectedGetter); // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open); NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatement", CreateStatement); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatementSync", CreateStatementSync); // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCConnection"), @@ -356,6 +357,35 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { } +/* + * CreateStatementSync + * + */ + +Handle ODBCConnection::CreateStatementSync(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::CreateStatementSync\n"); + HandleScope scope; + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + HSTMT hSTMT; + + SQLAllocHandle( + SQL_HANDLE_STMT, + conn->m_hDBC, + &hSTMT); + + Local params[3]; + params[0] = External::New(conn->m_hENV); + params[1] = External::New(conn->m_hDBC); + params[2] = External::New(hSTMT); + + Persistent js_result(ODBCStatement::constructor_template-> + GetFunction()->NewInstance(3, params)); + + return scope.Close(js_result); +} + /* * CreateStatement * @@ -396,11 +426,23 @@ void ODBCConnection::UV_CreateStatement(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); + DEBUG_PRINTF("ODBCConnection::UV_CreateStatement m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + data->conn->m_hENV, + data->conn->m_hDBC, + data->hSTMT + ); + //allocate a new statment handle SQLAllocHandle( SQL_HANDLE_STMT, data->conn->m_hDBC, - &data->hSTMT ); + &data->hSTMT); + DEBUG_PRINTF("ODBCConnection::UV_CreateStatement m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + data->conn->m_hENV, + data->conn->m_hDBC, + data->hSTMT + ); + uv_mutex_unlock(&ODBC::g_odbcMutex); } @@ -409,6 +451,12 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { HandleScope scope; create_statement_work_data* data = (create_statement_work_data *)(req->data); + + DEBUG_PRINTF("ODBCConnection::UV_AfterCreateStatement m_hDBC=%X m_hDBC=%X hSTMT=%X\n", + data->conn->m_hENV, + data->conn->m_hDBC, + data->hSTMT + ); Local args[3]; args[0] = External::New(data->conn->m_hENV); diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 94ee773e..c8c4c6eb 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -62,6 +62,8 @@ class ODBCConnection : public node::ObjectWrap { static void UV_CreateStatement(uv_work_t* work_req); static void UV_AfterCreateStatement(uv_work_t* work_req, int status); + static Handle CreateStatementSync(const Arguments& args); + struct Fetch_Request { Persistent callback; ODBCConnection *objResult; diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 0eebbc26..a34c43e5 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -47,8 +47,14 @@ void ODBCStatement::Init(v8::Handle target) { // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "execute", Execute); NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirect", ExecuteDirect); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepareSync", PrepareSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "bind", Bind); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "bindSync", BindSync); + + NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCStatement"), @@ -195,6 +201,26 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { data->cb.Dispose(); + if (data->stmt->paramCount) { + Parameter prm; + + //free parameter memory + for (int i = 0; i < data->stmt->paramCount; i++) { + if (prm = data->stmt->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + data->stmt->paramCount = 0; + + free(data->stmt->params); + } + free(data); free(req); @@ -305,6 +331,40 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { scope.Close(Undefined()); } +/* + * PrepareSync + * + */ + +Handle ODBCStatement::PrepareSync(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::PrepareSync\n"); + + HandleScope scope; + + REQ_STR_ARG(0, sql); + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + SQLRETURN ret; + char *sql2; + + sql2 = (char *) malloc(sql.length() +1); + strcpy(sql2, *sql); + + ret = SQLPrepare( + stmt->m_hSTMT, + (SQLCHAR *) sql2, + strlen(sql2)); + + if (SQL_SUCCEEDED(ret)) { + return scope.Close(True()); + } + else { + //TODO: throw an error object + return scope.Close(False()); + } +} + /* * Prepare * @@ -350,6 +410,12 @@ void ODBCStatement::UV_Prepare(uv_work_t* req) { prepare_work_data* data = (prepare_work_data *)(req->data); + DEBUG_PRINTF("ODBCStatement::UV_Prepare m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + data->stmt->m_hENV, + data->stmt->m_hDBC, + data->stmt->m_hSTMT + ); + SQLRETURN ret; ret = SQLPrepare( @@ -365,17 +431,20 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { prepare_work_data* data = (prepare_work_data *)(req->data); - HandleScope scope; + DEBUG_PRINTF("ODBCStatement::UV_AfterPrepare m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + data->stmt->m_hENV, + data->stmt->m_hDBC, + data->stmt->m_hSTMT + ); - //an easy reference to the statment object - ODBCStatement* self = data->stmt->self(); + HandleScope scope; //First thing, let's check if the execution of the query returned any errors if(data->result == SQL_ERROR) { ODBC::CallbackSQLError( - self->m_hENV, - self->m_hDBC, - self->m_hSTMT, + data->stmt->m_hENV, + data->stmt->m_hDBC, + data->stmt->m_hSTMT, data->cb); } else { @@ -389,7 +458,7 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { TryCatch try_catch; - self->Unref(); + data->stmt->Unref(); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -404,6 +473,76 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { scope.Close(Undefined()); } +/* + * BindSync + * + */ + +Handle ODBCStatement::BindSync(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::BindSync\n"); + + HandleScope scope; + + if ( !args[0]->IsArray() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 1 must be an Array")) + ); + } + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + DEBUG_PRINTF("ODBCStatement::BindSync m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + stmt->m_hENV, + stmt->m_hDBC, + stmt->m_hSTMT + ); + + stmt->params = ODBC::GetParametersFromArray( + Local::Cast(args[0]), + &stmt->paramCount); + + SQLRETURN ret; + Parameter prm; + + for (int i = 0; i < stmt->paramCount; i++) { + prm = stmt->params[i]; + + DEBUG_PRINTF( + "ODBC::BindSync - param[%i]: c_type=%i type=%i " + "buffer_length=%i size=%i length=%i &length=%X decimals=%i value=%s\n", + i, prm.c_type, prm.type, prm.buffer_length, prm.size, prm.length, + &stmt->params[i].length, prm.decimals, prm.buffer + ); + + ret = SQLBindParameter( + stmt->m_hSTMT, //StatementHandle + i + 1, //ParameterNumber + SQL_PARAM_INPUT, //InputOutputType + prm.c_type, //ValueType + prm.type, //ParameterType + prm.size, //ColumnSize + prm.decimals, //DecimalDigits + prm.buffer, //ParameterValuePtr + prm.buffer_length, //BufferLength + //using &prm.length did not work here... + &stmt->params[i].length); //StrLen_or_IndPtr + + if (ret == SQL_ERROR) { + break; + } + } + + if (SQL_SUCCEEDED(ret)) { + return scope.Close(True()); + } + else { + //TODO: throw an error object + return scope.Close(False()); + } + + return scope.Close(Undefined()); +} + /* * Bind * @@ -431,11 +570,17 @@ Handle ODBCStatement::Bind(const Arguments& args) { data->stmt = stmt; + DEBUG_PRINTF("ODBCStatement::Bind m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + data->stmt->m_hENV, + data->stmt->m_hDBC, + data->stmt->m_hSTMT + ); + data->cb = Persistent::New(cb); - data->params = ODBC::GetParametersFromArray( + data->stmt->params = ODBC::GetParametersFromArray( Local::Cast(args[0]), - &data->paramCount); + &data->stmt->paramCount); work_req->data = data; @@ -455,17 +600,23 @@ void ODBCStatement::UV_Bind(uv_work_t* req) { bind_work_data* data = (bind_work_data *)(req->data); + DEBUG_PRINTF("ODBCStatement::UV_Bind m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + data->stmt->m_hENV, + data->stmt->m_hDBC, + data->stmt->m_hSTMT + ); + SQLRETURN ret; Parameter prm; - for (int i = 0; i < data->paramCount; i++) { - prm = data->params[i]; + for (int i = 0; i < data->stmt->paramCount; i++) { + prm = data->stmt->params[i]; DEBUG_PRINTF( "ODBC::UV_Bind - param[%i]: c_type=%i type=%i " - "buffer_length=%i size=%i length=%i &length=%X decimals=%i\n", + "buffer_length=%i size=%i length=%i &length=%X decimals=%i value=%s\n", i, prm.c_type, prm.type, prm.buffer_length, prm.size, prm.length, - &data->params[i].length, prm.decimals + &data->stmt->params[i].length, prm.decimals, prm.buffer ); ret = SQLBindParameter( @@ -479,7 +630,7 @@ void ODBCStatement::UV_Bind(uv_work_t* req) { prm.buffer, //ParameterValuePtr prm.buffer_length, //BufferLength //using &prm.length did not work here... - &data->params[i].length); //StrLen_or_IndPtr + &data->stmt->params[i].length); //StrLen_or_IndPtr if (ret == SQL_ERROR) { break; @@ -487,20 +638,6 @@ void ODBCStatement::UV_Bind(uv_work_t* req) { } data->result = ret; - - //free memory - for (int i = 0; i < data->paramCount; i++) { - if (prm = data->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; - } - } - } - - free(data->params); } void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { @@ -546,6 +683,21 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { scope.Close(Undefined()); } +/* + * CloseSync + */ + +Handle ODBCStatement::CloseSync(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::Execute\n"); + + HandleScope scope; + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + stmt->Free(); + + return scope.Close(True()); +} /* void ODBC::UV_Tables(uv_work_t* req) { diff --git a/src/odbc_statement.h b/src/odbc_statement.h index b53223a6..1f2d8dd1 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -56,7 +56,9 @@ class ODBCStatement : public node::ObjectWrap { static void UV_AfterBind(uv_work_t* work_req, int status); //sync methods -// static Handle Close(const Arguments& args); + static Handle BindSync(const Arguments& args); + static Handle PrepareSync(const Arguments& args); + static Handle CloseSync(const Arguments& args); struct Fetch_Request { Persistent callback; @@ -70,7 +72,9 @@ class ODBCStatement : public node::ObjectWrap { HENV m_hENV; HDBC m_hDBC; HSTMT m_hSTMT; - int results; + + Parameter *params; + int paramCount; uint16_t *buffer; int bufferLength; @@ -101,8 +105,6 @@ struct prepare_work_data { struct bind_work_data { Persistent cb; ODBCStatement *stmt; - Parameter *params; - int paramCount; int result; }; diff --git a/test/bench-query.js b/test/bench-query.js index 8e3e3343..aa7c430e 100644 --- a/test/bench-query.js +++ b/test/bench-query.js @@ -13,7 +13,7 @@ db.open(common.connectionString, function(err){ function issueQuery() { var count = 0 - , iterations = 10000 + , iterations = 1000 , time = new Date().getTime(); for (var x = 0; x < iterations; x++) { diff --git a/test/common.js b/test/common.js index 962ea529..b8eec320 100644 --- a/test/common.js +++ b/test/common.js @@ -1,5 +1,5 @@ exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; -//exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; +exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; //exports.connectionString = process.env.ODBC_CONNETION_STRING; exports.connectionObject = { From 430c95a2d1d5e4f92ea1319585979a1588e5d85f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Apr 2013 14:58:30 -0400 Subject: [PATCH 116/511] Create a fast-path query on the connection object --- odbc.js | 107 ++++++--------------- src/odbc_connection.cpp | 199 +++++++++++++++++++++++++++++++++++++++- src/odbc_connection.h | 15 +++ 3 files changed, 243 insertions(+), 78 deletions(-) diff --git a/odbc.js b/odbc.js index 5570d32b..0cfc7b27 100644 --- a/odbc.js +++ b/odbc.js @@ -68,7 +68,7 @@ Database.prototype.query = function (sql, params, cb) { if (typeof(params) == 'function') { cb = params; - params = null; + params = []; } if (!self.connected) { @@ -76,57 +76,32 @@ Database.prototype.query = function (sql, params, cb) { } self.queue.push(function (next) { - var stmt = self.conn.createStatementSync(); - if (params) { - stmt.prepare(sql, function (err, result) { - if (err) { - cb(err); - - return next(); - } - - stmt.bind(params, function (err, result) { - if (err) { - cb(err); - - return next(); - } - - stmt.execute(function (err, result) { - if (err) { - cb(err); - - return next(); - } - - result.fetchAll(function (err, data) { - stmt.closeSync(); - - cb(err, data); - - return next(); - }); - }); - }); - }); - } - else { - stmt.executeDirect(sql, function (err, result) { - if (err) { - cb(err); - - return next(); - } + //ODBCConnection.query() is the fastest-path querying mechanism. + self.conn.query(sql, params, function (err, result) { + if (err) { + cb(err, [], false); + return next(); + } + + fetchMore(); + + function fetchMore() { + //TODO: keep calling back if there are more result sets result.fetchAll(function (err, data) { - stmt.closeSync(); + var moreResults = result.moreResults(); - cb(err, data); + cb(err, data, moreResults); - return next(); + if (moreResults) { + return fetchMore(); + } + else { + return next(); + } }); - }); - } + } + }); }); }; @@ -135,7 +110,7 @@ Database.prototype.queryResult = function (sql, params, cb) { if (typeof(params) == 'function') { cb = params; - params = null; + params = []; } if (!self.connected) { @@ -143,39 +118,17 @@ Database.prototype.queryResult = function (sql, params, cb) { } self.queue.push(function (next) { - self.conn.createStatement(function (err, stmt) { - if (err) { - cb(err); + //ODBCConnection.query() is the fastest-path querying mechanism. + self.conn.query(sql, params, function (err, result) { + if (err) { + cb(err, null); return next(); } - if (params) { - stmt.prepare(sql, function (err, result) { - if (err) { - cb(err); - - return next(); - } - - stmt.bind(params, function (err, result) { - if (err) { - cb(err); - - return next(); - } - - stmt.execute(cb); - - return next(); - }); - }); - } - else { - stmt.executeDirect(sql, cb); - - return next(); - } + cb(err, result); + + return next(); }); }); }; diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index cce4d9ab..5150b7fa 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -55,7 +55,8 @@ void ODBCConnection::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatement", CreateStatement); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatementSync", CreateStatementSync); - + NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", Query); + // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCConnection"), constructor_template->GetFunction()); @@ -479,3 +480,199 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { scope.Close(Undefined()); } + +/* + * Query + */ + +Handle ODBCConnection::Query(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::Query\n"); + + HandleScope scope; + + REQ_STR_ARG(0, sql); + + Local cb; + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + query_work_data* data = (query_work_data *) calloc(1, sizeof(query_work_data)); + + // populate data->params if parameters were supplied + if (args.Length() > 2) { + if ( !args[1]->IsArray() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 1 must be an Array")) + ); + } + else if ( !args[2]->IsFunction() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 2 must be a Function")) + ); + } + + cb = Local::Cast(args[2]); + + data->params = ODBC::GetParametersFromArray( + Local::Cast(args[1]), + &data->paramCount); + } + else { + if ( !args[1]->IsFunction() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 1 must be a Function")) + ); + } + + cb = Local::Cast(args[1]); + + data->paramCount = 0; + } + + data->sql = (char *) malloc(sql.length() +1); + data->cb = Persistent::New(cb); + + strcpy(data->sql, *sql); + + data->conn = conn; + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_Query, + (uv_after_work_cb)UV_AfterQuery); + + conn->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCConnection::UV_Query(uv_work_t* req) { + DEBUG_PRINTF("ODBCConnection::UV_Query\n"); + + query_work_data* data = (query_work_data *)(req->data); + + Parameter prm; + SQLRETURN ret; + + uv_mutex_lock(&ODBC::g_odbcMutex); + + //allocate a new statment handle + SQLAllocHandle( SQL_HANDLE_STMT, + data->conn->m_hDBC, + &data->hSTMT ); + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + //check to see if should excute a direct or a parameter bound query + if (!data->paramCount) { + // execute the query directly + ret = SQLExecDirect( + data->hSTMT, + (SQLCHAR *) data->sql, + strlen(data->sql)); + } + else { + // prepare statement, bind parameters and execute statement + ret = SQLPrepare( + data->hSTMT, + (SQLCHAR *) data->sql, + strlen(data->sql)); + + if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { + for (int i = 0; i < data->paramCount; i++) { + prm = data->params[i]; + + DEBUG_PRINTF("ODBCConnection::UV_Query - param[%i]: c_type=%i type=%i " + "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, + prm.buffer_length, prm.size, prm.length, &data->params[i].length); + + ret = SQLBindParameter( + data->hSTMT, //StatementHandle + i + 1, //ParameterNumber + SQL_PARAM_INPUT, //InputOutputType + prm.c_type, //ValueType + prm.type, //ParameterType + prm.size, //ColumnSize + 0, //DecimalDigits + prm.buffer, //ParameterValuePtr + prm.buffer_length, //BufferLength + //using &prm.length did not work here... + &data->params[i].length); //StrLen_or_IndPtr + + if (ret == SQL_ERROR) {break;} + } + + if (SQL_SUCCEEDED(ret)) { + ret = SQLExecute(data->hSTMT); + } + } + + // free parameters + for (int i = 0; i < data->paramCount; i++) { + if (prm = data->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_LONG: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + free(data->params); + } + + // this will be checked later in UV_AfterQuery + data->result = ret; +} + +void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCConnection::UV_AfterQuery\n"); + + HandleScope scope; + + query_work_data* data = (query_work_data *)(req->data); + + //check to see if there was an error during execution + if(data->result == SQL_ERROR) { + ODBC::CallbackSQLError( + data->conn->m_hENV, + data->conn->m_hDBC, + data->hSTMT, + data->cb); + } + else { + Local args[3]; + args[0] = External::New(data->conn->m_hENV); + args[1] = External::New(data->conn->m_hDBC); + args[2] = External::New(data->hSTMT); + Persistent js_result(ODBCResult::constructor_template-> + GetFunction()->NewInstance(3, args)); + + args[0] = Local::New(Null()); + args[1] = Local::New(js_result); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + } + + TryCatch try_catch; + + data->conn->Unref(); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + free(data->sql); + free(data); + free(req); + + scope.Close(Undefined()); +} + + + diff --git a/src/odbc_connection.h b/src/odbc_connection.h index c8c4c6eb..8ec91731 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -62,6 +62,11 @@ class ODBCConnection : public node::ObjectWrap { static void UV_CreateStatement(uv_work_t* work_req); static void UV_AfterCreateStatement(uv_work_t* work_req, int status); + static Handle Query(const Arguments& args); + static void UV_Query(uv_work_t* req); + static void UV_AfterQuery(uv_work_t* req, int status); + + //sync methods static Handle CreateStatementSync(const Arguments& args); struct Fetch_Request { @@ -87,6 +92,16 @@ struct create_statement_work_data { int result; }; +struct query_work_data { + Persistent cb; + ODBCConnection *conn; + HSTMT hSTMT; + Parameter *params; + int paramCount; + char *sql; + int result; +}; + struct open_connection_work_data { Persistent cb; ODBCConnection *conn; From 3378f315272690027b1d2ec19e6d5f322c2688f9 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Apr 2013 16:27:20 -0400 Subject: [PATCH 117/511] standardize code and function names --- odbc.js | 2 +- src/odbc_result.cpp | 190 ++++++++++++++++++++++++-------------------- src/odbc_result.h | 10 ++- test/common.js | 2 +- 4 files changed, 111 insertions(+), 93 deletions(-) diff --git a/odbc.js b/odbc.js index 0cfc7b27..d88a9871 100644 --- a/odbc.js +++ b/odbc.js @@ -89,7 +89,7 @@ Database.prototype.query = function (sql, params, cb) { function fetchMore() { //TODO: keep calling back if there are more result sets result.fetchAll(function (err, data) { - var moreResults = result.moreResults(); + var moreResults = result.moreResultsSync(); cb(err, data, moreResults); diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 1fffb7ce..e77ac600 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -47,8 +47,9 @@ void ODBCResult::Init(v8::Handle target) { // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAll", FetchAll); NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetch", Fetch); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "moreResults", MoreResults); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); + + NODE_SET_PROTOTYPE_METHOD(constructor_template, "moreResultsSync", MoreResultsSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCResult"), @@ -112,14 +113,20 @@ Handle ODBCResult::New(const Arguments& args) { return scope.Close(args.Holder()); } +/* + * Fetch + */ + Handle ODBCResult::Fetch(const Arguments& args) { DEBUG_PRINTF("ODBCResult::Fetch\n"); HandleScope scope; ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - Fetch_Request* req_fetch = (Fetch_Request *) calloc(1, sizeof(Fetch_Request)); + + fetch_work_data* data = (fetch_work_data *) calloc(1, sizeof(fetch_work_data)); Local cb; @@ -131,12 +138,16 @@ Handle ODBCResult::Fetch(const Arguments& args) { cb = Local::Cast(args[0]); - req_fetch->callback = Persistent::New(cb); + data->cb = Persistent::New(cb); - req_fetch->objResult = objODBCResult; - work_req->data = req_fetch; + data->objResult = objODBCResult; + work_req->data = data; - uv_queue_work(uv_default_loop(), work_req, UV_Fetch, (uv_after_work_cb)UV_AfterFetch); + uv_queue_work( + uv_default_loop(), + work_req, + UV_Fetch, + (uv_after_work_cb)UV_AfterFetch); objODBCResult->Ref(); @@ -146,11 +157,9 @@ Handle ODBCResult::Fetch(const Arguments& args) { void ODBCResult::UV_Fetch(uv_work_t* work_req) { DEBUG_PRINTF("ODBCResult::UV_Fetch\n"); - Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); - - ODBCResult* self = req_fetch->objResult->self(); + fetch_work_data* data = (fetch_work_data *)(work_req->data); - req_fetch->result = SQLFetch(self->m_hSTMT); + data->result = SQLFetch(data->objResult->m_hSTMT); } void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { @@ -158,95 +167,84 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { HandleScope scope; - Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); + fetch_work_data* data = (fetch_work_data *)(work_req->data); - SQLRETURN ret = req_fetch->result; - - ODBCResult* self = req_fetch->objResult->self(); + SQLRETURN ret = data->result; //check to see if there was an error if (ret == SQL_ERROR) { - Local objError = Object::New(); - - char errorMessage[512]; - char errorSQLState[128]; - - SQLError( self->m_hENV, - self->m_hDBC, - self->m_hSTMT, - (SQLCHAR *) errorSQLState, - NULL, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - NULL); - - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), - String::New("[node-odbc] Error in SQLFetch")); - objError->Set(String::New("message"), String::New(errorMessage)); - - //emit an error event immidiately. - Local args[1]; - args[0] = objError; - req_fetch->callback->Call(Context::GetCurrent()->Global(), 1, args); - req_fetch->callback.Dispose(); + ODBC::CallbackSQLError( + data->objResult->m_hENV, + data->objResult->m_hDBC, + data->objResult->m_hSTMT, + data->cb); + free(data); free(work_req); - free(req_fetch); - self->Unref(); + data->objResult->Unref(); return; } //check to see if we are at the end of the recordset if (ret == SQL_NO_DATA) { - ODBC::FreeColumns(self->columns, &self->colCount); + ODBC::FreeColumns(data->objResult->columns, &data->objResult->colCount); Handle args[2]; args[0] = Null(); args[1] = Null(); - req_fetch->callback->Call(Context::GetCurrent()->Global(), 2, args); - req_fetch->callback.Dispose(); + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb.Dispose(); + free(data); free(work_req); - free(req_fetch); - self->Unref(); + data->objResult->Unref(); return; } - if (self->colCount == 0) { - self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); + if (data->objResult->colCount == 0) { + data->objResult->columns = ODBC::GetColumns( + data->objResult->m_hSTMT, + &data->objResult->colCount); } Handle args[2]; args[0] = Null(); - args[1] = ODBC::GetRecordTuple( self->m_hSTMT, - self->columns, - &self->colCount, - self->buffer, - self->bufferLength); + args[1] = ODBC::GetRecordTuple( + data->objResult->m_hSTMT, + data->objResult->columns, + &data->objResult->colCount, + data->objResult->buffer, + data->objResult->bufferLength); - req_fetch->callback->Call(Context::GetCurrent()->Global(), 2, args); - req_fetch->callback.Dispose(); + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb.Dispose(); + free(data); free(work_req); - free(req_fetch); + return; } +/* + * FetchAll + */ + Handle ODBCResult::FetchAll(const Arguments& args) { DEBUG_PRINTF("ODBCResult::FetchAll\n"); HandleScope scope; ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - Fetch_Request* fetch_Request = (Fetch_Request *) calloc(1, sizeof(Fetch_Request)); + + fetch_work_data* data = (fetch_work_data *) calloc(1, sizeof(fetch_work_data)); Local cb; @@ -258,25 +256,42 @@ Handle ODBCResult::FetchAll(const Arguments& args) { cb = Local::Cast(args[0]); - fetch_Request->callback = Persistent::New(cb); + data->cb = Persistent::New(cb); + data->objResult = objODBCResult; - fetch_Request->objResult = objODBCResult; - work_req->data = fetch_Request; + work_req->data = data; - uv_queue_work(uv_default_loop(), work_req, UV_FetchAll, (uv_after_work_cb)UV_AfterFetchAll); + uv_queue_work(uv_default_loop(), + work_req, + UV_FetchAll, + (uv_after_work_cb)UV_AfterFetchAll); - objODBCResult->Ref(); + data->objResult->Ref(); return scope.Close(Undefined()); } void ODBCResult::UV_FetchAll(uv_work_t* work_req) { DEBUG_PRINTF("ODBCResult::UV_FetchAll\n"); - //Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); - - //ODBCResult* self = req_fetch->objResult->self(); - - //req_fetch->result = SQLFetch(self->m_hSTMT); + //fetch_work_data* data = (fetch_work_data *)(work_req->data); + + /* + * NOTE: Terriblness resides here: + * + * FetchAll is not actually asynchronous at this time. In order to truly do this + * we would need to allocate memory here and load the entire recordset into + * some structure so that we can then loop over that structure in UV_AfterFetchAll + * + * One reason that this is the case is that somewhere I read that you should + * not do anything with V8 data structure while you are in the thread pool. + * If that is not true, then we could just do all of UV_AfterFetchAll right here + * while we are in the thread pool. + * + * For true async behaviour, use Fetch; one at a time. + * + */ + + //data->result = SQLFetch(data->objResult->m_hSTMT); } void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { @@ -284,9 +299,9 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { HandleScope scope; - Fetch_Request* req_fetch = (Fetch_Request *)(work_req->data); + fetch_work_data* data = (fetch_work_data *)(work_req->data); - ODBCResult* self = req_fetch->objResult->self(); + ODBCResult* self = data->objResult->self(); Local objError = Object::New(); @@ -303,8 +318,6 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { while (true) { SQLRETURN ret = SQLFetch(self->m_hSTMT); - ODBCResult* self = req_fetch->objResult->self(); - //check to see if there was an error if (ret == SQL_ERROR) { errorCount++; @@ -336,12 +349,15 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { break; } - rows->Set( Integer::New(count), - ODBC::GetRecordTuple( self->m_hSTMT, - self->columns, - &self->colCount, - self->buffer, - self->bufferLength)); + rows->Set( + Integer::New(count), + ODBC::GetRecordTuple( + self->m_hSTMT, + self->columns, + &self->colCount, + self->buffer, + self->bufferLength) + ); count++; } @@ -350,36 +366,36 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { args[0] = Null(); args[1] = rows; - req_fetch->callback->Call(Context::GetCurrent()->Global(), 2, args); - req_fetch->callback.Dispose(); + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb.Dispose(); + free(data); free(work_req); - free(req_fetch); self->Unref(); } -Handle ODBCResult::Close(const Arguments& args) { +Handle ODBCResult::CloseSync(const Arguments& args) { DEBUG_PRINTF("ODBCResult::Close\n"); HandleScope scope; - ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); + ODBCResult* result = ObjectWrap::Unwrap(args.Holder()); - objODBCResult->Free(); + result->Free(); return scope.Close(Undefined()); } -Handle ODBCResult::MoreResults(const Arguments& args) { +Handle ODBCResult::MoreResultsSync(const Arguments& args) { DEBUG_PRINTF("ODBCResult::MoreResults\n"); HandleScope scope; - ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); - objODBCResult->colCount = 0; + ODBCResult* result = ObjectWrap::Unwrap(args.Holder()); + //result->colCount = 0; - SQLRETURN ret = SQLMoreResults(objODBCResult->m_hSTMT); + SQLRETURN ret = SQLMoreResults(result->m_hSTMT); return scope.Close(SQL_SUCCEEDED(ret) ? True() : False()); } diff --git a/src/odbc_result.h b/src/odbc_result.h index ae378871..4c0d7bd6 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -48,11 +48,11 @@ class ODBCResult : public node::ObjectWrap { static void UV_AfterFetchAll(uv_work_t* work_req, int status); //sync methods - static Handle Close(const Arguments& args); - static Handle MoreResults(const Arguments& args); + static Handle CloseSync(const Arguments& args); + static Handle MoreResultsSync(const Arguments& args); - struct Fetch_Request { - Persistent callback; + struct fetch_work_data { + Persistent cb; ODBCResult *objResult; SQLRETURN result; }; @@ -69,4 +69,6 @@ class ODBCResult : public node::ObjectWrap { short colCount; }; + + #endif diff --git a/test/common.js b/test/common.js index b8eec320..962ea529 100644 --- a/test/common.js +++ b/test/common.js @@ -1,5 +1,5 @@ exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; -exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; +//exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; //exports.connectionString = process.env.ODBC_CONNETION_STRING; exports.connectionObject = { From 55400505fe9692fdcf7fb0699c0594bb72eba04c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Apr 2013 16:42:46 -0400 Subject: [PATCH 118/511] reduce number of queries during bench --- test/bench-query.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bench-query.js b/test/bench-query.js index 8e3e3343..aa7c430e 100644 --- a/test/bench-query.js +++ b/test/bench-query.js @@ -13,7 +13,7 @@ db.open(common.connectionString, function(err){ function issueQuery() { var count = 0 - , iterations = 10000 + , iterations = 1000 , time = new Date().getTime(); for (var x = 0; x < iterations; x++) { From 8b44e03aa9472c493caf837b7e63ef11694dd1d2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Apr 2013 16:43:09 -0400 Subject: [PATCH 119/511] add cflags -g for debugging --- binding.gyp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/binding.gyp b/binding.gyp index d3e85f4b..bfc4a50a 100644 --- a/binding.gyp +++ b/binding.gyp @@ -12,6 +12,9 @@ [ 'OS == "linux"', { 'libraries' : [ '-lodbc' + ], + 'cflags' : [ + '-g' ] }], [ 'OS == "mac"', { From 11c0bdcbf7bac050b1b54d84dc31242d48757c81 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Apr 2013 19:25:17 -0400 Subject: [PATCH 120/511] replace result.close() with result.closeSync() and bump iterations back to 10k --- test/bench-query-fetch.js | 2 +- test/bench-query-fetchAll.js | 2 +- test/bench-query.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/bench-query-fetch.js b/test/bench-query-fetch.js index 3f3440ee..62048cb2 100644 --- a/test/bench-query-fetch.js +++ b/test/bench-query-fetch.js @@ -38,7 +38,7 @@ function issueQuery() { //if data is null, then no more data if (!data) { - rs.close(); + rs.closeSync(); if (++count == iterations) { var elapsed = new Date().getTime() - time; diff --git a/test/bench-query-fetchAll.js b/test/bench-query-fetchAll.js index bc683573..ae6461c6 100644 --- a/test/bench-query-fetchAll.js +++ b/test/bench-query-fetchAll.js @@ -32,7 +32,7 @@ function issueQuery() { return finish(); } - result.close(); + result.closeSync(); if (++count == iterations) { var elapsed = new Date().getTime() - time; diff --git a/test/bench-query.js b/test/bench-query.js index aa7c430e..8e3e3343 100644 --- a/test/bench-query.js +++ b/test/bench-query.js @@ -13,7 +13,7 @@ db.open(common.connectionString, function(err){ function issueQuery() { var count = 0 - , iterations = 1000 + , iterations = 10000 , time = new Date().getTime(); for (var x = 0; x < iterations; x++) { From 687769603d63defbe71c6383f7bf6c1713e6678e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Apr 2013 19:25:57 -0400 Subject: [PATCH 121/511] binding.gyp: enable -g cflags for linux --- binding.gyp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/binding.gyp b/binding.gyp index d1cae68f..c0aa30d9 100644 --- a/binding.gyp +++ b/binding.gyp @@ -15,6 +15,9 @@ [ 'OS == "linux"', { 'libraries' : [ '-lodbc' + ], + 'cflags' : [ + '-g' ] }], [ 'OS == "mac"', { From 71e5c6a375d3b66881b4eea2692347d5e7a9d72a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Apr 2013 19:26:51 -0400 Subject: [PATCH 122/511] odbc.js: fix bug in SimpleQueue --- odbc.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/odbc.js b/odbc.js index d88a9871..81d73509 100644 --- a/odbc.js +++ b/odbc.js @@ -154,6 +154,8 @@ SimpleQueue.prototype.maybeNext = function () { if (!self.executing && self.fifo.length) { var fn = self.fifo.shift(); + self.executing = true; + fn(function () { self.executing = false; From a1c293d60882ebbbeb3c394d4e4e6ec86c6892ca Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Apr 2013 19:27:52 -0400 Subject: [PATCH 123/511] odbc.js: fake the connection status to avoid c++ calls, use result.closeSync() --- odbc.js | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/odbc.js b/odbc.js index 81d73509..56ac16bd 100644 --- a/odbc.js +++ b/odbc.js @@ -36,13 +36,14 @@ function Database() { self.odbc = new odbc.ODBC(); self.queue = new SimpleQueue(); - self.__defineGetter__("connected", function () { - if (!self.conn) { - return false; - } - - return self.conn.connected; - }); + self.connected = false; +// self.__defineGetter__("connected", function () { +// if (!self.conn) { +// return false; +// } +// +// return self.conn.connected; +// }); } Database.prototype.open = function (connectionString, cb) { @@ -53,14 +54,24 @@ Database.prototype.open = function (connectionString, cb) { self.conn = conn; - self.conn.open(connectionString, cb); + self.conn.open(connectionString, function (err, result) { + if (err) return cb(err); + + self.connected = true; + + return cb(err, result); + }); }); }; Database.prototype.close = function (cb) { var self = this; - self.conn.close(cb); + self.conn.close(function (err) { + self.connected = false; + + return cb(err); + }); }; Database.prototype.query = function (sql, params, cb) { @@ -97,6 +108,8 @@ Database.prototype.query = function (sql, params, cb) { return fetchMore(); } else { + result.closeSync(); + return next(); } }); From a552ec88cc3e150ba26377072df62365eafda775 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Apr 2013 19:28:31 -0400 Subject: [PATCH 124/511] add debug statments and add missing Unref() --- src/odbc_result.cpp | 16 ++++++++++++++-- src/odbc_statement.cpp | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index e77ac600..3d43a2a0 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -32,6 +32,7 @@ using namespace node; Persistent ODBCResult::constructor_template; void ODBCResult::Init(v8::Handle target) { + DEBUG_PRINTF("ODBCResult::Init\n"); HandleScope scope; Local t = FunctionTemplate::New(New); @@ -63,7 +64,7 @@ ODBCResult::~ODBCResult() { } void ODBCResult::Free() { - DEBUG_PRINTF("ODBCResult::Free\n"); + DEBUG_PRINTF("ODBCResult::Free m_hSTMT=%X\n", m_hSTMT); if (m_hSTMT) { uv_mutex_lock(&ODBC::g_odbcMutex); @@ -71,7 +72,10 @@ void ODBCResult::Free() { //This doesn't actually deallocate the statement handle //that should not be done by the result object; that should //be done by the statement object - SQLFreeStmt(m_hSTMT, SQL_CLOSE); + //SQLFreeStmt(m_hSTMT, SQL_CLOSE); + + SQLFreeHandle( SQL_HANDLE_STMT, m_hSTMT); + m_hSTMT = NULL; uv_mutex_unlock(&ODBC::g_odbcMutex); @@ -98,6 +102,12 @@ Handle ODBCResult::New(const Arguments& args) { //create a new OBCResult object ODBCResult* objODBCResult = new ODBCResult(hENV, hDBC, hSTMT); + DEBUG_PRINTF("ODBCResult::New m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + objODBCResult->m_hENV, + objODBCResult->m_hDBC, + objODBCResult->m_hSTMT + ); + //specify the buffer length objODBCResult->bufferLength = MAX_VALUE_SIZE - 1; @@ -228,6 +238,8 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { free(data); free(work_req); + data->objResult->Unref(); + return; } diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index a34c43e5..40891e1d 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -32,6 +32,7 @@ using namespace node; Persistent ODBCStatement::constructor_template; void ODBCStatement::Init(v8::Handle target) { + DEBUG_PRINTF("ODBCStatement::Init\n"); HandleScope scope; Local t = FunctionTemplate::New(New); From a1ce81803432d7ce6dab162cf09f1b607d1288bd Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 14:40:14 -0400 Subject: [PATCH 125/511] remove dead code from odbc.cpp --- src/odbc.cpp | 268 +-------------------------------------------------- 1 file changed, 2 insertions(+), 266 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 9796fc70..d71013af 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -53,18 +53,8 @@ void ODBC::Init(v8::Handle target) { Local instance_template = constructor_template->InstanceTemplate(); instance_template->SetInternalFieldCount(1); - // Properties -// instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); -// instance_template->SetAccessor(String::New("connected"), ConnectedGetter); - // Prototype Methods - NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnection", CreateConnection); -// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchOpen", Open); -// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchClose", Close); -// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchQuery", Query); -// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchQueryAll", QueryAll); -// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchTables", Tables); -// NODE_SET_PROTOTYPE_METHOD(constructor_template, "dispatchColumns", Columns); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnection", CreateConnection); // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBC"), @@ -203,262 +193,8 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { scope.Close(Undefined()); } -/* -Handle ODBC::ModeGetter(Local property, const AccessorInfo &info) { - HandleScope scope; - - ODBC *obj = ObjectWrap::Unwrap(info.Holder()); - - if (obj->mode > 0) { - return scope.Close(Integer::New(obj->mode)); - } else { - return Undefined(); - } -} - -void ODBC::ModeSetter(Local property, Local value, const AccessorInfo &info) { - HandleScope scope; - - ODBC *obj = ObjectWrap::Unwrap(info.Holder()); - - if (value->IsNumber()) { - obj->mode = value->Int32Value(); - } -} - -Handle ODBC::ConnectedGetter(Local property, const AccessorInfo &info) { - HandleScope scope; - - ODBC *obj = ObjectWrap::Unwrap(info.Holder()); - - return scope.Close(obj->connected ? True() : False()); -} - -void ODBC::UV_AfterOpen(uv_work_t* req, int status) { - DEBUG_PRINTF("ODBC::UV_AfterOpen\n"); - HandleScope scope; - open_request* open_req = (open_request *)(req->data); - - ODBC* self = open_req->dbo->self(); - - Local argv[1]; - - bool err = false; - - if (open_req->result) { - err = true; - - SQLINTEGER i = 0; - SQLINTEGER native; - SQLSMALLINT len; - SQLRETURN ret; - char errorSQLState[7]; - char errorMessage[256]; - - do { - ret = SQLGetDiagRec( SQL_HANDLE_DBC, - open_req->dbo->self()->m_hDBC, - ++i, - (SQLCHAR *) errorSQLState, - &native, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - &len ); - - if (SQL_SUCCEEDED(ret)) { - Local objError = Object::New(); - - objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); - objError->Set(String::New("message"), String::New(errorMessage)); - objError->Set(String::New("state"), String::New(errorSQLState)); - - argv[0] = objError; - } - } while( ret == SQL_SUCCESS ); - } - - if (!err) { - self->connected = true; - - //only uv_ref if the connection was successful -#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_ref((uv_handle_t *)&ODBC::g_async); -#else - uv_ref(uv_default_loop()); -#endif - } - - TryCatch try_catch; - - open_req->dbo->Unref(); - open_req->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - - open_req->cb.Dispose(); - - free(open_req); - free(req); - scope.Close(Undefined()); -} - -void ODBC::UV_Open(uv_work_t* req) { - DEBUG_PRINTF("ODBC::UV_Open\n"); - open_request* open_req = (open_request *)(req->data); - ODBC* self = open_req->dbo->self(); - - uv_mutex_lock(&ODBC::g_odbcMutex); - - int ret = SQLAllocEnv( &self->m_hEnv ); - - if( ret == SQL_SUCCESS ) { - ret = SQLAllocConnect( self->m_hEnv,&self->m_hDBC ); - - if( ret == SQL_SUCCESS ) { - SQLSetConnectOption( self->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); - - char connstr[1024]; - - //Attempt to connect - ret = SQLDriverConnect( self->m_hDBC, - NULL, - (SQLCHAR*) open_req->connection, - strlen(open_req->connection), - (SQLCHAR*) connstr, - 1024, - NULL, - SQL_DRIVER_NOPROMPT); - - if( ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ) { - HSTMT hStmt; - - ret = SQLAllocStmt( self->m_hDBC, &hStmt ); - - ret = SQLGetFunctions( self->m_hDBC, - SQL_API_SQLMORERESULTS, - &self->canHaveMoreResults); - - if ( !SQL_SUCCEEDED(ret)) { - self->canHaveMoreResults = 0; - } - - ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt); - } - } - } - - uv_mutex_unlock(&ODBC::g_odbcMutex); - open_req->result = ret; -} - -Handle ODBC::Open(const Arguments& args) { - DEBUG_PRINTF("ODBC::Open\n"); - HandleScope scope; - - REQ_STR_ARG(0, connection); - REQ_FUN_ARG(1, cb); - - ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); - uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - open_request* open_req = (open_request *) calloc(1, sizeof(open_request) + connection.length()); - - if (!open_req) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); - } - - strcpy(open_req->connection, *connection); - open_req->cb = Persistent::New(cb); - open_req->dbo = dbo; - - work_req->data = open_req; - - uv_queue_work(uv_default_loop(), work_req, UV_Open, (uv_after_work_cb)UV_AfterOpen); - - dbo->Ref(); - - return scope.Close(args.Holder()); -} - -void ODBC::UV_AfterClose(uv_work_t* req, int status) { - DEBUG_PRINTF("ODBC::UV_AfterClose\n"); - HandleScope scope; - - close_request* close_req = (close_request *)(req->data); - - ODBC* dbo = close_req->dbo; - - Local argv[1]; - bool err = false; - - if (close_req->result) { - err = true; - argv[0] = Exception::Error(String::New("Error closing database")); - } - else { - dbo->connected = false; - - //only unref if the connection was closed -#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_unref((uv_handle_t *)&ODBC::g_async); -#else - uv_unref(uv_default_loop()); -#endif - } - - TryCatch try_catch; - - close_req->dbo->Unref(); - close_req->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - - close_req->cb.Dispose(); - - free(close_req); - free(req); - scope.Close(Undefined()); -} - -void ODBC::UV_Close(uv_work_t* req) { - DEBUG_PRINTF("ODBC::UV_Close\n"); - close_request* close_req = (close_request *)(req->data); - ODBC* dbo = close_req->dbo; - - dbo->Free(); -} - -Handle ODBC::Close(const Arguments& args) { - DEBUG_PRINTF("ODBC::Close\n"); - HandleScope scope; - - REQ_FUN_ARG(0, cb); - - ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); - uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - close_request* close_req = (close_request *) (calloc(1, sizeof(close_request))); - - if (!close_req) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); - } - - close_req->cb = Persistent::New(cb); - close_req->dbo = dbo; - - work_req->data = close_req; - - uv_queue_work(uv_default_loop(), work_req, UV_Close, (uv_after_work_cb)UV_AfterClose); - - dbo->Ref(); - - return scope.Close(Undefined()); -} +/* void ODBC::UV_AfterQuery(uv_work_t* req, int status) { query_request* prep_req = (query_request *)(req->data); From ebb18884aeaa1a5b7fe82bb489329775a9bc1a7c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 14:40:53 -0400 Subject: [PATCH 126/511] add new static methods to odbc.cpp --- src/odbc.cpp | 142 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 13 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index d71013af..c9ad5144 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -1079,29 +1079,145 @@ Handle ODBC::CallbackSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, Persistent cb) { + HandleScope scope; + + Local objError = ODBC::GetSQLError( + hENV, + hDBC, + hSTMT, + (char *) "[node-odbc] Error in some module" + ); + + Local args[1]; + args[0] = objError; + cb->Call(Context::GetCurrent()->Global(), 1, args); + + return scope.Close(Undefined()); +} +Local ODBC::GetSQLError (HENV hENV, + HDBC hDBC, + HSTMT hSTMT, + char* message) { + HandleScope scope; + Local objError = Object::New(); char errorMessage[512]; char errorSQLState[128]; - SQLError( hENV, - hDBC, - hSTMT, - (SQLCHAR *) errorSQLState, - NULL, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - NULL); + SQLError( + hENV, + hDBC, + hSTMT, + (SQLCHAR *) errorSQLState, + NULL, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + NULL); objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), String::New("[node-odbc] Error in " - "__Make This Dynamic__")); + objError->Set(String::New("error"), String::New(message)); objError->Set(String::New("message"), String::New(errorMessage)); - Local args[1]; - args[0] = objError; - cb->Call(Context::GetCurrent()->Global(), 1, args); + return scope.Close(objError); +} + +Local ODBC::GetSQLDiagRecError (HDBC hDBC) { + HandleScope scope; + + Local objError = Object::New(); + + SQLINTEGER i = 0; + SQLINTEGER native; + SQLSMALLINT len; + SQLRETURN ret; + char errorSQLState[7]; + char errorMessage[256]; + + do { + ret = SQLGetDiagRec( + SQL_HANDLE_DBC, + hDBC, + ++i, + (SQLCHAR *) errorSQLState, + &native, + (SQLCHAR *) errorMessage, + sizeof(errorMessage), + &len); + + if (SQL_SUCCEEDED(ret)) { + objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); + objError->Set(String::New("message"), String::New(errorMessage)); + objError->Set(String::New("state"), String::New(errorSQLState)); + } + } while( ret == SQL_SUCCESS ); + + return scope.Close(objError); +} + + +Local ODBC::GetAllRecordsSync (HENV hENV, + HDBC hDBC, + HSTMT hSTMT, + uint16_t* buffer, + int bufferLength) { + DEBUG_PRINTF("ODBC::GetAllRecordsSync\n"); + + HandleScope scope; + + Local objError = Object::New(); + + int count = 0; + int errorCount = 0; + short colCount = 0; + + Column* columns = GetColumns(hSTMT, &colCount); + + Local rows = Array::New(); + + //loop through all records + while (true) { + SQLRETURN ret = SQLFetch(hSTMT); + + //check to see if there was an error + if (ret == SQL_ERROR) { + //TODO: what do we do when we actually get an error here... + //should we throw?? + + errorCount++; + + objError = ODBC::GetSQLError( + hENV, + hDBC, + hSTMT, + (char *) "[node-odbc] Error in ODBC::GetAllRecordsSync" + ); + + break; + } + + //check to see if we are at the end of the recordset + if (ret == SQL_NO_DATA) { + ODBC::FreeColumns(columns, &colCount); + + break; + } + + rows->Set( + Integer::New(count), + ODBC::GetRecordTuple( + hSTMT, + columns, + &colCount, + buffer, + bufferLength) + ); + + count++; + } + //TODO: what do we do about errors!?! + scope.Close(rows); } extern "C" void init (v8::Handle target) { From a48a15fe68b441d336e4f8a5e832ce8b5be4eebb Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 14:41:21 -0400 Subject: [PATCH 127/511] add new static methods to odbc.h --- src/odbc.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/odbc.h b/src/odbc.h index 8bc7d3f4..188968d5 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -65,6 +65,9 @@ class ODBC : public node::ObjectWrap { static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle CallbackSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, Persistent cb); + static Local GetSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, char* message); + static Local GetSQLDiagRecError (HDBC hDBC); + static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); static Parameter* GetParametersFromArray (Local values, int* paramCount); From 92cd38069d43880e8ad588749279e8ccdfbbede1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 14:44:09 -0400 Subject: [PATCH 128/511] Add ODBCResult::FetchAllSync() and make ODBCResult::FetchAll() actually async --- src/odbc_result.cpp | 166 +++++++++++++++++++++++++++++++------------- src/odbc_result.h | 6 ++ 2 files changed, 124 insertions(+), 48 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 3d43a2a0..f65fd4c1 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -51,6 +51,7 @@ void ODBCResult::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "moreResultsSync", MoreResultsSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAllSync", FetchAllSync); // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCResult"), @@ -268,6 +269,11 @@ Handle ODBCResult::FetchAll(const Arguments& args) { cb = Local::Cast(args[0]); + data->rows = Persistent::New(Array::New()); + data->errorCount = 0; + data->count = 0; + data->objError = Persistent::New(Object::New()); + data->cb = Persistent::New(cb); data->objResult = objODBCResult; @@ -285,26 +291,11 @@ Handle ODBCResult::FetchAll(const Arguments& args) { void ODBCResult::UV_FetchAll(uv_work_t* work_req) { DEBUG_PRINTF("ODBCResult::UV_FetchAll\n"); - //fetch_work_data* data = (fetch_work_data *)(work_req->data); - - /* - * NOTE: Terriblness resides here: - * - * FetchAll is not actually asynchronous at this time. In order to truly do this - * we would need to allocate memory here and load the entire recordset into - * some structure so that we can then loop over that structure in UV_AfterFetchAll - * - * One reason that this is the case is that somewhere I read that you should - * not do anything with V8 data structure while you are in the thread pool. - * If that is not true, then we could just do all of UV_AfterFetchAll right here - * while we are in the thread pool. - * - * For true async behaviour, use Fetch; one at a time. - * - */ - - //data->result = SQLFetch(data->objResult->m_hSTMT); -} + + fetch_work_data* data = (fetch_work_data *)(work_req->data); + + data->result = SQLFetch(data->objResult->m_hSTMT); + } void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll\n"); @@ -315,8 +306,96 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { 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 there was an error + if (data->result == SQL_ERROR) { + data->errorCount++; + + data->objError = Persistent::New(ODBC::GetSQLError( + self->m_hENV, + self->m_hDBC, + 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 { + + 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 args[2]; + //TODO: we need to return the error object if there was an error + //however, on queries like "Drop...." the ret from SQLFetch is + //SQL_ERROR, but there is not valid error message. I guess it's because + //there is acually no result set... + + //if (self->errorCount > 0) { + // args[0] = objError; + //} + //else { + args[0] = Null(); + //} + args[1] = data->rows; + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb.Dispose(); + + //TODO: Do we need to free self->rows somehow? + free(data); + free(work_req); + + self->Unref(); + } + + scope.Close(Undefined()); +} + +/* + * FetchAllSync + */ + +Handle ODBCResult::FetchAllSync(const Arguments& args) { + DEBUG_PRINTF("ODBCResult::FetchAllSync\n"); + + HandleScope scope; + + ODBCResult* self = ObjectWrap::Unwrap(args.Holder()); + Local objError = Object::New(); + SQLRETURN ret; int count = 0; int errorCount = 0; @@ -328,28 +407,18 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { //loop through all records while (true) { - SQLRETURN ret = SQLFetch(self->m_hSTMT); + ret = SQLFetch(self->m_hSTMT); //check to see if there was an error if (ret == SQL_ERROR) { errorCount++; - char errorMessage[512]; - char errorSQLState[128]; - - SQLError( self->m_hENV, - self->m_hDBC, - self->m_hSTMT, - (SQLCHAR *) errorSQLState, - NULL, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - NULL); - - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), - String::New("[node-odbc] Error in SQLFetch")); - objError->Set(String::New("message"), String::New(errorMessage)); + objError = ODBC::GetSQLError( + self->m_hENV, + self->m_hDBC, + self->m_hSTMT, + (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll" + ); break; } @@ -374,17 +443,18 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { count++; } - Handle args[2]; - args[0] = Null(); - args[1] = rows; - - data->cb->Call(Context::GetCurrent()->Global(), 2, args); - data->cb.Dispose(); - - free(data); - free(work_req); - - self->Unref(); + //TODO: we need to return the error object if there was an error + //however, on queries like "Drop...." the ret from SQLFetch is + //SQL_ERROR, but there is not valid error message. I guess it's because + //there is acually no result set... + // + //we will need to throw if there is a valid error. + //if (errorCount > 0) { + // //THROW! + // args[0] = objError; + //} + + return scope.Close(rows); } Handle ODBCResult::CloseSync(const Arguments& args) { diff --git a/src/odbc_result.h b/src/odbc_result.h index 4c0d7bd6..9007f39d 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -50,11 +50,17 @@ class ODBCResult : public node::ObjectWrap { //sync methods static Handle CloseSync(const Arguments& args); static Handle MoreResultsSync(const Arguments& args); + static Handle FetchAllSync(const Arguments& args); struct fetch_work_data { Persistent cb; ODBCResult *objResult; SQLRETURN result; + + int count; + int errorCount; + Persistent rows; + Persistent objError; }; ODBCResult *self(void) { return this; } From eb3b00453d6f844b488d982a6b4a4c0716c2fd73 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 14:45:00 -0400 Subject: [PATCH 129/511] Use ODBC::GetSQLDiagRecError() instead of doing it manually --- src/odbc_connection.cpp | 45 ++++++++++++----------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 5150b7fa..388804d2 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -202,6 +202,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterOpen\n"); HandleScope scope; + open_connection_work_data* data = (open_connection_work_data *)(req->data); Local argv[1]; @@ -211,34 +212,9 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { if (data->result) { err = true; - SQLINTEGER i = 0; - SQLINTEGER native; - SQLSMALLINT len; - SQLRETURN ret; - char errorSQLState[7]; - char errorMessage[256]; - - do { - ret = SQLGetDiagRec( - SQL_HANDLE_DBC, - data->conn->self()->m_hDBC, - ++i, - (SQLCHAR *) errorSQLState, - &native, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - &len); - - if (SQL_SUCCEEDED(ret)) { - Local objError = Object::New(); - - objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); - objError->Set(String::New("message"), String::New(errorMessage)); - objError->Set(String::New("state"), String::New(errorSQLState)); - - argv[0] = objError; - } - } while( ret == SQL_SUCCESS ); + Local objError = ODBC::GetSQLDiagRecError(data->conn->self()->m_hDBC); + + argv[0] = objError; } if (!err) { @@ -412,7 +388,11 @@ Handle ODBCConnection::CreateStatement(const Arguments& args) { work_req->data = data; - uv_queue_work(uv_default_loop(), work_req, UV_CreateStatement, (uv_after_work_cb)UV_AfterCreateStatement); + uv_queue_work( + uv_default_loop(), + work_req, + UV_CreateStatement, + (uv_after_work_cb)UV_AfterCreateStatement); conn->Ref(); @@ -586,9 +566,10 @@ void ODBCConnection::UV_Query(uv_work_t* req) { for (int i = 0; i < data->paramCount; i++) { prm = data->params[i]; - DEBUG_PRINTF("ODBCConnection::UV_Query - param[%i]: c_type=%i type=%i " - "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, - prm.buffer_length, prm.size, prm.length, &data->params[i].length); + DEBUG_PRINTF( + "ODBCConnection::UV_Query - param[%i]: c_type=%i type=%i " + "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, + prm.buffer_length, prm.size, prm.length, &data->params[i].length); ret = SQLBindParameter( data->hSTMT, //StatementHandle From 03a28510892d095e77a50abe1406582799dc0d42 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 14:45:14 -0400 Subject: [PATCH 130/511] todo --- src/odbc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index c9ad5144..3f731798 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -764,6 +764,7 @@ Column* ODBC::GetColumns(SQLHSTMT hStmt, short* colCount) { for (int i = 0; i < *colCount; i++) { //save the index number of this column columns[i].index = i + 1; + //TODO:that's a lot of memory for each field name.... columns[i].name = new unsigned char[MAX_FIELD_SIZE]; //set the first byte of name to \0 instead of memsetting the entire buffer From e3c7ec8aea46c27b63e9da2916ec2f8cc69a415e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 15:20:14 -0400 Subject: [PATCH 131/511] add benchmark for sync queries --- test/bench-querySync.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/bench-querySync.js diff --git a/test/bench-querySync.js b/test/bench-querySync.js new file mode 100644 index 00000000..2d4dc4c0 --- /dev/null +++ b/test/bench-querySync.js @@ -0,0 +1,31 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database(); + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , iterations = 10000 + , time = new Date().getTime(); + + for (var x = 0; x < iterations; x++) { + var data = db.querySync("select 1 + 1 as test"); + count += 1; + } + + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + + db.close(function () { + console.log("connection closed"); + }); +} \ No newline at end of file From d16904ad3d99fd2eac6c61a8b1dbc37f077f9e45 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 15:21:08 -0400 Subject: [PATCH 132/511] add ODBCConnection::QuerySyng --- odbc.js | 20 ++++++ src/odbc_connection.cpp | 131 ++++++++++++++++++++++++++++++++++++++++ src/odbc_connection.h | 1 + 3 files changed, 152 insertions(+) diff --git a/odbc.js b/odbc.js index 56ac16bd..5640f762 100644 --- a/odbc.js +++ b/odbc.js @@ -146,6 +146,26 @@ Database.prototype.queryResult = function (sql, params, cb) { }); }; +Database.prototype.querySync = function (sql, params) { + var self = this; + + if (!params) { + params = []; + } + + if (!self.connected) { + throw ({ message : "Connection not open."}); + } + + var result = self.conn.querySync(sql, params); + + var data = result.fetchAllSync(); + + result.closeSync(); + + return data; +}; + function SimpleQueue() { var self = this; diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 388804d2..12ffa90e 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -56,6 +56,7 @@ void ODBCConnection::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatement", CreateStatement); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatementSync", CreateStatementSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", Query); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "querySync", QuerySync); // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCConnection"), @@ -656,4 +657,134 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { } +/* + * QuerySync + */ + +Handle ODBCConnection::QuerySync(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::QuerySync\n"); + + HandleScope scope; + REQ_STR_ARG(0, sql); + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + Local objError = Object::New(); + Parameter* params; + Parameter prm; + SQLRETURN ret; + HSTMT hSTMT; + int paramCount = 0; + char* sqlString; + + // populate params if parameters were supplied + if (args.Length() > 1) { + if ( !args[1]->IsArray() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 1 must be an Array")) + ); + } + + params = ODBC::GetParametersFromArray( + Local::Cast(args[1]), + ¶mCount); + } + + sqlString = (char *) malloc(sql.length() +1); + + strcpy(sqlString, *sql); + + uv_mutex_lock(&ODBC::g_odbcMutex); + + //allocate a new statment handle + SQLAllocHandle( SQL_HANDLE_STMT, + conn->m_hDBC, + &hSTMT ); + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + //check to see if should excute a direct or a parameter bound query + if (!paramCount) { + // execute the query directly + ret = SQLExecDirect( + hSTMT, + (SQLCHAR *) sqlString, + strlen(sqlString)); + } + else { + // prepare statement, bind parameters and execute statement + ret = SQLPrepare( + hSTMT, + (SQLCHAR *) sqlString, + strlen(sqlString)); + + if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { + for (int i = 0; i < paramCount; i++) { + prm = params[i]; + + DEBUG_PRINTF( + "ODBCConnection::UV_Query - param[%i]: c_type=%i type=%i " + "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, + prm.buffer_length, prm.size, prm.length, ¶ms[i].length); + + ret = SQLBindParameter( + hSTMT, //StatementHandle + i + 1, //ParameterNumber + SQL_PARAM_INPUT, //InputOutputType + prm.c_type, //ValueType + prm.type, //ParameterType + prm.size, //ColumnSize + 0, //DecimalDigits + prm.buffer, //ParameterValuePtr + prm.buffer_length, //BufferLength + //using &prm.length did not work here... + ¶ms[i].length); //StrLen_or_IndPtr + + if (ret == SQL_ERROR) {break;} + } + + if (SQL_SUCCEEDED(ret)) { + ret = SQLExecute(hSTMT); + } + } + + // free parameters + for (int i = 0; i < paramCount; i++) { + if (prm = params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_LONG: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + free(params); + } + + //check to see if there was an error during execution + if(ret == SQL_ERROR) { + objError = ODBC::GetSQLError( + conn->m_hENV, + conn->m_hDBC, + hSTMT, + (char *) "[node-odbc] Error in ODBCConnection::QuerySync" + ); + + ThrowException(Exception::Error(objError->Get(String::New("error"))->ToString())); + + return scope.Close(Undefined()); + } + else { + Local args[3]; + args[0] = External::New(conn->m_hENV); + args[1] = External::New(conn->m_hDBC); + args[2] = External::New(hSTMT); + Persistent js_result(ODBCResult::constructor_template-> + GetFunction()->NewInstance(3, args)); + + return scope.Close(js_result); + } +} \ No newline at end of file diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 8ec91731..17f1c21a 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -68,6 +68,7 @@ class ODBCConnection : public node::ObjectWrap { //sync methods static Handle CreateStatementSync(const Arguments& args); + static Handle QuerySync(const Arguments& args); struct Fetch_Request { Persistent callback; From c179901cfca241efc10ef014049bcc05675d2da1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 15:26:08 -0400 Subject: [PATCH 133/511] test: bad connection string; this test will probably fail on windows. --- test/test-bad-connection-string.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/test-bad-connection-string.js diff --git a/test/test-bad-connection-string.js b/test/test-bad-connection-string.js new file mode 100644 index 00000000..49e83637 --- /dev/null +++ b/test/test-bad-connection-string.js @@ -0,0 +1,16 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; + + +db.open("this is wrong", function(err) { + assert.deepEqual(err, { + error: '[node-odbc] SQL_ERROR', + message: '[unixODBC][Driver Manager]Data source name not found, and no default driver specified', + state: 'IM002' + }); + + assert.equal(db.connected, false); +}); From 6993392da986f4ec4b3d88bbd8a14c96415844ff Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 15:32:24 -0400 Subject: [PATCH 134/511] test: add test-querySync-select --- test/test-querySync-select.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/test-querySync-select.js diff --git a/test/test-querySync-select.js b/test/test-querySync-select.js new file mode 100644 index 00000000..c4cf00df --- /dev/null +++ b/test/test-querySync-select.js @@ -0,0 +1,16 @@ +var common = require("./common") + , odbc = require("../odbc.js") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + var data = db.querySync("select 1 as COLINT, 'some test' as COLTEXT "); + + db.close(function () { + assert.deepEqual(data, [{ COLINT: '1', COLTEXT: 'some test' }]); + }); +}); From d933a1adcb5fae064e04d7dff6a9cff75257a289 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:08:13 -0400 Subject: [PATCH 135/511] re-introduce describe(); move SimpleQueue to external module --- odbc.js | 99 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/odbc.js b/odbc.js index 5640f762..90482d45 100644 --- a/odbc.js +++ b/odbc.js @@ -16,6 +16,7 @@ */ var odbc = require("bindings")("odbc_bindings") + , SimpleQueue = require("./simple-queue") ; module.exports = function (options) { @@ -67,10 +68,13 @@ Database.prototype.open = function (connectionString, cb) { Database.prototype.close = function (cb) { var self = this; - self.conn.close(function (err) { - self.connected = false; - - return cb(err); + self.queue.push(function (next) { + self.conn.close(function (err) { + self.connected = false; + + cb(err); + return next(); + }); }); }; @@ -166,34 +170,89 @@ Database.prototype.querySync = function (sql, params) { return data; }; -function SimpleQueue() { +Database.prototype.columns = function(catalog, schema, table, column, callback) { var self = this; + if (!self.queue) self.queue = []; - self.fifo = []; - self.executing = false; -} + callback = callback || arguments[arguments.length - 1]; + + self.queue.push(function (next) { + self.conn.columns(catalog, schema, table, column, function (err, result) { + if (err) return callback(err, [], false); + + result.fetchAll(function (err, data) { + callback(err, data); + + return next(); + }); + }); + }); +}; -SimpleQueue.prototype.push = function (fn) { +Database.prototype.tables = function(catalog, schema, table, type, callback) { var self = this; + if (!self.queue) self.queue = []; - self.fifo.push(fn); + callback = callback || arguments[arguments.length - 1]; - self.maybeNext(); + self.queue.push(function (next) { + self.conn.tables(catalog, schema, table, type, function (err, result) { + if (err) return callback(err, [], false); + + result.fetchAll(function (err, data) { + callback(err, data); + + return next(); + }); + }); + }); }; -SimpleQueue.prototype.maybeNext = function () { +Database.prototype.describe = function(obj, callback) { var self = this; - if (!self.executing && self.fifo.length) { - var fn = self.fifo.shift(); + if (typeof(callback) != "function") { + throw({ + error : "[node-odbc] Missing Arguments", + message : "You must specify a callback function in order for the describe method to work." + }); - self.executing = true; + return false; + } + + if (typeof(obj) != "object") { + callback({ + error : "[node-odbc] Missing Arguments", + message : "You must pass an object as argument 0 if you want anything productive to happen in the describe method." + }, []); - fn(function () { - self.executing = false; - - self.maybeNext(); - }); + return false; + } + + if (!obj.database) { + callback({ + error : "[node-odbc] Missing Arguments", + message : "The object you passed did not contain a database property. This is required for the describe method to work." + }, []); + + return false; + } + + //set some defaults if they weren't passed + obj.schema = obj.schema || "%"; + obj.type = obj.type || "table"; + + if (obj.table && obj.column) { + //get the column details + self.columns(obj.database, obj.schema, obj.table, obj.column, callback); + } + else if (obj.table) { + //get the columns in the table + self.columns(obj.database, obj.schema, obj.table, "%", callback); + } + else { + //get the tables in the database + self.tables(obj.database, obj.schema, null, obj.type || "table", callback); } }; From ec3d33259133b9f2bad153c1e4beffa43ac29ff9 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:08:41 -0400 Subject: [PATCH 136/511] add simple-query --- lib/simple-query.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 lib/simple-query.js diff --git a/lib/simple-query.js b/lib/simple-query.js new file mode 100644 index 00000000..df51f07f --- /dev/null +++ b/lib/simple-query.js @@ -0,0 +1,32 @@ +module.exports = SimpleQueue; + +function SimpleQueue() { + var self = this; + + self.fifo = []; + self.executing = false; +} + +SimpleQueue.prototype.push = function (fn) { + var self = this; + + self.fifo.push(fn); + + self.maybeNext(); +}; + +SimpleQueue.prototype.maybeNext = function () { + var self = this; + + if (!self.executing && self.fifo.length) { + var fn = self.fifo.shift(); + + self.executing = true; + + fn(function () { + self.executing = false; + + self.maybeNext(); + }); + } +}; \ No newline at end of file From ca0c52882d1ff0afcf0abe0d81be74f40d8bdc19 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:08:51 -0400 Subject: [PATCH 137/511] move odbc.js --- odbc.js => lib/odbc.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename odbc.js => lib/odbc.js (100%) diff --git a/odbc.js b/lib/odbc.js similarity index 100% rename from odbc.js rename to lib/odbc.js From 06a5788e3f1fe0e26e046d7ea1d450f89f842ae0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:11:24 -0400 Subject: [PATCH 138/511] rename file. --- lib/{simple-query.js => simple-queue.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/{simple-query.js => simple-queue.js} (100%) diff --git a/lib/simple-query.js b/lib/simple-queue.js similarity index 100% rename from lib/simple-query.js rename to lib/simple-queue.js From 38a394ea5bc874f71a8a189791c94e6cd77b9f2f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:11:56 -0400 Subject: [PATCH 139/511] moved odbc to lib --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dda49f8b..7c729c7c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "odbc", "description": "unixodbc bindings for node", "version": "0.5.1", - "main": "odbc.js", + "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { "type": "git", From 8862955dc31cd9de3d9499468727b806100ae492 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:12:38 -0400 Subject: [PATCH 140/511] ODBCConnection: add back Table and Column() --- src/odbc_connection.cpp | 189 +++++++++++++++++++++++++++++++++++++++- src/odbc_connection.h | 16 +++- 2 files changed, 202 insertions(+), 3 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 12ffa90e..7b858350 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -58,6 +58,9 @@ void ODBCConnection::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", Query); NODE_SET_PROTOTYPE_METHOD(constructor_template, "querySync", QuerySync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "columns", Columns); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "tables", Tables); + // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCConnection"), constructor_template->GetFunction()); @@ -649,7 +652,13 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { } data->cb.Dispose(); + free(data->sql); + free(data->catalog); + free(data->schema); + free(data->table); + free(data->type); + free(data->column); free(data); free(req); @@ -787,4 +796,182 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { return scope.Close(js_result); } -} \ No newline at end of file +} + +/* + * Tables + */ + +Handle ODBCConnection::Tables(const Arguments& args) { + HandleScope scope; + + REQ_STR_OR_NULL_ARG(0, catalog); + REQ_STR_OR_NULL_ARG(1, schema); + REQ_STR_OR_NULL_ARG(2, table); + REQ_STR_OR_NULL_ARG(3, type); + Local cb = Local::Cast(args[4]); + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + query_work_data* data = + (query_work_data *) calloc(1, sizeof(query_work_data)); + + if (!data) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + } + + data->sql = NULL; + data->catalog = NULL; + data->schema = NULL; + data->table = NULL; + data->type = NULL; + data->column = NULL; + data->cb = Persistent::New(cb); + + if (!String::New(*catalog)->Equals(String::New("null"))) { + data->catalog = (char *) malloc(catalog.length() +1); + strcpy(data->catalog, *catalog); + } + + if (!String::New(*schema)->Equals(String::New("null"))) { + data->schema = (char *) malloc(schema.length() +1); + strcpy(data->schema, *schema); + } + + if (!String::New(*table)->Equals(String::New("null"))) { + data->table = (char *) malloc(table.length() +1); + strcpy(data->table, *table); + } + + if (!String::New(*type)->Equals(String::New("null"))) { + data->type = (char *) malloc(type.length() +1); + strcpy(data->type, *type); + } + + data->conn = conn; + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_Tables, + (uv_after_work_cb) UV_AfterQuery); + + conn->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCConnection::UV_Tables(uv_work_t* req) { + query_work_data* data = (query_work_data *)(req->data); + + uv_mutex_lock(&ODBC::g_odbcMutex); + + SQLAllocStmt(data->conn->m_hDBC, &data->hSTMT ); + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + SQLRETURN ret = SQLTables( + data->hSTMT, + (SQLCHAR *) data->catalog, SQL_NTS, + (SQLCHAR *) data->schema, SQL_NTS, + (SQLCHAR *) data->table, SQL_NTS, + (SQLCHAR *) data->type, SQL_NTS + ); + + // this will be checked later in UV_AfterQuery + data->result = ret; +} + + + +/* + * Columns + */ + +Handle ODBCConnection::Columns(const Arguments& args) { + HandleScope scope; + + REQ_STR_OR_NULL_ARG(0, catalog); + REQ_STR_OR_NULL_ARG(1, schema); + REQ_STR_OR_NULL_ARG(2, table); + REQ_STR_OR_NULL_ARG(3, column); + + Local cb = Local::Cast(args[4]); + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + query_work_data* data = (query_work_data *) calloc(1, sizeof(query_work_data)); + + if (!data) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + } + + data->sql = NULL; + data->catalog = NULL; + data->schema = NULL; + data->table = NULL; + data->type = NULL; + data->column = NULL; + data->cb = Persistent::New(cb); + + if (!String::New(*catalog)->Equals(String::New("null"))) { + data->catalog = (char *) malloc(catalog.length() +1); + strcpy(data->catalog, *catalog); + } + + if (!String::New(*schema)->Equals(String::New("null"))) { + data->schema = (char *) malloc(schema.length() +1); + strcpy(data->schema, *schema); + } + + if (!String::New(*table)->Equals(String::New("null"))) { + data->table = (char *) malloc(table.length() +1); + strcpy(data->table, *table); + } + + if (!String::New(*column)->Equals(String::New("null"))) { + data->column = (char *) malloc(column.length() +1); + strcpy(data->column, *column); + } + + data->conn = conn; + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_Columns, + (uv_after_work_cb)UV_AfterQuery); + + conn->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCConnection::UV_Columns(uv_work_t* req) { + query_work_data* data = (query_work_data *)(req->data); + + uv_mutex_lock(&ODBC::g_odbcMutex); + + SQLAllocStmt(data->conn->m_hDBC, &data->hSTMT ); + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + SQLRETURN ret = SQLColumns( + data->hSTMT, + (SQLCHAR *) data->catalog, SQL_NTS, + (SQLCHAR *) data->schema, SQL_NTS, + (SQLCHAR *) data->table, SQL_NTS, + (SQLCHAR *) data->column, SQL_NTS + ); + + // this will be checked later in UV_AfterQuery + data->result = ret; +} diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 17f1c21a..1176be46 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -66,6 +66,12 @@ class ODBCConnection : public node::ObjectWrap { static void UV_Query(uv_work_t* req); static void UV_AfterQuery(uv_work_t* req, int status); + static Handle Columns(const Arguments& args); + static void UV_Columns(uv_work_t* req); + + static Handle Tables(const Arguments& args); + static void UV_Tables(uv_work_t* req); + //sync methods static Handle CreateStatementSync(const Arguments& args); static Handle QuerySync(const Arguments& args); @@ -97,9 +103,17 @@ struct query_work_data { Persistent cb; ODBCConnection *conn; HSTMT hSTMT; + Parameter *params; int paramCount; + char *sql; + char *catalog; + char *schema; + char *table; + char *type; + char *column; + int result; }; @@ -116,6 +130,4 @@ struct close_connection_work_data { int result; }; - - #endif From ab6b3341df28dfda8a01370116be4b6335db8aa6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:12:54 -0400 Subject: [PATCH 141/511] throw exception --- src/odbc_result.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index f65fd4c1..f989eec9 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -449,11 +449,10 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { //there is acually no result set... // //we will need to throw if there is a valid error. - //if (errorCount > 0) { - // //THROW! - // args[0] = objError; - //} - + if (errorCount > 0) { + ThrowException(Exception::Error(objError->Get(String::New("error"))->ToString())); + } + return scope.Close(rows); } From 3be6b58aa46088049afc51429fb7bc2c8f5cbd69 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:13:12 -0400 Subject: [PATCH 142/511] remove comments --- src/odbc_statement.cpp | 150 ----------------------------------------- 1 file changed, 150 deletions(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 40891e1d..a0bbc724 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -699,153 +699,3 @@ Handle ODBCStatement::CloseSync(const Arguments& args) { return scope.Close(True()); } - -/* -void ODBC::UV_Tables(uv_work_t* req) { - query_request* prep_req = (query_request *)(req->data); - - uv_mutex_lock(&ODBC::g_odbcMutex); - SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->hSTMT ); - uv_mutex_unlock(&ODBC::g_odbcMutex); - - SQLRETURN ret = SQLTables( - prep_req->hSTMT, - (SQLCHAR *) prep_req->catalog, SQL_NTS, - (SQLCHAR *) prep_req->schema, SQL_NTS, - (SQLCHAR *) prep_req->table, SQL_NTS, - (SQLCHAR *) prep_req->type, SQL_NTS - ); - - // this will be checked later in UV_AfterQuery - prep_req->result = ret; -} - -Handle ODBC::Tables(const Arguments& args) { - HandleScope scope; - - REQ_STR_OR_NULL_ARG(0, catalog); - REQ_STR_OR_NULL_ARG(1, schema); - REQ_STR_OR_NULL_ARG(2, table); - REQ_STR_OR_NULL_ARG(3, type); - Local cb = Local::Cast(args[4]); - - ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); - uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); - - if (!prep_req) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); - } - - prep_req->sql = NULL; - prep_req->catalog = NULL; - prep_req->schema = NULL; - prep_req->table = NULL; - prep_req->type = NULL; - prep_req->column = NULL; - prep_req->cb = Persistent::New(cb); - - if (!String::New(*catalog)->Equals(String::New("null"))) { - prep_req->catalog = (char *) malloc(catalog.length() +1); - strcpy(prep_req->catalog, *catalog); - } - - if (!String::New(*schema)->Equals(String::New("null"))) { - prep_req->schema = (char *) malloc(schema.length() +1); - strcpy(prep_req->schema, *schema); - } - - if (!String::New(*table)->Equals(String::New("null"))) { - prep_req->table = (char *) malloc(table.length() +1); - strcpy(prep_req->table, *table); - } - - if (!String::New(*type)->Equals(String::New("null"))) { - prep_req->type = (char *) malloc(type.length() +1); - strcpy(prep_req->type, *type); - } - - prep_req->dbo = dbo; - work_req->data = prep_req; - - uv_queue_work(uv_default_loop(), work_req, UV_Tables, (uv_after_work_cb)UV_AfterQueryAll); - - dbo->Ref(); - - return scope.Close(Undefined()); -} - -void ODBC::UV_Columns(uv_work_t* req) { - query_request* prep_req = (query_request *)(req->data); - - SQLAllocStmt(prep_req->dbo->m_hDBC,&prep_req->hSTMT ); - - SQLRETURN ret = SQLColumns( - prep_req->hSTMT, - (SQLCHAR *) prep_req->catalog, SQL_NTS, - (SQLCHAR *) prep_req->schema, SQL_NTS, - (SQLCHAR *) prep_req->table, SQL_NTS, - (SQLCHAR *) prep_req->column, SQL_NTS - ); - - // this will be checked later in UV_AfterQuery - prep_req->result = ret; -} - -Handle ODBC::Columns(const Arguments& args) { - HandleScope scope; - - REQ_STR_OR_NULL_ARG(0, catalog); - REQ_STR_OR_NULL_ARG(1, schema); - REQ_STR_OR_NULL_ARG(2, table); - REQ_STR_OR_NULL_ARG(3, column); - Local cb = Local::Cast(args[4]); - - ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); - uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); - - if (!prep_req) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); - } - - prep_req->sql = NULL; - prep_req->catalog = NULL; - prep_req->schema = NULL; - prep_req->table = NULL; - prep_req->type = NULL; - prep_req->column = NULL; - prep_req->cb = Persistent::New(cb); - - if (!String::New(*catalog)->Equals(String::New("null"))) { - prep_req->catalog = (char *) malloc(catalog.length() +1); - strcpy(prep_req->catalog, *catalog); - } - - if (!String::New(*schema)->Equals(String::New("null"))) { - prep_req->schema = (char *) malloc(schema.length() +1); - strcpy(prep_req->schema, *schema); - } - - if (!String::New(*table)->Equals(String::New("null"))) { - prep_req->table = (char *) malloc(table.length() +1); - strcpy(prep_req->table, *table); - } - - if (!String::New(*column)->Equals(String::New("null"))) { - prep_req->column = (char *) malloc(column.length() +1); - strcpy(prep_req->column, *column); - } - - prep_req->dbo = dbo; - work_req->data = prep_req; - - uv_queue_work(uv_default_loop(), work_req, UV_Columns, (uv_after_work_cb)UV_AfterQueryAll); - - dbo->Ref(); - - return scope.Close(Undefined()); -} -*/ \ No newline at end of file From d6a14a6d45edb3640bbc0e8c98c1aeaf6c60619d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:13:37 -0400 Subject: [PATCH 143/511] remove comments and add better ones --- src/odbc.cpp | 587 ++++----------------------------------------------- src/odbc.h | 28 --- 2 files changed, 37 insertions(+), 578 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 3f731798..53d547f8 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -123,7 +123,6 @@ void ODBC::WatcherCallback(uv_async_t *w, int revents) { /* * CreateConnection - * */ Handle ODBC::CreateConnection(const Arguments& args) { @@ -195,555 +194,8 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { } /* -void ODBC::UV_AfterQuery(uv_work_t* req, int status) { - query_request* prep_req = (query_request *)(req->data); - - HandleScope scope; - - //an easy reference to the Database object - ODBC* self = prep_req->dbo->self(); - - //used to capture the return value from various SQL function calls - SQLRETURN ret; - - //First thing, let's check if the execution of the query returned any errors - //in UV_Query - if(prep_req->result == SQL_ERROR) { - Local objError = Object::New(); - - char errorMessage[512]; - char errorSQLState[128]; - - SQLError( self->m_hEnv, - self->m_hDBC, - prep_req->hSTMT, - (SQLCHAR *) errorSQLState, - NULL, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - NULL); - - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), String::New("[node-odbc] Error in " - "SQLExecDirect or SQLExecute")); - objError->Set(String::New("message"), String::New(errorMessage)); - - //only set the query value of the object if we actually have a query - if (prep_req->sql != NULL) { - objError->Set(String::New("query"), String::New(prep_req->sql)); - } - - //emit an error event immidiately. - Local args[1]; - args[0] = objError; - prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); - } - else { - Local args[3]; - args[0] = External::New(self->m_hEnv); - args[1] = External::New(self->m_hDBC); - args[2] = External::New(prep_req->hSTMT); - Persistent js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(3, args)); - - args[0] = Local::New(Null()); - args[1] = Local::New(js_result); - - prep_req->cb->Call(Context::GetCurrent()->Global(), 2, args); - } - - TryCatch try_catch; - - self->Unref(); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - - prep_req->cb.Dispose(); - free(prep_req->sql); - free(prep_req->catalog); - free(prep_req->schema); - free(prep_req->table); - free(prep_req->type); - free(prep_req); - free(req); - - scope.Close(Undefined()); -} - -void ODBC::UV_Query(uv_work_t* req) { - query_request* prep_req = (query_request *)(req->data); - - Parameter prm; - SQLRETURN ret; - - uv_mutex_lock(&ODBC::g_odbcMutex); - - //allocate a new statment handle - SQLAllocHandle( SQL_HANDLE_STMT, - prep_req->dbo->m_hDBC, - &prep_req->hSTMT ); - - uv_mutex_unlock(&ODBC::g_odbcMutex); - - //check to see if should excute a direct or a parameter bound query - if (!prep_req->paramCount) { - // execute the query directly - ret = SQLExecDirect( prep_req->hSTMT, - (SQLCHAR *) prep_req->sql, - strlen(prep_req->sql)); - } - else { - // prepare statement, bind parameters and execute statement - ret = SQLPrepare( prep_req->hSTMT, - (SQLCHAR *) prep_req->sql, - strlen(prep_req->sql)); - - if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { - for (int i = 0; i < prep_req->paramCount; i++) { - prm = prep_req->params[i]; - - DEBUG_PRINTF("ODBC::UV_Query - param[%i]: c_type=%i type=%i " - "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, - prm.buffer_length, prm.size, prm.length, &prep_req->params[i].length); - - ret = SQLBindParameter( prep_req->hSTMT, //StatementHandle - i + 1, //ParameterNumber - SQL_PARAM_INPUT, //InputOutputType - prm.c_type, //ValueType - prm.type, //ParameterType - prm.size, //ColumnSize - 0, //DecimalDigits - prm.buffer, //ParameterValuePtr - prm.buffer_length, //BufferLength - //using &prm.length did not work here... - &prep_req->params[i].length); //StrLen_or_IndPtr - - if (ret == SQL_ERROR) {break;} - } - - if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { - ret = SQLExecute(prep_req->hSTMT); - } - } - - // free parameters - for (int i = 0; i < prep_req->paramCount; i++) { - if (prm = prep_req->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_LONG: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; - } - } - } - free(prep_req->params); - } - - // this will be checked later in UV_AfterQuery - prep_req->result = ret; -} - -Handle ODBC::Query(const Arguments& args) { - HandleScope scope; - - REQ_STR_ARG(0, sql); - - Local cb; - - ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); - uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); - - if (!prep_req) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); - } - - // populate prep_req->params if parameters were supplied - // - if (args.Length() > 2) { - if ( !args[1]->IsArray() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 1 must be an Array")) - ); - } - else if ( !args[2]->IsFunction() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 2 must be a Function")) - ); - } - - cb = Local::Cast(args[2]); - prep_req->params = GetParametersFromArray(Local::Cast(args[1]), &prep_req->paramCount); - } - else { - if ( !args[1]->IsFunction() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 1 must be a Function")) - ); - } - - cb = Local::Cast(args[1]); - - prep_req->paramCount = 0; - } - - prep_req->sql = (char *) malloc(sql.length() +1); - prep_req->catalog = NULL; - prep_req->schema = NULL; - prep_req->table = NULL; - prep_req->type = NULL; - prep_req->column = NULL; - prep_req->cb = Persistent::New(cb); - - strcpy(prep_req->sql, *sql); - - prep_req->dbo = dbo; - work_req->data = prep_req; - - uv_queue_work(uv_default_loop(), work_req, UV_Query, (uv_after_work_cb)UV_AfterQuery); - - dbo->Ref(); - - return scope.Close(Undefined()); -} - -void ODBC::UV_AfterQueryAll(uv_work_t* req, int status) { - query_request* prep_req = (query_request *)(req->data); - - HandleScope scope; - - //an easy reference to the Database object - ODBC* self = prep_req->dbo->self(); - - //our error object which we will use if we discover errors while processing the result set - Local objError; - - //used to keep track of the number of columns received in a result set - short colCount = 0; - - //used to keep track of the number of event emittions that have occurred - short emitCount = 0; - - //used to keep track of the number of errors that have been found - short errorCount = 0; - - //used to capture the return value from various SQL function calls - SQLRETURN ret; - - //allocate a buffer for incoming column values - uint16_t* buf = (uint16_t *) malloc(MAX_VALUE_SIZE); - - //check to make sure malloc succeeded - if (buf == NULL) { - objError = Object::New(); - - //malloc failed, set an error message - objError->Set(String::New("error"), String::New("[node-odbc] Failed Malloc")); - objError->Set(String::New("message"), String::New("An attempt to allocate memory failed. This allocation was for a value buffer of incoming recordset values.")); - - //emit an error event immidiately. - Local args[3]; - args[0] = objError; - args[1] = Local::New(Null()); - args[2] = Local::New(False()); - - //emit an error event - prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); - - //emit a result event - goto cleanupshutdown; - } - //else { - //malloc succeeded so let's continue - - //set the first byte of the buffer to \0 instead of memsetting the entire buffer to 0 - buf[0] = '\0'; - - //First thing, let's check if the execution of the query returned any errors (in UV_Query) - if(prep_req->result == SQL_ERROR) { - objError = Object::New(); - - errorCount++; - - char errorMessage[512]; - char errorSQLState[128]; - SQLError(self->m_hEnv, self->m_hDBC, prep_req->hSTMT,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); - objError->Set(String::New("message"), String::New(errorMessage)); - - //only set the query value of the object if we actually have a query - if (prep_req->sql != NULL) { - objError->Set(String::New("query"), String::New(prep_req->sql)); - } - - //emit an error event immidiately. - Local args[1]; - args[0] = objError; - prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); - goto cleanupshutdown; - } - - //loop through all result sets - do { - Local rows = Array::New(); - - // retrieve and store column attributes to build the row object - Column *columns = GetColumns(prep_req->hSTMT, &colCount); - - if (colCount > 0) { - int count = 0; - - // i dont think odbc will tell how many rows are returned, loop until out... - while(true) { - Local tuple = Object::New(); - ret = SQLFetch(prep_req->hSTMT); - - //TODO: Do something to enable/disable dumping these info messages to the console. - if (ret == SQL_SUCCESS_WITH_INFO ) { - char errorMessage[512]; - char errorSQLState[128]; - SQLError(self->m_hEnv, self->m_hDBC, prep_req->hSTMT,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); - - printf("UV_Query => %s\n", errorMessage); - printf("UV_Query => %s\n", errorSQLState); - } - - if (ret == SQL_ERROR) { - objError = Object::New(); - - char errorMessage[512]; - char errorSQLState[128]; - SQLError(self->m_hEnv, self->m_hDBC, prep_req->hSTMT,(SQLCHAR *)errorSQLState,NULL,(SQLCHAR *)errorMessage, sizeof(errorMessage), NULL); - - errorCount++; - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); - objError->Set(String::New("message"), String::New(errorMessage)); - objError->Set(String::New("query"), String::New(prep_req->sql)); - - //emit an error event immidiately. - Local args[1]; - args[0] = objError; - prep_req->cb->Call(Context::GetCurrent()->Global(), 1, args); - - break; - } - - if (ret == SQL_NO_DATA) { - break; - } - - for(int i = 0; i < colCount; i++) { - tuple->Set( String::New((const char *) columns[i].name), - GetColumnValue( prep_req->hSTMT, columns[i], buf, MAX_VALUE_SIZE - 1)); - } - - rows->Set(Integer::New(count), tuple); - count++; - } - - for(int i = 0; i < colCount; i++) { - delete [] columns[i].name; - } - - delete [] columns; - } - - //move to the next result set - ret = SQLMoreResults( prep_req->hSTMT ); - - //Only trigger an emit if there are columns OR if this is the last result and none others have been emitted - //odbc will process individual statments like select @something = 1 as a recordset even though it doesn't have - //any columns. We don't want to emit those unless there are actually columns - if (colCount > 0 || ( ret != SQL_SUCCESS && emitCount == 0 )) { - emitCount++; - - Local args[3]; - - if (errorCount) { - args[0] = objError; //(objError->IsUndefined()) ? Undefined() : ; - } - else { - args[0] = Local::New(Null()); - } - - args[1] = rows; - //true or false, are there more result sets to follow this emit? - args[2] = Local::New(( ret == SQL_SUCCESS ) ? True() : False() ); - - prep_req->cb->Call(Context::GetCurrent()->Global(), 3, args); - } - } - while ( self->canHaveMoreResults && ret == SQL_SUCCESS ); - //} //end of malloc check -cleanupshutdown: - TryCatch try_catch; - - self->Unref(); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - - uv_mutex_lock(&ODBC::g_odbcMutex); - - SQLFreeHandle(SQL_HANDLE_STMT, prep_req->hSTMT); - - uv_mutex_unlock(&ODBC::g_odbcMutex); - - free(buf); - prep_req->cb.Dispose(); - free(prep_req->sql); - free(prep_req->catalog); - free(prep_req->schema); - free(prep_req->table); - free(prep_req->type); - free(prep_req); - free(req); - scope.Close(Undefined()); -} - -void ODBC::UV_QueryAll(uv_work_t* req) { - DEBUG_PRINTF("ODBC::UV_QueryAll\n"); - query_request* prep_req = (query_request *)(req->data); - - Parameter prm; - SQLRETURN ret; - - uv_mutex_lock(&ODBC::g_odbcMutex); - - SQLAllocHandle( SQL_HANDLE_STMT, prep_req->dbo->m_hDBC, &prep_req->hSTMT ); - - uv_mutex_unlock(&ODBC::g_odbcMutex); - - //check to see if should excute a direct or a parameter bound query - if (!prep_req->paramCount) { - // execute the query directly - ret = SQLExecDirect( prep_req->hSTMT,(SQLCHAR *)prep_req->sql, strlen(prep_req->sql) ); - } - else { - // prepare statement, bind parameters and execute statement - ret = SQLPrepare(prep_req->hSTMT, (SQLCHAR *)prep_req->sql, strlen(prep_req->sql)); - - if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { - for (int i = 0; i < prep_req->paramCount; i++) { - prm = prep_req->params[i]; - - DEBUG_PRINTF("ODBC::UV_QueryAll - param[%i]: c_type=%i type=%i " - "buffer_length=%i size=%i length=%i &length=%X decimals=%i\n", i, prm.c_type, prm.type, - prm.buffer_length, prm.size, prm.length, &prep_req->params[i].length, prm.decimals); - - ret = SQLBindParameter( prep_req->hSTMT, //StatementHandle - i + 1, //ParameterNumber - SQL_PARAM_INPUT, //InputOutputType - prm.c_type, //ValueType - prm.type, //ParameterType - prm.size, //ColumnSize - prm.decimals, //DecimalDigits - prm.buffer, //ParameterValuePtr - prm.buffer_length, //BufferLength - //using &prm.length did not work here... - &prep_req->params[i].length); //StrLen_or_IndPtr - - if (ret == SQL_ERROR) { - break; - } - } - - if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { - ret = SQLExecute(prep_req->hSTMT); - } - } - - // free parameters - // - for (int i = 0; i < prep_req->paramCount; i++) { - if (prm = prep_req->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; - } - } - } - - free(prep_req->params); - } - - prep_req->result = ret; // this will be checked later in UV_AfterQuery -} - -Handle ODBC::QueryAll(const Arguments& args) { - HandleScope scope; - - REQ_STR_ARG(0, sql); - - Local cb; - - ODBC* dbo = ObjectWrap::Unwrap(args.This()); - uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); - query_request* prep_req = (query_request *) calloc(1, sizeof(query_request)); - - if (!prep_req) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); - } - - // populate prep_req->params if parameters were supplied - // - if (args.Length() > 2) { - if ( !args[1]->IsArray() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 1 must be an Array")) - ); - } - else if ( !args[2]->IsFunction() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 2 must be a Function")) - ); - } - - cb = Local::Cast(args[2]); - prep_req->params = GetParametersFromArray(Local::Cast(args[1]), &prep_req->paramCount); - } - else { - if ( !args[1]->IsFunction() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 1 must be a Function")) - ); - } - - cb = Local::Cast(args[1]); - - prep_req->paramCount = 0; - } - - prep_req->sql = (char *) malloc(sql.length() +1); - prep_req->catalog = NULL; - prep_req->schema = NULL; - prep_req->table = NULL; - prep_req->type = NULL; - prep_req->column = NULL; - prep_req->cb = Persistent::New(cb); - - strcpy(prep_req->sql, *sql); - - prep_req->dbo = dbo; - work_req->data = prep_req; - - uv_queue_work(uv_default_loop(), work_req, UV_QueryAll, (uv_after_work_cb)UV_AfterQueryAll); - - dbo->Ref(); - - return scope.Close(Undefined()); -} -*/ + * GetColumns + */ Column* ODBC::GetColumns(SQLHSTMT hStmt, short* colCount) { SQLRETURN ret; @@ -795,6 +247,10 @@ Column* ODBC::GetColumns(SQLHSTMT hStmt, short* colCount) { return columns; } +/* + * FreeColumns + */ + void ODBC::FreeColumns(Column* columns, short* colCount) { for(int i = 0; i < *colCount; i++) { delete [] columns[i].name; @@ -805,6 +261,10 @@ void ODBC::FreeColumns(Column* columns, short* colCount) { *colCount = 0; } +/* + * GetColumnValue + */ + Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength) { HandleScope scope; @@ -955,6 +415,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } } +/* + * GetRecordTuple + */ + Handle ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { @@ -971,6 +435,10 @@ Handle ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, return scope.Close(tuple); } +/* + * GetRecordArray + */ + Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { @@ -987,6 +455,10 @@ Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, return scope.Close(array); } +/* + * GetParametersFromArray + */ + Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { DEBUG_PRINTF("ODBC::GetParametersFromArray\n"); *paramCount = values->Length(); @@ -1076,6 +548,10 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { return params; } +/* + * CallbackSQLError + */ + Handle ODBC::CallbackSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, @@ -1096,6 +572,10 @@ Handle ODBC::CallbackSQLError (HENV hENV, return scope.Close(Undefined()); } +/* + * GetSQLError + */ + Local ODBC::GetSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, @@ -1124,6 +604,10 @@ Local ODBC::GetSQLError (HENV hENV, return scope.Close(objError); } +/* + * GetSQLDiagRecError + */ + Local ODBC::GetSQLDiagRecError (HDBC hDBC) { HandleScope scope; @@ -1157,6 +641,9 @@ Local ODBC::GetSQLDiagRecError (HDBC hDBC) { return scope.Close(objError); } +/* + * GetAllRecordsSync + */ Local ODBC::GetAllRecordsSync (HENV hENV, HDBC hDBC, diff --git a/src/odbc.h b/src/odbc.h index 188968d5..ffec8567 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -85,34 +85,6 @@ class ODBC : public node::ObjectWrap { static void UV_CreateConnection(uv_work_t* work_req); static void UV_AfterCreateConnection(uv_work_t* work_req, int status); - //Property Getter/Setters -/* static Handle ModeGetter(Local property, const AccessorInfo &info); - static void ModeSetter(Local property, Local value, const AccessorInfo &info); - - static Handle ConnectedGetter(Local property, const AccessorInfo &info); - - static void UV_AfterOpen(uv_work_t* req, int status); - static void UV_Open(uv_work_t* req); - static Handle Open(const Arguments& args); - - static void UV_AfterClose(uv_work_t* req, int status); - static void UV_Close(uv_work_t* req); - static Handle Close(const Arguments& args); - - static void UV_AfterQuery(uv_work_t* req, int status); - static void UV_Query(uv_work_t* req); - static Handle Query(const Arguments& args); - - static void UV_AfterQueryAll(uv_work_t* req, int status); - static void UV_QueryAll(uv_work_t* req); - static Handle QueryAll(const Arguments& args); - - static void UV_Tables(uv_work_t* req); - static Handle Tables(const Arguments& args); - - static void UV_Columns(uv_work_t* req); - static Handle Columns(const Arguments& args); -*/ static void WatcherCallback(uv_async_t* w, int revents); ODBC *self(void) { return this; } From f4a4faa1dbfe1fd798a191f53e8ffce5811361c4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:16:55 -0400 Subject: [PATCH 144/511] change source of odbc module (just use '../') --- test/bench-insert.js | 2 +- test/bench-query-fetch.js | 2 +- test/bench-query-fetchAll.js | 2 +- test/bench-query.js | 2 +- test/bench-querySync.js | 2 +- test/sql-cli.js | 2 +- test/test-bad-connection-string.js | 2 +- test/test-closed.js | 2 +- test/test-connection-object.js | 2 +- test/test-date.js | 2 +- test/test-describe-column.js | 2 +- test/test-describe-database.js | 2 +- test/test-describe-table.js | 2 +- test/test-multi-open-close.js | 2 +- test/test-multi-open-query-close.js | 2 +- test/test-open-close.js | 2 +- test/test-param-select-with-booleans-only.js | 2 +- test/test-param-select-with-decimals-only.js | 2 +- test/test-param-select-with-null.js | 2 +- test/test-param-select-with-nulls-mixed.js | 2 +- test/test-param-select-with-numbers-mixed.js | 2 +- test/test-param-select-with-numbers-only.js | 2 +- test/test-param-select-with-strings-only.js | 2 +- test/test-pool-close.js | 2 +- test/test-pool-connect.js | 2 +- test/test-query-create-table.js | 2 +- test/test-query-drop-table.js | 2 +- test/test-query-insert.js | 2 +- test/test-query-select-fetch.js | 2 +- test/test-query-select-fetchAll.js | 2 +- test/test-query-select-unicode.js | 2 +- test/test-query-select.js | 2 +- test/test-querySync-select.js | 2 +- 33 files changed, 33 insertions(+), 33 deletions(-) diff --git a/test/bench-insert.js b/test/bench-insert.js index d112cb04..f1538649 100644 --- a/test/bench-insert.js +++ b/test/bench-insert.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database(); db.open(common.connectionString, function(err){ diff --git a/test/bench-query-fetch.js b/test/bench-query-fetch.js index 62048cb2..81c522a8 100644 --- a/test/bench-query-fetch.js +++ b/test/bench-query-fetch.js @@ -1,5 +1,5 @@ var common = require("./common") -, odbc = require("../odbc.js") +, odbc = require("../") , db = new odbc.Database(); db.open(common.connectionString, function(err){ diff --git a/test/bench-query-fetchAll.js b/test/bench-query-fetchAll.js index ae6461c6..a5b4b63d 100644 --- a/test/bench-query-fetchAll.js +++ b/test/bench-query-fetchAll.js @@ -1,5 +1,5 @@ var common = require("./common") -, odbc = require("../odbc.js") +, odbc = require("../") , db = new odbc.Database(); db.open(common.connectionString, function(err){ diff --git a/test/bench-query.js b/test/bench-query.js index 8e3e3343..c56f3c69 100644 --- a/test/bench-query.js +++ b/test/bench-query.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database(); db.open(common.connectionString, function(err){ diff --git a/test/bench-querySync.js b/test/bench-querySync.js index 2d4dc4c0..92edb18b 100644 --- a/test/bench-querySync.js +++ b/test/bench-querySync.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database(); db.open(common.connectionString, function(err){ diff --git a/test/sql-cli.js b/test/sql-cli.js index d8c73f4d..50ce0688 100644 --- a/test/sql-cli.js +++ b/test/sql-cli.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database(); db.open(common.connectionString, function(err) diff --git a/test/test-bad-connection-string.js b/test/test-bad-connection-string.js index 49e83637..9f32ffe1 100644 --- a/test/test-bad-connection-string.js +++ b/test/test-bad-connection-string.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-closed.js b/test/test-closed.js index a54e3245..2a7ff5d4 100644 --- a/test/test-closed.js +++ b/test/test-closed.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-connection-object.js b/test/test-connection-object.js index 9008ebd0..d780297e 100644 --- a/test/test-connection-object.js +++ b/test/test-connection-object.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-date.js b/test/test-date.js index 9d65cc10..aafb9095 100644 --- a/test/test-date.js +++ b/test/test-date.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-describe-column.js b/test/test-describe-column.js index 583ae9e7..9bbd3c76 100644 --- a/test/test-describe-column.js +++ b/test/test-describe-column.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-describe-database.js b/test/test-describe-database.js index cf24f657..a5201905 100644 --- a/test/test-describe-database.js +++ b/test/test-describe-database.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-describe-table.js b/test/test-describe-table.js index 5e464be5..00fbc220 100644 --- a/test/test-describe-table.js +++ b/test/test-describe-table.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-multi-open-close.js b/test/test-multi-open-close.js index 21099973..9d6097e9 100644 --- a/test/test-multi-open-close.js +++ b/test/test-multi-open-close.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , openCallback = 0 , closeCallback = 0 , openCount = 100 diff --git a/test/test-multi-open-query-close.js b/test/test-multi-open-query-close.js index 436aaeb3..835bf505 100644 --- a/test/test-multi-open-query-close.js +++ b/test/test-multi-open-query-close.js @@ -1,5 +1,5 @@ var common = require("./common") -, odbc = require("../odbc.js") +, odbc = require("../") , openCallback = 0 , closeCallback = 0 , queryCallback = 0 diff --git a/test/test-open-close.js b/test/test-open-close.js index da88b51f..2ee660f9 100644 --- a/test/test-open-close.js +++ b/test/test-open-close.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert"); diff --git a/test/test-param-select-with-booleans-only.js b/test/test-param-select-with-booleans-only.js index eb045aba..94b27bf4 100644 --- a/test/test-param-select-with-booleans-only.js +++ b/test/test-param-select-with-booleans-only.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert"); diff --git a/test/test-param-select-with-decimals-only.js b/test/test-param-select-with-decimals-only.js index dc015935..35df7d99 100644 --- a/test/test-param-select-with-decimals-only.js +++ b/test/test-param-select-with-decimals-only.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert"); diff --git a/test/test-param-select-with-null.js b/test/test-param-select-with-null.js index 888334de..d1e68584 100644 --- a/test/test-param-select-with-null.js +++ b/test/test-param-select-with-null.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert"); diff --git a/test/test-param-select-with-nulls-mixed.js b/test/test-param-select-with-nulls-mixed.js index fdcc61fa..efc04220 100644 --- a/test/test-param-select-with-nulls-mixed.js +++ b/test/test-param-select-with-nulls-mixed.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert"); diff --git a/test/test-param-select-with-numbers-mixed.js b/test/test-param-select-with-numbers-mixed.js index d6732cee..8a5cb6b5 100644 --- a/test/test-param-select-with-numbers-mixed.js +++ b/test/test-param-select-with-numbers-mixed.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert"); diff --git a/test/test-param-select-with-numbers-only.js b/test/test-param-select-with-numbers-only.js index 6b4409d6..3450f07f 100644 --- a/test/test-param-select-with-numbers-only.js +++ b/test/test-param-select-with-numbers-only.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert"); diff --git a/test/test-param-select-with-strings-only.js b/test/test-param-select-with-strings-only.js index 104aa8a8..8ce19dc2 100644 --- a/test/test-param-select-with-strings-only.js +++ b/test/test-param-select-with-strings-only.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert"); diff --git a/test/test-pool-close.js b/test/test-pool-close.js index 353a039a..2a36f756 100644 --- a/test/test-pool-close.js +++ b/test/test-pool-close.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , pool = new odbc.Pool() , connectionString = common.connectionString , connections = [] diff --git a/test/test-pool-connect.js b/test/test-pool-connect.js index a52d588f..a0f0d12b 100644 --- a/test/test-pool-connect.js +++ b/test/test-pool-connect.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , pool = new odbc.Pool() , connectionString = common.connectionString , connections = [] diff --git a/test/test-query-create-table.js b/test/test-query-create-table.js index 0eec3c9f..b5711134 100644 --- a/test/test-query-create-table.js +++ b/test/test-query-create-table.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-query-drop-table.js b/test/test-query-drop-table.js index 2e283ddf..f4612f27 100644 --- a/test/test-query-drop-table.js +++ b/test/test-query-drop-table.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-query-insert.js b/test/test-query-insert.js index bc67b8b1..0369d8e4 100644 --- a/test/test-query-insert.js +++ b/test/test-query-insert.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") , insertCount = 0; diff --git a/test/test-query-select-fetch.js b/test/test-query-select-fetch.js index 090a8b27..40628b9e 100644 --- a/test/test-query-select-fetch.js +++ b/test/test-query-select-fetch.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-query-select-fetchAll.js b/test/test-query-select-fetchAll.js index 6f9aeba9..100b7273 100644 --- a/test/test-query-select-fetchAll.js +++ b/test/test-query-select-fetchAll.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-query-select-unicode.js b/test/test-query-select-unicode.js index 9224cc55..91cb3bd4 100644 --- a/test/test-query-select-unicode.js +++ b/test/test-query-select-unicode.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-query-select.js b/test/test-query-select.js index 7677911d..8109c40b 100644 --- a/test/test-query-select.js +++ b/test/test-query-select.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; diff --git a/test/test-querySync-select.js b/test/test-querySync-select.js index c4cf00df..75fd8b7b 100644 --- a/test/test-querySync-select.js +++ b/test/test-querySync-select.js @@ -1,5 +1,5 @@ var common = require("./common") - , odbc = require("../odbc.js") + , odbc = require("../") , db = new odbc.Database() , assert = require("assert") ; From 5a263763802eb25010cb896d771e9388e1763a3c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:20:19 -0400 Subject: [PATCH 145/511] remove commented code --- lib/odbc.js | 263 ---------------------------------------------------- 1 file changed, 263 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 90482d45..8665db38 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -256,269 +256,6 @@ Database.prototype.describe = function(obj, callback) { } }; -/* -function Database () { - var self = this; - var db = new odbc.Database(); - db.executing = false; - db.connected = false; - db.queue = []; - - db.__proto__ = Database.prototype; - - return db; -}; - -Database.prototype = { - __proto__: odbc.Database.prototype, - constructor: Database, -}; - -Database.prototype.processQueue = function () { - var self = this; - - if (!self.queue) self.queue = []; - - if (self.connected && !self.executing && self.queue.length) { - var currentQuery = self.queue[0]; - self.executing = true; - - currentQuery.method.apply(currentQuery.context, currentQuery.args); //TODO: we need to make sure we aren't sending any extra arguments to the cpp method - } -}; - -Database.prototype.query = function(sql, params, callback) { - var self = this, args = []; - - if (callback == null) { - callback = params; // no parameters supplied - params = null; - } - - if (!self.connected) { - return callback( { message : "Connection not open." }, [], false ); - } - - if (!self.queue) self.queue = []; - - args.push(sql); - - if (params) { - args.push(params); - } - - args.push(function (error, rows, morefollowing) { - //check to see if this is the last result set returned - if (!morefollowing) { - self.queue.shift(); - self.executing = false; - } - - if (callback) callback.apply(self, arguments); - - self.processQueue(); - }); - - self.queue.push({ - context : self, - method : self.dispatchQueryAll, - args : args - }); - - self.processQueue(); -}; - -Database.prototype.queryResult = function(sql, params, callback) { - var self = this, args = []; - - if (callback == null) { - callback = params; // no parameters supplied - params = null; - } - - if (!self.connected) { - return callback( { message : "Connection not open." }, [], false ); - } - - if (!self.queue) self.queue = []; - - args.push(sql); - - if (params) { - args.push(params); - } - - args.push(function (error, rows, morefollowing) { - //check to see if this is the last result set returned - if (!morefollowing) { - self.queue.shift(); - self.executing = false; - } - - if (callback) callback.apply(self, arguments); - - self.processQueue(); - }); - - self.queue.push({ - context : self, - method : self.dispatchQuery, - args : args - }); - - self.processQueue(); -}; - -Database.prototype.open = function(connectionString, callback) { - var self = this; - - if (self.connected) { - return callback( { message : "Connection already open." }, [], false); - } - - if (typeof(connectionString) == "object") { - var obj = connectionString; - connectionString = ""; - - Object.keys(obj).forEach(function (key) { - connectionString += key + "=" + obj[key] + ";"; - }); - } - - self.dispatchOpen(connectionString, function (err) { - if (!err) { - self.connected = true; - self.processQueue(); - } - - return callback(err); - }); -}; - -// -// * -// * We must queue the close. If we don't then we may close during the middle of a query which -// * could cause a segfault or other madness -// * -// - -Database.prototype.close = function(callback) { - var self = this; - - if (!self.queue) self.queue = []; - - self.queue.push({ - context : self, - method : self.dispatchClose, - args : [function (err) { - self.queue = []; - self.connected = false; - self.executing = false; - - if (err && !callback) throw err; - else if (callback) callback(err) - }] - }); - - self.processQueue(); -}; - -Database.prototype.tables = function(catalog, schema, table, type, callback) { - var self = this; - if (!self.queue) self.queue = []; - - callback = callback || arguments[arguments.length - 1]; - - var args = [catalog, schema, table, type, function () { - self.queue.shift(); - self.executing = false; - - if (callback) callback.apply(self, arguments); - - self.processQueue(); - }]; - - self.queue.push({ - context : self, - method : self.dispatchTables, - args : args - }); - - self.processQueue(); -}; - -Database.prototype.columns = function(catalog, schema, table, column, callback) { - var self = this; - if (!self.queue) self.queue = []; - - callback = callback || arguments[arguments.length - 1]; - - var args = [catalog, schema, table, column, function () { - self.queue.shift(); - self.executing = false; - - if (callback) callback.apply(self, arguments); - - self.processQueue(); - }]; - - self.queue.push({ - context : self, - method : self.dispatchColumns, - args : args - }); - - self.processQueue(); -}; - -Database.prototype.describe = function(obj, callback) { - var self = this; - - if (typeof(callback) != "function") { - throw({ - error : "[node-odbc] Missing Arguments", - message : "You must specify a callback function in order for the describe method to work." - }); - - return false; - } - - if (typeof(obj) != "object") { - callback({ - error : "[node-odbc] Missing Arguments", - message : "You must pass an object as argument 0 if you want anything productive to happen in the describe method." - }, []); - - return false; - } - - if (!obj.database) { - callback({ - error : "[node-odbc] Missing Arguments", - message : "The object you passed did not contain a database property. This is required for the describe method to work." - }, []); - - return false; - } - - //set some defaults if they weren't passed - obj.schema = obj.schema || "%"; - obj.type = obj.type || "table"; - - if (obj.table && obj.column) { - //get the column details - self.columns(obj.database, obj.schema, obj.table, obj.column, callback); - } - else if (obj.table) { - //get the columns in the table - self.columns(obj.database, obj.schema, obj.table, "%", callback); - } - else { - //get the tables in the database - self.tables(obj.database, obj.schema, null, obj.type || "table", callback); - } -}; -*/ module.exports.Pool = Pool; Pool.count = 0; From 69f58afc238b11e92a17cf4dfdd2abba000b13fa Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 17:31:35 -0400 Subject: [PATCH 146/511] remove wscript file, it is now out of date --- wscript | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 wscript diff --git a/wscript b/wscript deleted file mode 100644 index a399d646..00000000 --- a/wscript +++ /dev/null @@ -1,16 +0,0 @@ -def set_options(opt): - opt.tool_options("compiler_cxx") - -def configure(conf): - conf.check_tool("compiler_cxx") - conf.check_tool("node_addon") - if not conf.check(lib="odbc", libpath=['/usr/local/lib', '/opt/local/lib'], uselib_store="ODBC"): - conf.fatal('Missing libodbc'); - conf.env.append_value('LIBPATH_ODBC', '/opt/local/lib'); - -def build(bld): - obj = bld.new_task_gen("cxx", "shlib", "node_addon") - obj.target = "odbc_bindings" - obj.source = "src/Database.cpp" - obj.uselib = "ODBC" - From 841241e6e8010dfdb230b8f26c1294c6a9548978 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 18:07:38 -0400 Subject: [PATCH 147/511] throw the entire error object --- src/odbc_connection.cpp | 2 +- src/odbc_result.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 7b858350..479d4fb7 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -782,7 +782,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { (char *) "[node-odbc] Error in ODBCConnection::QuerySync" ); - ThrowException(Exception::Error(objError->Get(String::New("error"))->ToString())); + ThrowException(objError); return scope.Close(Undefined()); } diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index f989eec9..5a158423 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -417,7 +417,8 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { self->m_hENV, self->m_hDBC, self->m_hSTMT, - (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll" + (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll; probably" + " your query did not have a result set." ); break; @@ -450,7 +451,7 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { // //we will need to throw if there is a valid error. if (errorCount > 0) { - ThrowException(Exception::Error(objError->Get(String::New("error"))->ToString())); + ThrowException(objError); } return scope.Close(rows); From c481e63931a2f7d44899359bba7a644cfd358bcd Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 18:07:54 -0400 Subject: [PATCH 148/511] new tests --- test/test-query-create-table-fetchSync.js | 25 ++++++++++++++++++++ test/test-query-select-fetchAllSync.js | 24 +++++++++++++++++++ test/test-querySync-select-with-execption.js | 24 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 test/test-query-create-table-fetchSync.js create mode 100644 test/test-query-select-fetchAllSync.js create mode 100644 test/test-querySync-select-with-execption.js diff --git a/test/test-query-create-table-fetchSync.js b/test/test-query-create-table-fetchSync.js new file mode 100644 index 00000000..8cfa3a9c --- /dev/null +++ b/test/test-query-create-table-fetchSync.js @@ -0,0 +1,25 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + db.queryResult("create table " + common.tableName + " (COLINT INTEGER, COLDATETIME DATETIME, COLTEXT TEXT)", function (err, result) { + console.log(arguments); + + try { + //this should throw because there was no result to be had? + var data = result.fetchAllSync(); + console.log(data); + } + catch (e) { + console.log(e); + } + + + db.close(function () { + + }); + }); +}); diff --git a/test/test-query-select-fetchAllSync.js b/test/test-query-select-fetchAllSync.js new file mode 100644 index 00000000..405e6217 --- /dev/null +++ b/test/test-query-select-fetchAllSync.js @@ -0,0 +1,24 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + db.queryResult("select 1 as COLINT, 'some test' as COLTEXT union select 2, 'something else' ", function (err, result) { + assert.equal(err, null); + assert.equal(result.constructor.name, "ODBCResult"); + + var data = result.fetchAllSync(); + + db.close(function () { + assert.deepEqual(data, [ + {"COLINT":1,"COLTEXT":"some test"} + ,{"COLINT":2,"COLTEXT":"something else"} + ]); + }); + }); +}); diff --git a/test/test-querySync-select-with-execption.js b/test/test-querySync-select-with-execption.js new file mode 100644 index 00000000..6690771f --- /dev/null +++ b/test/test-querySync-select-with-execption.js @@ -0,0 +1,24 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + var err = null; + + try { + var data = db.querySync("select invalid query"); + } + catch (e) { + console.log(e); + + err = e; + } + + db.close(function () { + assert.equal(err.error, "[node-odbc] Error in ODBCConnection::QuerySync"); + }); +}); From c80df3730123a650ee569c66d5463b48670405a3 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 18:41:33 -0400 Subject: [PATCH 149/511] add ODBCConnection::OpenSync --- src/odbc_connection.cpp | 90 +++++++++++++++++++++++++++++++++++++++++ src/odbc_connection.h | 1 + 2 files changed, 91 insertions(+) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 479d4fb7..99eae556 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -52,6 +52,7 @@ void ODBCConnection::Init(v8::Handle target) { // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "openSync", OpenSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatement", CreateStatement); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatementSync", CreateStatementSync); @@ -248,6 +249,95 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { scope.Close(Undefined()); } +/* + * OpenSync + */ + +Handle ODBCConnection::OpenSync(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::OpenSync\n"); + HandleScope scope; + + REQ_STR_ARG(0, connection); + + //get reference to the connection object + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + Local objError; + SQLRETURN ret; + bool err = false; + char* connectionString; + char connstr[1024]; + + //allocate memory for the connection string + connectionString = (char *) malloc(connection.length() +1); + + //copy the connection string to the work data + strcpy(connectionString, *connection); + + uv_mutex_lock(&ODBC::g_odbcMutex); + + //TODO: make this configurable + SQLSetConnectOption( conn->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); + + //Attempt to connect + ret = SQLDriverConnect( + conn->m_hDBC, + NULL, + (SQLCHAR*) connectionString, + strlen(connectionString), + (SQLCHAR*) connstr, + 1024, + NULL, + SQL_DRIVER_NOPROMPT); + + if (!SQL_SUCCEEDED(ret)) { + err = true; + + objError = ODBC::GetSQLDiagRecError(conn->self()->m_hDBC); + } + else { + HSTMT hStmt; + + //allocate a temporary statment + ret = SQLAllocStmt(conn->m_hDBC, &hStmt); + + //try to determine if the driver can handle + //multiple recordsets + ret = SQLGetFunctions( + conn->m_hDBC, + SQL_API_SQLMORERESULTS, + &conn->canHaveMoreResults); + + if (!SQL_SUCCEEDED(ret)) { + conn->canHaveMoreResults = 0; + } + + //free the handle + ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt); + + conn->self()->connected = true; + + //only uv_ref if the connection was successful + #if NODE_VERSION_AT_LEAST(0, 7, 9) + uv_ref((uv_handle_t *)&ODBC::g_async); + #else + uv_ref(uv_default_loop()); + #endif + } + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + free(connectionString); + + if (err) { + ThrowException(objError); + return scope.Close(False()); + } + else { + return scope.Close(True()); + } +} + /* * Close * diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 1176be46..d89b49e7 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -74,6 +74,7 @@ class ODBCConnection : public node::ObjectWrap { //sync methods static Handle CreateStatementSync(const Arguments& args); + static Handle OpenSync(const Arguments& args); static Handle QuerySync(const Arguments& args); struct Fetch_Request { From 2ea0c19339959e98d029ad21e847c38606ea7643 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Apr 2013 18:58:33 -0400 Subject: [PATCH 150/511] Add ODBC::CreateConnectionSync --- src/odbc.cpp | 30 ++++++++++++++++++++++++++++++ src/odbc.h | 3 +++ 2 files changed, 33 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index 53d547f8..b99050e9 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -55,6 +55,7 @@ void ODBC::Init(v8::Handle target) { // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnection", CreateConnection); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnectionSync", CreateConnectionSync); // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBC"), @@ -193,6 +194,35 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { scope.Close(Undefined()); } +/* + * CreateConnectionSync + */ + +Handle ODBC::CreateConnectionSync(const Arguments& args) { + DEBUG_PRINTF("ODBC::CreateConnectionSync\n"); + HandleScope scope; + + ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); + + HDBC hDBC; + + uv_mutex_lock(&ODBC::g_odbcMutex); + + //allocate a new connection handle + SQLRETURN ret = SQLAllocConnect(dbo->m_hEnv, &hDBC); + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + Local params[2]; + params[0] = External::New(dbo->m_hEnv); + params[1] = External::New(hDBC); + + Persistent js_result(ODBCConnection::constructor_template-> + GetFunction()->NewInstance(2, params)); + + return scope.Close(js_result); +} + /* * GetColumns */ diff --git a/src/odbc.h b/src/odbc.h index ffec8567..19918e43 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -87,6 +87,9 @@ class ODBC : public node::ObjectWrap { static void WatcherCallback(uv_async_t* w, int revents); + //sync methods + static Handle CreateConnectionSync(const Arguments& args); + ODBC *self(void) { return this; } protected: From d7be772f92a6ea60cd3539d5d6bb74efad7acfda Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 7 Apr 2013 22:07:07 -0400 Subject: [PATCH 151/511] Added ODBCConnection::BeginTransactionSync() and ODBCCOnnection::EndTransactionSync(rollback) and related tests --- lib/odbc.js | 32 +++++++++ src/odbc.h | 6 ++ src/odbc_connection.cpp | 87 ++++++++++++++++++++++++ src/odbc_connection.h | 2 + test/data/sqlite-test.db-journal | Bin 0 -> 2576 bytes test/test-binding-transaction-commit.js | 54 +++++++++++++++ test/test-transaction-commit-sync.js | 54 +++++++++++++++ 7 files changed, 235 insertions(+) create mode 100644 test/data/sqlite-test.db-journal create mode 100644 test/test-binding-transaction-commit.js create mode 100644 test/test-transaction-commit-sync.js diff --git a/lib/odbc.js b/lib/odbc.js index 8665db38..495d7741 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -170,6 +170,38 @@ Database.prototype.querySync = function (sql, params) { return data; }; +Database.prototype.beginTransactionSync = function () { + var self = this; + + self.conn.beginTransactionSync(); + + return self; +}; + +Database.prototype.endTransactionSync = function (rollback) { + var self = this; + + self.conn.endTransactionSync(rollback); + + return self; +}; + +Database.prototype.commitTransactionSync = function () { + var self = this; + + self.conn.endTransactionSync(false); //don't rollback + + return self; +} + +Database.prototype.rollbackTransactionSync = function () { + var self = this; + + self.conn.endTransactionSync(true); //rollback + + return self; +} + Database.prototype.columns = function(catalog, schema, table, column, callback) { var self = this; if (!self.queue) self.queue = []; diff --git a/src/odbc.h b/src/odbc.h index 19918e43..19b274bf 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -164,6 +164,12 @@ struct query_request { String::New("Argument " #I " must be a function"))); \ Local VAR = Local::Cast(args[I]); +#define REQ_BOOL_ARG(I, VAR) \ + if (args.Length() <= (I) || !args[I]->IsBoolean()) \ + return ThrowException(Exception::TypeError( \ + String::New("Argument " #I " must be a boolean"))); \ + Local VAR = (args[I]->ToBoolean()); + #define REQ_EXT_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsExternal()) \ return ThrowException(Exception::TypeError( \ diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 99eae556..579fb5ca 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -59,6 +59,9 @@ void ODBCConnection::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", Query); NODE_SET_PROTOTYPE_METHOD(constructor_template, "querySync", QuerySync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "beginTransactionSync", BeginTransactionSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "endTransactionSync", EndTransactionSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "columns", Columns); NODE_SET_PROTOTYPE_METHOD(constructor_template, "tables", Tables); @@ -1065,3 +1068,87 @@ void ODBCConnection::UV_Columns(uv_work_t* req) { // this will be checked later in UV_AfterQuery data->result = ret; } + +/* + * BeginTransactionSync + * + */ + +Handle ODBCConnection::BeginTransactionSync(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::BeginTransactionSync\n"); + HandleScope scope; + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + SQLRETURN ret; + + //set the connection manual commits + ret = SQLSetConnectAttr( + conn->m_hDBC, + SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER) SQL_AUTOCOMMIT_OFF, + SQL_NTS); + + if (!SQL_SUCCEEDED(ret)) { + Local objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); + + ThrowException(objError); + + return scope.Close(False()); + } + + return scope.Close(True()); +} + +/* + * EndTransactionSync + * + */ + +Handle ODBCConnection::EndTransactionSync(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::EndTransactionSync\n"); + HandleScope scope; + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + REQ_BOOL_ARG(0, rollback); + + SQLRETURN ret; + SQLSMALLINT completionType = (rollback->Value()) + ? SQL_ROLLBACK + : SQL_COMMIT + ; + + //Call SQLEndTran + ret = SQLEndTran( + SQL_HANDLE_DBC, + conn->m_hDBC, + completionType); + + //check how the transaction went + if (!SQL_SUCCEEDED(ret)) { + Local objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); + + ThrowException(objError); + + return scope.Close(False()); + } + + //Reset the connection back to autocommit + ret = SQLSetConnectAttr( + conn->m_hDBC, + SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER) SQL_AUTOCOMMIT_ON, + SQL_NTS); + + //check how setting the connection attr went + if (!SQL_SUCCEEDED(ret)) { + Local objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); + + ThrowException(objError); + + return scope.Close(False()); + } + + return scope.Close(True()); +} \ No newline at end of file diff --git a/src/odbc_connection.h b/src/odbc_connection.h index d89b49e7..27cacbc0 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -76,6 +76,8 @@ class ODBCConnection : public node::ObjectWrap { static Handle CreateStatementSync(const Arguments& args); static Handle OpenSync(const Arguments& args); static Handle QuerySync(const Arguments& args); + static Handle BeginTransactionSync(const Arguments& args); + static Handle EndTransactionSync(const Arguments& args); struct Fetch_Request { Persistent callback; diff --git a/test/data/sqlite-test.db-journal b/test/data/sqlite-test.db-journal new file mode 100644 index 0000000000000000000000000000000000000000..efaf781a018596e8a5edf15001e1517ea903f609 GIT binary patch literal 2576 zcmZQzKmhmcEmc4s6BseDAS6av!#M<)ctO4!jqc$b$D@854FQ}X04?_!g9CjsOHvim z@{4j4OB9TOMFJxugM)$sP@@451M3AiPzJIX;XG6}k}wL>fI;_>BB-okWG-R=qERp! z0wX*G8o3$S#Z65a8!bx`lX6o1{9Rn*{au`#<3n77L*hdmoqSxei7`0`xjKfpDu5Lz vU{j%>;q33@=@+5^#IEkHK{^URz6(%Kh^Mcs0*nC`4RMVKQ2-H|pmG2J6@)d@ literal 0 HcmV?d00001 diff --git a/test/test-binding-transaction-commit.js b/test/test-binding-transaction-commit.js new file mode 100644 index 00000000..fa06991a --- /dev/null +++ b/test/test-binding-transaction-commit.js @@ -0,0 +1,54 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.ODBC() + , assert = require("assert") + , exitCode = 0 + ; + +db.createConnection(function (err, conn) { + conn.open(common.connectionString, function (err) { + common.createTables(conn, function (err, data) { + try { + conn.beginTransactionSync(); + + var result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + + conn.endTransactionSync(true); //rollback + + result = conn.querySync("select * from " + common.tableName); + + assert.deepEqual(result.fetchAllSync(), []); + } + catch (e) { + console.log("Failed when rolling back"); + console.log(e); + exitCode = 1 + } + + try { + //Start a new transaction + conn.beginTransactionSync(); + + result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + + conn.endTransactionSync(false); //commit + + result = conn.querySync("select * from " + common.tableName); + + assert.deepEqual(result.fetchAllSync(), [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); + } + catch (e) { + console.log("Failed when committing"); + console.log(e); + + exitCode = 2; + } + + common.dropTables(conn, function (err) { + conn.close(function () { + process.exit(exitCode); + }); + }); + }); + }); +}); diff --git a/test/test-transaction-commit-sync.js b/test/test-transaction-commit-sync.js new file mode 100644 index 00000000..fca8d43b --- /dev/null +++ b/test/test-transaction-commit-sync.js @@ -0,0 +1,54 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + , exitCode = 0 + ; + + +db.open(common.connectionString, function (err) { + common.createTables(db, function (err, data) { + try { + db.beginTransactionSync(); + + var results = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + + db.rollbackTransaction(); + + results = db.querySync("select * from " + common.tableName); + + assert.deepEqual(results, []); + } + catch (e) { + console.log("Failed when rolling back"); + console.log(e); + exitCode = 1 + } + + try { + //Start a new transaction + db.beginTransactionSync(); + + result = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + + db.commitTransaction(); //commit + + result = db.querySync("select * from " + common.tableName); + + assert.deepEqual(result, [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); + } + catch (e) { + console.log("Failed when committing"); + console.log(e); + + exitCode = 2; + } + + common.dropTables(db, function (err) { + db.close(function () { + process.exit(exitCode); + }); + }); + }); +}); + From 4cbb32812bbbdeeabae919fe97f31f734206efc1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 7 Apr 2013 23:35:48 -0400 Subject: [PATCH 152/511] fix throwing of error in fetchAllSync when there are no columns; ie: during 'insert into...' --- src/odbc_result.cpp | 81 +++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 5a158423..2ff32b20 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -405,51 +405,52 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { Local rows = Array::New(); - //loop through all records - while (true) { - ret = SQLFetch(self->m_hSTMT); - - //check to see if there was an error - if (ret == SQL_ERROR) { - errorCount++; + //Only loop through the recordset if there are columns + if (self->colCount > 0) { + //loop through all records + while (true) { + ret = SQLFetch(self->m_hSTMT); - objError = ODBC::GetSQLError( - self->m_hENV, - self->m_hDBC, - self->m_hSTMT, - (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll; probably" - " your query did not have a result set." - ); - - break; - } - - //check to see if we are at the end of the recordset - if (ret == SQL_NO_DATA) { - ODBC::FreeColumns(self->columns, &self->colCount); + //check to see if there was an error + if (ret == SQL_ERROR) { + errorCount++; + + objError = ODBC::GetSQLError( + self->m_hENV, + self->m_hDBC, + self->m_hSTMT, + (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll; probably" + " your query did not have a result set." + ); + + break; + } - break; - } - - rows->Set( - Integer::New(count), - ODBC::GetRecordTuple( - self->m_hSTMT, - self->columns, - &self->colCount, - self->buffer, - self->bufferLength) - ); + //check to see if we are at the end of the recordset + if (ret == SQL_NO_DATA) { + ODBC::FreeColumns(self->columns, &self->colCount); + + break; + } + + rows->Set( + Integer::New(count), + ODBC::GetRecordTuple( + self->m_hSTMT, + self->columns, + &self->colCount, + self->buffer, + self->bufferLength) + ); - count++; + count++; + } + } + else { + ODBC::FreeColumns(self->columns, &self->colCount); } - //TODO: we need to return the error object if there was an error - //however, on queries like "Drop...." the ret from SQLFetch is - //SQL_ERROR, but there is not valid error message. I guess it's because - //there is acually no result set... - // - //we will need to throw if there is a valid error. + //throw the error object if there were errors if (errorCount > 0) { ThrowException(objError); } From 2d57edcd6a3df6930f6570d1ca9b1bb04d492ed5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 7 Apr 2013 23:39:03 -0400 Subject: [PATCH 153/511] In EndTransactionSync, don't throw before resetting the conection and only throw the first error if more than one are found --- src/odbc_connection.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 579fb5ca..7a3aee90 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -1113,7 +1113,9 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { REQ_BOOL_ARG(0, rollback); + Local objError; SQLRETURN ret; + bool error = false; SQLSMALLINT completionType = (rollback->Value()) ? SQL_ROLLBACK : SQL_COMMIT @@ -1127,11 +1129,9 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { //check how the transaction went if (!SQL_SUCCEEDED(ret)) { - Local objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); - - ThrowException(objError); + error = true; - return scope.Close(False()); + objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); } //Reset the connection back to autocommit @@ -1142,13 +1142,23 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { SQL_NTS); //check how setting the connection attr went - if (!SQL_SUCCEEDED(ret)) { - Local objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); + //but only process the code if an error has not already + //occurred. If an error occurred during SQLEndTran, + //that is the error that we want to throw. + if (!SQL_SUCCEEDED(ret) && !error) { + //TODO: if this also failed, we really should + //be restarting the connection or something to deal with this state + error = true; + objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); + } + + if (error) { ThrowException(objError); return scope.Close(False()); } - - return scope.Close(True()); + else { + return scope.Close(True()); + } } \ No newline at end of file From 196c4c850ede3e815b27230e9f0c68f8128246c2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 7 Apr 2013 23:39:47 -0400 Subject: [PATCH 154/511] test: fix function names --- test/test-transaction-commit-sync.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-transaction-commit-sync.js b/test/test-transaction-commit-sync.js index fca8d43b..fe91069f 100644 --- a/test/test-transaction-commit-sync.js +++ b/test/test-transaction-commit-sync.js @@ -13,7 +13,7 @@ db.open(common.connectionString, function (err) { var results = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); - db.rollbackTransaction(); + db.rollbackTransactionSync(); results = db.querySync("select * from " + common.tableName); @@ -31,7 +31,7 @@ db.open(common.connectionString, function (err) { result = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); - db.commitTransaction(); //commit + db.commitTransactionSync(); //commit result = db.querySync("select * from " + common.tableName); From 6226eb2fc5c7c65a38f4bfeb0ab5a8820ec5eed2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 8 Apr 2013 00:02:10 -0400 Subject: [PATCH 155/511] fix returning errors when there are no columns; applies to fetch, fetchAll, fetchAllSync --- src/odbc_result.cpp | 111 +++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 2ff32b20..35f7c37e 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -181,61 +181,72 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { fetch_work_data* data = (fetch_work_data *)(work_req->data); SQLRETURN ret = data->result; + //TODO: we should probably define this on the work data so we + //don't have to keep creating it? + Local objError; + bool moreWork = true; + bool error = false; + if (data->objResult->colCount == 0) { + data->objResult->columns = ODBC::GetColumns( + data->objResult->m_hSTMT, + &data->objResult->colCount); + } + + //check to see if the result has no columns + if (data->objResult->colCount == 0) { + //this means + moreWork = false; + } //check to see if there was an error - if (ret == SQL_ERROR) { - ODBC::CallbackSQLError( + else if (ret == SQL_ERROR) { + moreWork = false; + error = true; + + objError = ODBC::GetSQLError( data->objResult->m_hENV, data->objResult->m_hDBC, data->objResult->m_hSTMT, - data->cb); - - free(data); - free(work_req); - - data->objResult->Unref(); - - return; + (char *) "Error in ODBCResult::UV_AfterFetch"); } - //check to see if we are at the end of the recordset - if (ret == SQL_NO_DATA) { - ODBC::FreeColumns(data->objResult->columns, &data->objResult->colCount); - + else if (ret == SQL_NO_DATA) { + moreWork = false; + } + + if (moreWork) { Handle args[2]; + args[0] = Null(); - args[1] = Null(); - + args[1] = ODBC::GetRecordTuple( + data->objResult->m_hSTMT, + data->objResult->columns, + &data->objResult->colCount, + data->objResult->buffer, + data->objResult->bufferLength); + data->cb->Call(Context::GetCurrent()->Global(), 2, args); data->cb.Dispose(); + } + else { + ODBC::FreeColumns(data->objResult->columns, &data->objResult->colCount); - free(data); - free(work_req); + Handle args[2]; - data->objResult->Unref(); + //if there was an error, pass that as arg[0] otherwise Null + if (error) { + args[0] = objError; + } + else { + args[0] = Null(); + } - return; - } - - if (data->objResult->colCount == 0) { - data->objResult->columns = ODBC::GetColumns( - data->objResult->m_hSTMT, - &data->objResult->colCount); + args[1] = Null(); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb.Dispose(); } - Handle args[2]; - - args[0] = Null(); - args[1] = ODBC::GetRecordTuple( - data->objResult->m_hSTMT, - data->objResult->columns, - &data->objResult->colCount, - data->objResult->buffer, - data->objResult->bufferLength); - - data->cb->Call(Context::GetCurrent()->Global(), 2, args); - data->cb.Dispose(); - free(data); free(work_req); @@ -312,8 +323,14 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { 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 - if (data->result == SQL_ERROR) { + else if (data->result == SQL_ERROR) { data->errorCount++; data->objError = Persistent::New(ODBC::GetSQLError( @@ -356,17 +373,13 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { ODBC::FreeColumns(self->columns, &self->colCount); Handle args[2]; - //TODO: we need to return the error object if there was an error - //however, on queries like "Drop...." the ret from SQLFetch is - //SQL_ERROR, but there is not valid error message. I guess it's because - //there is acually no result set... - //if (self->errorCount > 0) { - // args[0] = objError; - //} - //else { + if (data->errorCount > 0) { + args[0] = data->objError; + } + else { args[0] = Null(); - //} + } args[1] = data->rows; data->cb->Call(Context::GetCurrent()->Global(), 2, args); From 509834aec60dd54b0562e3264b6f77458269b170 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 8 Apr 2013 00:20:56 -0400 Subject: [PATCH 156/511] Add contansts to ODBC object --- src/odbc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index b99050e9..ccbd3081 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -53,6 +53,12 @@ void ODBC::Init(v8::Handle target) { Local instance_template = constructor_template->InstanceTemplate(); instance_template->SetInternalFieldCount(1); + // Constants + NODE_DEFINE_CONSTANT(constructor_template, SQL_CLOSE); + NODE_DEFINE_CONSTANT(constructor_template, SQL_DROP); + NODE_DEFINE_CONSTANT(constructor_template, SQL_UNBIND); + NODE_DEFINE_CONSTANT(constructor_template, SQL_RESET_PARAMS); + // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnection", CreateConnection); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnectionSync", CreateConnectionSync); From e648fcb0bc3eab8590a2ae314152c889abcee8e1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 8 Apr 2013 00:21:37 -0400 Subject: [PATCH 157/511] ODBCResult: instead of calling Free during closeSync; call SQLFreeStmt() with closeOptions --- src/odbc_result.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 35f7c37e..a55fa63e 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -476,9 +476,12 @@ Handle ODBCResult::CloseSync(const Arguments& args) { HandleScope scope; + OPT_INT_ARG(0, closeOption, SQL_CLOSE); + ODBCResult* result = ObjectWrap::Unwrap(args.Holder()); - result->Free(); + //result->Free(); + SQLFreeStmt(result->m_hSTMT, closeOption); return scope.Close(Undefined()); } From 673ed2b107ae06953057a7513137d1660be68c55 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 8 Apr 2013 00:27:34 -0400 Subject: [PATCH 158/511] TODO note for ODBCResult::Free --- src/odbc_result.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index a55fa63e..238a6a1d 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -69,6 +69,13 @@ void ODBCResult::Free() { if (m_hSTMT) { uv_mutex_lock(&ODBC::g_odbcMutex); + //TODO: we need to keep track of whether or not the result object + //can acutally call SQLFreeHandle which destroys the STMT handle + //the only case where it should be allowed to do this is when the + //result object was created by ODBCConnection.query* + //If there is a statement object, then that object should be used + //to destroy the handle. + //This doesn't actually deallocate the statement handle //that should not be done by the result object; that should From ccb09fb24c636b80b93aa56b7df27d0e8dc52f13 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 8 Apr 2013 00:39:46 -0400 Subject: [PATCH 159/511] go back to using result->Free() during closeSync; temporarily --- src/odbc_result.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 238a6a1d..ddd452d2 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -486,9 +486,11 @@ Handle ODBCResult::CloseSync(const Arguments& args) { OPT_INT_ARG(0, closeOption, SQL_CLOSE); ODBCResult* result = ObjectWrap::Unwrap(args.Holder()); - - //result->Free(); - SQLFreeStmt(result->m_hSTMT, closeOption); + + //TODO: undoing this change for now util this logic is all + //worked out. + //SQLFreeStmt(result->m_hSTMT, closeOption); + result->Free(); return scope.Close(Undefined()); } From b3ae2373168e0666d2726fc935b240ba51c729b9 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 12 Apr 2013 13:03:21 -0400 Subject: [PATCH 160/511] 0.5.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c729c7c..f7c556ae 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.1", + "version": "0.5.2", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From e86f2d899e619041fb032905816e6c98c8e56782 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 12 Apr 2013 17:42:48 -0400 Subject: [PATCH 161/511] remove accidentally commited journal --- test/data/sqlite-test.db-journal | Bin 2576 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/data/sqlite-test.db-journal diff --git a/test/data/sqlite-test.db-journal b/test/data/sqlite-test.db-journal deleted file mode 100644 index efaf781a018596e8a5edf15001e1517ea903f609..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2576 zcmZQzKmhmcEmc4s6BseDAS6av!#M<)ctO4!jqc$b$D@854FQ}X04?_!g9CjsOHvim z@{4j4OB9TOMFJxugM)$sP@@451M3AiPzJIX;XG6}k}wL>fI;_>BB-okWG-R=qERp! z0wX*G8o3$S#Z65a8!bx`lX6o1{9Rn*{au`#<3n77L*hdmoqSxei7`0`xjKfpDu5Lz vU{j%>;q33@=@+5^#IEkHK{^URz6(%Kh^Mcs0*nC`4RMVKQ2-H|pmG2J6@)d@ From 97c735ea866c393db95743b045d0392029211484 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 12 Apr 2013 17:43:39 -0400 Subject: [PATCH 162/511] binding.gyp formatting --- binding.gyp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/binding.gyp b/binding.gyp index f1c874b9..612c5ec7 100644 --- a/binding.gyp +++ b/binding.gyp @@ -9,17 +9,16 @@ 'src/odbc_result.cpp' ], 'defines' : [ - ], 'conditions' : [ [ 'OS == "linux"', { - 'libraries' : [ - '-lodbc' - ], - 'cflags' : [ - '-g' - ] - }], + 'libraries' : [ + '-lodbc' + ], + 'cflags' : [ + '-g' + ] + }], [ 'OS == "mac"', { 'libraries' : [ '-lodbc' From 1daa17ab0a350679216df41e15d86750bf25842a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 12 Apr 2013 17:46:57 -0400 Subject: [PATCH 163/511] keep track of result objects which can or can not free their handle --- src/odbc.cpp | 1 + src/odbc.h | 4 +-- src/odbc_connection.cpp | 16 +++++++--- src/odbc_result.cpp | 65 ++++++++++++++++++++++++----------------- src/odbc_result.h | 7 +++-- src/odbc_statement.cpp | 20 ++++++++----- 6 files changed, 71 insertions(+), 42 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index ccbd3081..a41e7c74 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -58,6 +58,7 @@ void ODBC::Init(v8::Handle target) { NODE_DEFINE_CONSTANT(constructor_template, SQL_DROP); NODE_DEFINE_CONSTANT(constructor_template, SQL_UNBIND); NODE_DEFINE_CONSTANT(constructor_template, SQL_RESET_PARAMS); + NODE_DEFINE_CONSTANT(constructor_template, SQL_DESTROY); //SQL_DESTROY is non-standard // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnection", CreateConnection); diff --git a/src/odbc.h b/src/odbc.h index 19b274bf..a7a92284 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -34,6 +34,7 @@ using namespace node; #define MODE_COLLECT_AND_CALLBACK 1 #define MODE_CALLBACK_FOR_EACH 2 +#define SQL_DESTROY 9999 typedef struct { unsigned char *name; @@ -116,9 +117,6 @@ struct close_request { int result; }; - - - struct query_request { Persistent cb; ODBC *dbo; diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 7a3aee90..1d5c0e1c 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -723,12 +723,16 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { data->cb); } else { - Local args[3]; + Local args[4]; + bool canFreeHandle = true; + args[0] = External::New(data->conn->m_hENV); args[1] = External::New(data->conn->m_hDBC); args[2] = External::New(data->hSTMT); + args[3] = External::New(&canFreeHandle); + Persistent js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(3, args)); + GetFunction()->NewInstance(4, args)); args[0] = Local::New(Null()); args[1] = Local::New(js_result); @@ -880,12 +884,16 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { return scope.Close(Undefined()); } else { - Local args[3]; + Local args[4]; + bool canFreeHandle; + args[0] = External::New(conn->m_hENV); args[1] = External::New(conn->m_hDBC); args[2] = External::New(hSTMT); + args[3] = External::New(&canFreeHandle); + Persistent js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(3, args)); + GetFunction()->NewInstance(4, args)); return scope.Close(js_result); } diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index ddd452d2..0755c95e 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -65,32 +65,22 @@ ODBCResult::~ODBCResult() { } void ODBCResult::Free() { - DEBUG_PRINTF("ODBCResult::Free m_hSTMT=%X\n", m_hSTMT); + DEBUG_PRINTF("ODBCResult::Free m_hSTMT=%X m_canFreeHandle=%X\n", m_hSTMT, m_canFreeHandle); - if (m_hSTMT) { + if (m_hSTMT && m_canFreeHandle) { uv_mutex_lock(&ODBC::g_odbcMutex); - //TODO: we need to keep track of whether or not the result object - //can acutally call SQLFreeHandle which destroys the STMT handle - //the only case where it should be allowed to do this is when the - //result object was created by ODBCConnection.query* - //If there is a statement object, then that object should be used - //to destroy the handle. - - - //This doesn't actually deallocate the statement handle - //that should not be done by the result object; that should - //be done by the statement object - //SQLFreeStmt(m_hSTMT, SQL_CLOSE); SQLFreeHandle( SQL_HANDLE_STMT, m_hSTMT); m_hSTMT = NULL; - + uv_mutex_unlock(&ODBC::g_odbcMutex); + } + + if (bufferLength > 0) { + free(buffer); - if (bufferLength > 0) { - free(buffer); - } + bufferLength = 0; } } @@ -102,18 +92,21 @@ Handle ODBCResult::New(const Arguments& args) { 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(js_henv->Value()); HDBC hDBC = static_cast(js_hdbc->Value()); HSTMT hSTMT = static_cast(js_hstmt->Value()); + bool canFreeHandle = static_cast(js_hstmt->Value()); //create a new OBCResult object - ODBCResult* objODBCResult = new ODBCResult(hENV, hDBC, hSTMT); + ODBCResult* objODBCResult = new ODBCResult(hENV, hDBC, hSTMT, canFreeHandle); - DEBUG_PRINTF("ODBCResult::New m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + 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_hSTMT, + objODBCResult->m_canFreeHandle ); //specify the buffer length @@ -478,19 +471,39 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { return scope.Close(rows); } +/* + * CloseSync + * + * When calling a Close method, + */ + Handle ODBCResult::CloseSync(const Arguments& args) { DEBUG_PRINTF("ODBCResult::Close\n"); HandleScope scope; - OPT_INT_ARG(0, closeOption, SQL_CLOSE); + OPT_INT_ARG(0, closeOption, SQL_DESTROY); ODBCResult* result = ObjectWrap::Unwrap(args.Holder()); - //TODO: undoing this change for now util this logic is all - //worked out. - //SQLFreeStmt(result->m_hSTMT, closeOption); - result->Free(); + 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(Undefined()); } diff --git a/src/odbc_result.h b/src/odbc_result.h index 9007f39d..012c3613 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -27,11 +27,12 @@ class ODBCResult : public node::ObjectWrap { protected: ODBCResult() {}; - explicit ODBCResult(HENV hENV, HDBC hDBC, HSTMT hSTMT): + explicit ODBCResult(HENV hENV, HDBC hDBC, HSTMT hSTMT, bool canFreeHandle): ObjectWrap(), m_hENV(hENV), m_hDBC(hDBC), - m_hSTMT(hSTMT) {}; + m_hSTMT(hSTMT), + m_canFreeHandle(canFreeHandle) {}; ~ODBCResult(); @@ -69,6 +70,8 @@ class ODBCResult : public node::ObjectWrap { HENV m_hENV; HDBC m_hDBC; HSTMT m_hSTMT; + bool m_canFreeHandle; + uint16_t *buffer; int bufferLength; Column *columns; diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index a0bbc724..dff57790 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -95,19 +95,19 @@ Handle ODBCStatement::New(const Arguments& args) { HSTMT hSTMT = static_cast(js_hstmt->Value()); //create a new OBCResult object - ODBCStatement* objODBCResult = new ODBCStatement(hENV, hDBC, hSTMT); + ODBCStatement* stmt = new ODBCStatement(hENV, hDBC, hSTMT); //specify the buffer length - objODBCResult->bufferLength = MAX_VALUE_SIZE - 1; + stmt->bufferLength = MAX_VALUE_SIZE - 1; //initialze a buffer for this object - objODBCResult->buffer = (uint16_t *) malloc(objODBCResult->bufferLength + 1); + stmt->buffer = (uint16_t *) malloc(stmt->bufferLength + 1); //TODO: make sure the malloc succeeded //set the initial colCount to 0 - objODBCResult->colCount = 0; + stmt->colCount = 0; - objODBCResult->Wrap(args.Holder()); + stmt->Wrap(args.Holder()); return scope.Close(args.Holder()); } @@ -179,12 +179,15 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { } else { Local args[3]; + bool canFreeHandle; + args[0] = External::New(self->m_hENV); args[1] = External::New(self->m_hDBC); args[2] = External::New(self->m_hSTMT); + args[3] = External::New(&canFreeHandle); Persistent js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(3, args)); + GetFunction()->NewInstance(4, args)); args[0] = Local::New(Null()); args[1] = Local::New(js_result); @@ -302,12 +305,15 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { } else { Local args[3]; + bool canFreeHandle = false; + args[0] = External::New(self->m_hENV); args[1] = External::New(self->m_hDBC); args[2] = External::New(self->m_hSTMT); + args[3] = External::New(&canFreeHandle); Persistent js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(3, args)); + GetFunction()->NewInstance(4, args)); args[0] = Local::New(Null()); args[1] = Local::New(js_result); From 4ca0f8dde28250caac8d4c00658d96e446f0a86f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 12 Apr 2013 19:36:19 -0400 Subject: [PATCH 164/511] Memory leak fixes --- src/odbc.cpp | 4 ++-- src/odbc.h | 2 +- src/odbc_connection.cpp | 32 +++++++++++++++++++------------- src/odbc_result.cpp | 10 ++++++---- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index a41e7c74..1b130d34 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -456,7 +456,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, * GetRecordTuple */ -Handle ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, +Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { HandleScope scope; @@ -500,7 +500,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { DEBUG_PRINTF("ODBC::GetParametersFromArray\n"); *paramCount = values->Length(); - Parameter * params = new Parameter[*paramCount]; + Parameter* params = (Parameter *) malloc(*paramCount * sizeof(Parameter)); for (int i = 0; i < *paramCount; i++) { Local value = values->Get(i); diff --git a/src/odbc.h b/src/odbc.h index a7a92284..39f43ca5 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -63,7 +63,7 @@ class ODBC : public node::ObjectWrap { static Column* GetColumns(SQLHSTMT hStmt, short* colCount); static void FreeColumns(Column* columns, short* colCount); static Handle GetColumnValue(SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength); - static Handle GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); + static Local GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle CallbackSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, Persistent cb); static Local GetSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, char* message); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 1d5c0e1c..2c79d245 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -688,19 +688,6 @@ void ODBCConnection::UV_Query(uv_work_t* req) { ret = SQLExecute(data->hSTMT); } } - - // free parameters - for (int i = 0; i < data->paramCount; i++) { - if (prm = data->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_LONG: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; - } - } - } - free(data->params); } // this will be checked later in UV_AfterQuery @@ -749,6 +736,23 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { } data->cb.Dispose(); + + if (data->paramCount) { + Parameter prm; + // free parameters + for (int i = 0; i < data->paramCount; i++) { + if (prm = data->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_LONG: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + free(data->params); + } free(data->sql); free(data->catalog); @@ -870,6 +874,8 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { free(params); } + free(sqlString); + //check to see if there was an error during execution if(ret == SQL_ERROR) { objError = ODBC::GetSQLError( diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 0755c95e..42aca4c8 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -78,9 +78,8 @@ void ODBCResult::Free() { } if (bufferLength > 0) { - free(buffer); - bufferLength = 0; + free(buffer); } } @@ -375,15 +374,18 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { Handle args[2]; if (data->errorCount > 0) { - args[0] = data->objError; + args[0] = Local::New(data->objError); } else { args[0] = Null(); } - args[1] = data->rows; + + args[1] = Local::New(data->rows); data->cb->Call(Context::GetCurrent()->Global(), 2, args); data->cb.Dispose(); + data->rows.Dispose(); + data->objError.Dispose(); //TODO: Do we need to free self->rows somehow? free(data); From c3a8f526e40d2ec938aec127dc05dd5bd79d378a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 16 Apr 2013 18:39:29 -0400 Subject: [PATCH 165/511] Add prepare() method This is a shortcut to creating a new statement and then preparing the statement with a SQL string. An ODBCStatement object is returned which can then be used to .bind([values]) to the prepared statement and then .execute(function (err, result)) to actually execute the statement. The .execute() method requires a callback which takes an err and result argument. The result object is an ODBCResult which can be used to .fetchAll(function (err, data)) or .fetch(function (err, obj)) --- lib/odbc.js | 72 ++++++++++++++++++++++++++++++++++++++++++++ test/test-prepare.js | 35 +++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 test/test-prepare.js diff --git a/lib/odbc.js b/lib/odbc.js index 495d7741..316c3eb9 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -17,6 +17,7 @@ var odbc = require("bindings")("odbc_bindings") , SimpleQueue = require("./simple-queue") + , util = require("util") ; module.exports = function (options) { @@ -288,6 +289,77 @@ Database.prototype.describe = function(obj, callback) { } }; +Database.prototype.prepare = function (sql, cb) { + var self = this; + + self.conn.createStatement(function (err, stmt) { + if (err) return cb(err); + + stmt.queue = new SimpleQueue(); + + stmt.prepare(sql, function (err) { + if (err) return cb(err); + + return cb(null, stmt); + }); + }); +} + +//Proxy all of the asynchronous functions so that they are queued +odbc.ODBCStatement.prototype._execute = odbc.ODBCStatement.prototype.execute; +odbc.ODBCStatement.prototype._executeDirect = odbc.ODBCStatement.prototype.executeDirect; +odbc.ODBCStatement.prototype._prepare = odbc.ODBCStatement.prototype.prepare; +odbc.ODBCStatement.prototype._bind = odbc.ODBCStatement.prototype.bind; + +odbc.ODBCStatement.prototype.execute = function (cb) { + var self = this; + + self.queue.push(function (next) { + self._execute(function (err, result) { + cb(err, result); + + return next(); + }); + }); +}; + +odbc.ODBCStatement.prototype.executeDirect = function (sql, cb) { + var self = this; + + self.queue.push(function (next) { + self._executeDirect(sql, function (err, result) { + cb(err, result); + + return next(); + }); + }); +}; + +odbc.ODBCStatement.prototype.prepare = function (sql, cb) { + var self = this; + + self.queue.push(function (next) { + self._prepare(sql, function (err) { + cb(err); + + return next(); + }); + }); +}; + +odbc.ODBCStatement.prototype.bind = function (ary, cb) { + var self = this; + + self.queue.push(function (next) { + self._bind(ary, function (err) { + cb(err); + + return next(); + }); + }); +}; + + module.exports.Pool = Pool; Pool.count = 0; diff --git a/test/test-prepare.js b/test/test-prepare.js new file mode 100644 index 00000000..0ea0294d --- /dev/null +++ b/test/test-prepare.js @@ -0,0 +1,35 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + db.prepare("select ? as col1", function (err, stmt) { + assert.equal(err, null); + assert.equal(stmt.constructor.name, "ODBCStatement"); + + stmt.bind(["hello world"], function (err) { + assert.equal(err, null); + + stmt.execute(function (err, result) { + assert.equal(err, null); + assert.equal(result.constructor.name, "ODBCResult"); + + result.fetchAll(function (err, data) { + assert.equal(err, null); + console.log(data); + + result.closeSync(); + + db.close(function () { + assert.deepEqual(data, [{ col1: "hello world" }]); + }); + }); + }); + }); + }); +}); From 29e9c153d37d05d50dbc1f33426e485a6b9bfaec Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 16 Apr 2013 19:05:00 -0400 Subject: [PATCH 166/511] Add .prepareSync() --- lib/odbc.js | 13 +++++++++++++ test/test-prepareSync.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 test/test-prepareSync.js diff --git a/lib/odbc.js b/lib/odbc.js index 316c3eb9..61faf46d 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -305,6 +305,18 @@ Database.prototype.prepare = function (sql, cb) { }); } +Database.prototype.prepareSync = function (sql, cb) { + var self = this; + + var stmt = self.conn.createStatementSync(); + + stmt.queue = new SimpleQueue(); + + stmt.prepareSync(sql); + + return stmt; +} + //Proxy all of the asynchronous functions so that they are queued odbc.ODBCStatement.prototype._execute = odbc.ODBCStatement.prototype.execute; odbc.ODBCStatement.prototype._executeDirect = odbc.ODBCStatement.prototype.executeDirect; @@ -360,6 +372,7 @@ odbc.ODBCStatement.prototype.bind = function (ary, cb) { }; + module.exports.Pool = Pool; Pool.count = 0; diff --git a/test/test-prepareSync.js b/test/test-prepareSync.js new file mode 100644 index 00000000..0fd6210a --- /dev/null +++ b/test/test-prepareSync.js @@ -0,0 +1,31 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + var stmt = db.prepareSync("select ? as col1, ? as col2, ? as col3"); + assert.equal(stmt.constructor.name, "ODBCStatement"); + + stmt.bindSync(["hello world", 1, null]); + + stmt.execute(function (err, result) { + assert.equal(err, null); + assert.equal(result.constructor.name, "ODBCResult"); + + result.fetchAll(function (err, data) { + assert.equal(err, null); + console.log(data); + + result.closeSync(); + + db.close(function () { + assert.deepEqual(data, [{ col1: "hello world", col2 : 1, col3 : null }]); + }); + }); + }); +}); From e5cb4121dcc5786da3bbd1889183497843faba4f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 19 Apr 2013 18:40:12 -0400 Subject: [PATCH 167/511] fix setting of canFreeHandle --- src/odbc_result.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 42aca4c8..cd42bbad 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -96,7 +96,7 @@ Handle ODBCResult::New(const Arguments& args) { HENV hENV = static_cast(js_henv->Value()); HDBC hDBC = static_cast(js_hdbc->Value()); HSTMT hSTMT = static_cast(js_hstmt->Value()); - bool canFreeHandle = static_cast(js_hstmt->Value()); + bool canFreeHandle = static_cast(js_canFreeHandle->Value()); //create a new OBCResult object ODBCResult* objODBCResult = new ODBCResult(hENV, hDBC, hSTMT, canFreeHandle); From 02bfd52ffd8ee7bc999c2950eed615395c62f017 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 19 Apr 2013 18:40:57 -0400 Subject: [PATCH 168/511] Add FETCH_ARRAY and FETCH_OBJECT constants --- src/odbc.cpp | 15 ++++++++++++++- src/odbc.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 1b130d34..bafecfee 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -59,6 +59,8 @@ void ODBC::Init(v8::Handle target) { NODE_DEFINE_CONSTANT(constructor_template, SQL_UNBIND); NODE_DEFINE_CONSTANT(constructor_template, SQL_RESET_PARAMS); NODE_DEFINE_CONSTANT(constructor_template, SQL_DESTROY); //SQL_DESTROY is non-standard + NODE_DEFINE_CONSTANT(constructor_template, FETCH_ARRAY); + NODE_DEFINE_CONSTANT(constructor_template, FETCH_OBJECT); // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnection", CreateConnection); @@ -120,6 +122,9 @@ Handle ODBC::New(const Arguments& args) { int ret = SQLAllocEnv( &dbo->m_hEnv ); //TODO: check if ret succeeded, if not, throw error to javascript land + if (!SQL_SUCCEEDED(ret)) { + //TODO: do something. + } return scope.Close(args.Holder()); } @@ -171,6 +176,10 @@ void ODBC::UV_CreateConnection(uv_work_t* req) { //allocate a new connection handle int ret = SQLAllocConnect(data->dbo->m_hEnv, &data->hDBC); + if (!SQL_SUCCEEDED(ret)) { + //TODO: do something. + } + uv_mutex_unlock(&ODBC::g_odbcMutex); } @@ -217,7 +226,11 @@ Handle ODBC::CreateConnectionSync(const Arguments& args) { //allocate a new connection handle SQLRETURN ret = SQLAllocConnect(dbo->m_hEnv, &hDBC); - + + if (!SQL_SUCCEEDED(ret)) { + //TODO: do something! + } + uv_mutex_unlock(&ODBC::g_odbcMutex); Local params[2]; diff --git a/src/odbc.h b/src/odbc.h index 39f43ca5..11685f88 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -34,6 +34,8 @@ using namespace node; #define MODE_COLLECT_AND_CALLBACK 1 #define MODE_CALLBACK_FOR_EACH 2 +#define FETCH_ARRAY 3 +#define FETCH_OBJECT 4 #define SQL_DESTROY 9999 typedef struct { From 06c58423a8eab72c9c00bb13d8ab30d0eea55cfa Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 19 Apr 2013 18:42:41 -0400 Subject: [PATCH 169/511] ODBCConnection: accept Object as an argument to query functions and implment noResults flag to not create a result object --- src/odbc_connection.cpp | 236 ++++++++++++++++++++++++++++++++-------- src/odbc_connection.h | 5 + 2 files changed, 195 insertions(+), 46 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 2c79d245..1e0e5fe0 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -31,6 +31,9 @@ using namespace v8; using namespace node; Persistent ODBCConnection::constructor_template; +Persistent ODBCConnection::OPTION_SQL = Persistent::New(String::New("sql")); +Persistent ODBCConnection::OPTION_PARAMS = Persistent::New(String::New("params")); +Persistent ODBCConnection::OPTION_NORESULTS = Persistent::New(String::New("noResults")); void ODBCConnection::Init(v8::Handle target) { DEBUG_PRINTF("ODBCConnection::Init\n"); @@ -566,52 +569,108 @@ Handle ODBCConnection::Query(const Arguments& args) { DEBUG_PRINTF("ODBCConnection::Query\n"); HandleScope scope; - - REQ_STR_ARG(0, sql); - Local cb; - + Local cb; + + String::Utf8Value* sql; + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); query_work_data* data = (query_work_data *) calloc(1, sizeof(query_work_data)); - // populate data->params if parameters were supplied - if (args.Length() > 2) { - if ( !args[1]->IsArray() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 1 must be an Array")) - ); + //Check arguments for different variations of calling this function + if (args.Length() == 3) { + //handle Query("sql string", [params], function cb () {}); + + if ( !args[0]->IsString() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 0 must be an String.") + )); + } + else if ( !args[1]->IsArray() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 1 must be an Array.") + )); + } + else if ( !args[2]->IsFunction() ) { + return ThrowException(Exception::TypeError( + String::New("Argument 2 must be a Function.") + )); + } + + sql = new String::Utf8Value(args[0]->ToString()); + + data->params = ODBC::GetParametersFromArray( + Local::Cast(args[1]), + &data->paramCount); + + cb = Local::Cast(args[2]); + } + else if (args.Length() == 2 ) { + //handle either Query("sql", cb) or Query({ settings }, cb) + + if (!args[2]->IsFunction()) { + return ThrowException(Exception::TypeError( + String::New("ODBCConnection::Query(): Argument 1 must be a Function.")) + ); + } + + cb = Local::Cast(args[1]); + + if (args[0]->IsString()) { + //handle Query("sql", function cb () {}) + + sql = new String::Utf8Value(args[0]->ToString()); + + data->paramCount = 0; + } + else if (args[0]->IsObject()) { + //NOTE: going forward this is the way we should expand options + //rather than adding more arguments to the function signature. + //specify options on an options object. + //handle Query({}, function cb () {}); + + Local obj = args[0]->ToObject(); + + if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { + sql = new String::Utf8Value(obj->Get(OPTION_SQL)->ToString()); } - else if ( !args[2]->IsFunction() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 2 must be a Function")) - ); + + if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { + data->params = ODBC::GetParametersFromArray( + Local::Cast(obj->Get(OPTION_PARAMS)), + &data->paramCount); + } + else { + data->paramCount = 0; } - - cb = Local::Cast(args[2]); - data->params = ODBC::GetParametersFromArray( - Local::Cast(args[1]), - &data->paramCount); + if (obj->Has(OPTION_NORESULTS) && obj->Get(OPTION_NORESULTS)->IsBoolean()) { + data->noResultObject = obj->Get(OPTION_NORESULTS)->ToBoolean()->Value(); + } + else { + data->noResultObject = false; + } + } + else { + return ThrowException(Exception::TypeError( + String::New("ODBCConnection::Query(): Argument 0 must be a String or an Object.")) + ); + } } else { - if ( !args[1]->IsFunction() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 1 must be a Function")) - ); - } - - cb = Local::Cast(args[1]); - - data->paramCount = 0; + return ThrowException(Exception::TypeError( + String::New("ODBCConnection::Query(): Requires either 2 or 3 Arguments. ")) + ); } + //Done checking arguments - data->sql = (char *) malloc(sql.length() +1); + data->sql = (char *) malloc(sql->length() +1); data->cb = Persistent::New(cb); - strcpy(data->sql, *sql); + strcpy(data->sql, **sql); data->conn = conn; work_req->data = data; @@ -709,6 +768,23 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { data->hSTMT, data->cb); } + else if (data->noResultObject) { + //We have been requested to not create a result object + //this means we should release the handle now and call back + //with True() + + uv_mutex_lock(&ODBC::g_odbcMutex); + + SQLFreeHandle(SQL_HANDLE_STMT, data->hSTMT); + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + Local args[2]; + args[0] = Local::New(Null()); + args[1] = Local::New(True()); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + } else { Local args[4]; bool canFreeHandle = true; @@ -776,34 +852,91 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { HandleScope scope; - REQ_STR_ARG(0, sql); + String::Utf8Value* sql; ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); Local objError = Object::New(); - Parameter* params; + Parameter* params = new Parameter[0]; Parameter prm; SQLRETURN ret; HSTMT hSTMT; int paramCount = 0; char* sqlString; + bool noResultObject = false; - // populate params if parameters were supplied - if (args.Length() > 1) { - if ( !args[1]->IsArray() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 1 must be an Array")) - ); + //Check arguments for different variations of calling this function + if (args.Length() == 2) { + //handle QuerySync("sql string", [params]); + + if ( !args[0]->IsString() ) { + return ThrowException(Exception::TypeError( + String::New("ODBCConnection::QuerySync(): Argument 0 must be an String.") + )); + } + else if (!args[1]->IsArray()) { + return ThrowException(Exception::TypeError( + String::New("ODBCConnection::QuerySync(): Argument 1 must be an Array.") + )); + } + + sql = new String::Utf8Value(args[0]->ToString()); + + params = ODBC::GetParametersFromArray( + Local::Cast(args[1]), + ¶mCount); + + } + else if (args.Length() == 1 ) { + //handle either QuerySync("sql") or QuerySync({ settings }) + + if (args[0]->IsString()) { + //handle Query("sql") + sql = new String::Utf8Value(args[0]->ToString()); + + paramCount = 0; + } + else if (args[0]->IsObject()) { + //NOTE: going forward this is the way we should expand options + //rather than adding more arguments to the function signature. + //specify options on an options object. + //handle Query({}, function cb () {}); + + Local obj = args[0]->ToObject(); + + if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { + sql = new String::Utf8Value(obj->Get(OPTION_SQL)->ToString()); } - - params = ODBC::GetParametersFromArray( - Local::Cast(args[1]), - ¶mCount); + + if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { + params = ODBC::GetParametersFromArray( + Local::Cast(obj->Get(OPTION_PARAMS)), + ¶mCount); + } + else { + paramCount = 0; + } + + if (obj->Has(OPTION_NORESULTS) && obj->Get(OPTION_NORESULTS)->IsBoolean()) { + noResultObject = obj->Get(OPTION_NORESULTS)->ToBoolean()->Value(); + } + } + else { + return ThrowException(Exception::TypeError( + String::New("ODBCConnection::QuerySync(): Argument 0 must be a String or an Object.")) + ); + } + } + else { + return ThrowException(Exception::TypeError( + String::New("ODBCConnection::QuerySync(): Requires either 1 or 2 Arguments. ")) + ); } + //Done checking arguments - sqlString = (char *) malloc(sql.length() +1); + sqlString = (char *) malloc(sql->length() +1); - strcpy(sqlString, *sql); + strcpy(sqlString, **sql); uv_mutex_lock(&ODBC::g_odbcMutex); @@ -839,7 +972,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { prm.buffer_length, prm.size, prm.length, ¶ms[i].length); ret = SQLBindParameter( - hSTMT, //StatementHandle + hSTMT, //StatementHandle i + 1, //ParameterNumber SQL_PARAM_INPUT, //InputOutputType prm.c_type, //ValueType @@ -849,7 +982,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { prm.buffer, //ParameterValuePtr prm.buffer_length, //BufferLength //using &prm.length did not work here... - ¶ms[i].length); //StrLen_or_IndPtr + ¶ms[i].length); //StrLen_or_IndPtr if (ret == SQL_ERROR) {break;} } @@ -889,6 +1022,17 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { return scope.Close(Undefined()); } + else if (noResultObject) { + //if there is not result object requested then + //we must destroy the STMT ourselves. + uv_mutex_lock(&ODBC::g_odbcMutex); + + SQLFreeHandle(SQL_HANDLE_STMT, hSTMT); + + uv_mutex_unlock(&ODBC::g_odbcMutex); + + return scope.Close(True()); + } else { Local args[4]; bool canFreeHandle; diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 27cacbc0..9adce20d 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -20,7 +20,11 @@ class ODBCConnection : public node::ObjectWrap { public: + static Persistent OPTION_SQL; + static Persistent OPTION_PARAMS; + static Persistent OPTION_NORESULTS; static Persistent constructor_template; + static void Init(v8::Handle target); void Free(); @@ -109,6 +113,7 @@ struct query_work_data { Parameter *params; int paramCount; + bool noResultObject; char *sql; char *catalog; From dc6b8be5d8ca49e4b3129084da66e4b26599fefb Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 19 Apr 2013 18:43:15 -0400 Subject: [PATCH 170/511] ODBCStatement: initialize return values to supress compiler warnings --- src/odbc_statement.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index dff57790..9cc4eaff 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -508,7 +508,7 @@ Handle ODBCStatement::BindSync(const Arguments& args) { Local::Cast(args[0]), &stmt->paramCount); - SQLRETURN ret; + SQLRETURN ret = SQL_SUCCESS; Parameter prm; for (int i = 0; i < stmt->paramCount; i++) { @@ -613,7 +613,7 @@ void ODBCStatement::UV_Bind(uv_work_t* req) { data->stmt->m_hSTMT ); - SQLRETURN ret; + SQLRETURN ret = SQL_SUCCESS; Parameter prm; for (int i = 0; i < data->stmt->paramCount; i++) { From 62a838ed6ebe2769638e8ce08d4d9b2105e5f9ea Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 19 Apr 2013 18:43:58 -0400 Subject: [PATCH 171/511] ODBCResult: implement fetchMode option for fetch*() functions --- src/odbc_result.cpp | 146 ++++++++++++++++++++++++++++++++------------ src/odbc_result.h | 2 + 2 files changed, 110 insertions(+), 38 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index cd42bbad..c7ff7caf 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -30,6 +30,7 @@ using namespace v8; using namespace node; Persistent ODBCResult::constructor_template; +Persistent ODBCResult::OPTION_FETCH_MODE = Persistent::New(String::New("fetchMode")); void ODBCResult::Init(v8::Handle target) { DEBUG_PRINTF("ODBCResult::Init\n"); @@ -140,14 +141,27 @@ Handle ODBCResult::Fetch(const Arguments& args) { Local cb; - if (args.Length() == 0 || !args[0]->IsFunction()) { + if (args.Length() == 1 && args[0]->IsFunction()) { + cb = Local::Cast(args[0]); + } + else if (args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction()) { + cb = Local::Cast(args[1]); + + Local 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 { + data->fetchMode = FETCH_OBJECT; + } + } + else { return ThrowException(Exception::TypeError( - String::New("Argument 0 must be a callback function.")) - ); + String::New("ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function.") + )); } - cb = Local::Cast(args[0]); - data->cb = Persistent::New(cb); data->objResult = objODBCResult; @@ -217,13 +231,23 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { Handle args[2]; args[0] = Null(); - args[1] = ODBC::GetRecordTuple( - data->objResult->m_hSTMT, - data->objResult->columns, - &data->objResult->colCount, - data->objResult->buffer, - data->objResult->bufferLength); - + if (data->fetchMode == FETCH_ARRAY) { + args[1] = ODBC::GetRecordArray( + data->objResult->m_hSTMT, + data->objResult->columns, + &data->objResult->colCount, + data->objResult->buffer, + data->objResult->bufferLength); + } + else { + args[1] = ODBC::GetRecordTuple( + data->objResult->m_hSTMT, + data->objResult->columns, + &data->objResult->colCount, + data->objResult->buffer, + data->objResult->bufferLength); + } + data->cb->Call(Context::GetCurrent()->Global(), 2, args); data->cb.Dispose(); } @@ -271,14 +295,28 @@ Handle ODBCResult::FetchAll(const Arguments& args) { Local cb; - if (args.Length() == 0 || !args[0]->IsFunction()) { + if (args.Length() == 1 && args[0]->IsFunction()) { + cb = Local::Cast(args[0]); + data->fetchMode = FETCH_OBJECT; + } + else if (args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction()) { + cb = Local::Cast(args[1]); + + Local 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 { + data->fetchMode = FETCH_OBJECT; + } + } + else { return ThrowException(Exception::TypeError( - String::New("Argument 0 must be a callback function.")) - ); + String::New("ODBCResult::FetchAll(): 1 or 2 arguments are required. The last argument must be a callback function.") + )); } - cb = Local::Cast(args[0]); - data->rows = Persistent::New(Array::New()); data->errorCount = 0; data->count = 0; @@ -346,17 +384,28 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { doMoreWork = false; } else { - - data->rows->Set( - Integer::New(data->count), - ODBC::GetRecordTuple( - self->m_hSTMT, - self->columns, - &self->colCount, - self->buffer, - self->bufferLength) - ); - + 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++; } @@ -413,6 +462,15 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { SQLRETURN ret; int count = 0; int errorCount = 0; + int fetchMode = FETCH_OBJECT; + + if (args.Length() == 1 && args[0]->IsObject()) { + Local obj = args[0]->ToObject(); + + if (obj->Has(OPTION_FETCH_MODE) && obj->Get(OPTION_FETCH_MODE)->IsInt32()) { + fetchMode = obj->Get(OPTION_FETCH_MODE)->ToInt32()->Value(); + } + } if (self->colCount == 0) { self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); @@ -448,16 +506,28 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { break; } - rows->Set( - Integer::New(count), - ODBC::GetRecordTuple( - self->m_hSTMT, - self->columns, - &self->colCount, - self->buffer, - self->bufferLength) - ); - + if (fetchMode == FETCH_ARRAY) { + rows->Set( + Integer::New(count), + ODBC::GetRecordArray( + self->m_hSTMT, + self->columns, + &self->colCount, + self->buffer, + self->bufferLength) + ); + } + else { + rows->Set( + Integer::New(count), + ODBC::GetRecordTuple( + self->m_hSTMT, + self->columns, + &self->colCount, + self->buffer, + self->bufferLength) + ); + } count++; } } diff --git a/src/odbc_result.h b/src/odbc_result.h index 012c3613..50711de0 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -19,6 +19,7 @@ class ODBCResult : public node::ObjectWrap { public: + static Persistent OPTION_FETCH_MODE; static Persistent constructor_template; static void Init(v8::Handle target); @@ -58,6 +59,7 @@ class ODBCResult : public node::ObjectWrap { ODBCResult *objResult; SQLRETURN result; + int fetchMode; int count; int errorCount; Persistent rows; From 0bf323a3270b5c05be6683d45def6735e6277076 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 19 Apr 2013 19:10:27 -0400 Subject: [PATCH 172/511] ODBCConnection: fix function arg index expectation --- src/odbc_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 1e0e5fe0..b4a259eb 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -611,7 +611,7 @@ Handle ODBCConnection::Query(const Arguments& args) { else if (args.Length() == 2 ) { //handle either Query("sql", cb) or Query({ settings }, cb) - if (!args[2]->IsFunction()) { + if (!args[1]->IsFunction()) { return ThrowException(Exception::TypeError( String::New("ODBCConnection::Query(): Argument 1 must be a Function.")) ); From 4c9d7387a4c7c077bfbd95cfc409a512f23b7e4a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 19 Apr 2013 19:11:03 -0400 Subject: [PATCH 173/511] optimize call to ODBCConnection::Query to make use of avoiding processing params --- lib/odbc.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 61faf46d..da6e138d 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -84,7 +84,7 @@ Database.prototype.query = function (sql, params, cb) { if (typeof(params) == 'function') { cb = params; - params = []; + params = null; } if (!self.connected) { @@ -93,7 +93,7 @@ Database.prototype.query = function (sql, params, cb) { self.queue.push(function (next) { //ODBCConnection.query() is the fastest-path querying mechanism. - self.conn.query(sql, params, function (err, result) { + var cbActual = function (err, result) { if (err) { cb(err, [], false); @@ -119,7 +119,14 @@ Database.prototype.query = function (sql, params, cb) { } }); } - }); + } + + if (params) { + self.conn.query(sql, params, cbActual); + } + else { + self.conn.query(sql, cbActual); + } }); }; From 950d732ea693e2dea0380c6676003e11ee264ded Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 22 Apr 2013 14:33:46 -0400 Subject: [PATCH 174/511] implement ODBCConnection::CloseSync --- src/odbc_connection.cpp | 26 ++++++++++++++++++++++++++ src/odbc_connection.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index b4a259eb..df8b31f5 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -57,6 +57,7 @@ void ODBCConnection::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open); NODE_SET_PROTOTYPE_METHOD(constructor_template, "openSync", OpenSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatement", CreateStatement); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatementSync", CreateStatementSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", Query); @@ -433,6 +434,31 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { scope.Close(Undefined()); } +/* + * CloseSync + */ + +Handle ODBCConnection::CloseSync(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::CloseSync\n"); + HandleScope scope; + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + //TODO: check to see if there are any open statements + //on this connection + + conn->Free(); + + conn->connected = false; + +#if NODE_VERSION_AT_LEAST(0, 7, 9) + uv_unref((uv_handle_t *)&ODBC::g_async); +#else + uv_unref(uv_default_loop()); +#endif + + scope.Close(True()); +} /* * CreateStatementSync diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 9adce20d..b6dc455c 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -77,6 +77,7 @@ class ODBCConnection : public node::ObjectWrap { static void UV_Tables(uv_work_t* req); //sync methods + static Handle CloseSync(const Arguments& args); static Handle CreateStatementSync(const Arguments& args); static Handle OpenSync(const Arguments& args); static Handle QuerySync(const Arguments& args); From d482bc67ff942bb447bf108e178ae5b51f9e750a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 22 Apr 2013 14:34:32 -0400 Subject: [PATCH 175/511] Update README.md to include new features --- README.md | 179 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 144 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index ceababe1..8cadc692 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ node-odbc --------- -An asynchronous interface for node.js to unixODBC and its supported drivers. +An asynchronous/synchronous interface for node.js to unixODBC and its supported +drivers. requirements ------------ @@ -10,7 +11,7 @@ requirements * on Ubuntu/Debian `sudo apt-get install unixodbc unixodbc-dev` * on OSX using macports.org `sudo port unixODBC` * odbc drivers for target database -* properly configured odbc.ini and odbcinst.ini. +* properly configured odbc.ini and odbcinst.ini. install ------- @@ -31,11 +32,50 @@ node-gyp configure build npm install odbc ``` +quick example +------------- + +```javascript +var db = require('odbc')() + , cn = process.env.ODBC_CONNECTION_STRING + ; + +db.open(cn, function (err) { + if (err) return console.log(err); + + db.query('select * from user where user_id = ?', [42], function (err, data) { + if (err) console.log(err); + + console.log(data); + + db.close(function () { + console.log('done'); + }); + }); +}); +``` + api --- ### Database +The simple api is based on instances of the `Database` class. You may get an +instance in a couple of ways. + +By using the helper function: + +```javascript +var db = require("odbc")(); +``` + +or by creating an instance with the constructor function: + +```javascript +var Database = require("odbc").Database + , db = new Database(); +``` + #### .open(connectionString, callback) Open a connection to a database. @@ -44,8 +84,7 @@ Open a connection to a database. * **callback** - `callback (err)` ```javascript -var Database = require("odbc").Database - , db = new Database() +var db = require("odbc")() , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" ; @@ -57,10 +96,30 @@ db.open(cn, function (err) { //we now have an open connection to the database }); ``` +#### .openSync(connectionString) + +Synchronously open a connection to a database. + +* **connectionString** - The ODBC connection string for your database + +```javascript +var db = require("odbc")() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +try { + var result = db.openSync(cn); +} +catch (e) { + console.log(e.message); +} + +//we now have an open connection to the database +``` #### .query(sqlQuery [, bindingParameters], callback) -Issue a SQL query to the database which is currently open. +Issue an asynchronous SQL query to the database which is currently open. * **sqlQuery** - The SQL query to be executed. * **bindingParameters** - _OPTIONAL_ - An array of values that will be bound to @@ -68,8 +127,7 @@ Issue a SQL query to the database which is currently open. * **callback** - `callback (err, rows, moreResultSets)` ```javascript -var Database = require("odbc").Database - , db = new Database() +var db = require("odbc")() , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" ; @@ -93,6 +151,28 @@ db.open(cn, function (err) { }); ``` +#### .querySync(sqlQuery [, bindingParameters]) + +Synchronously issue a SQL query to the database that is currently open. + +* **sqlQuery** - The SQL query to be executed. +* **bindingParameters** - _OPTIONAL_ - An array of values that will be bound to + any '?' characters in `sqlQuery`. + +```javascript +var db = require("odbc")() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +//blocks until the connection is opened. +db.openSync(cn); + +//blocks until the query is completed and all data has been acquired +var rows = db.querySync("select top 10 * from customers"); + +console.log(rows); +``` + #### .close(callback) Close the currently opened database. @@ -100,8 +180,7 @@ Close the currently opened database. * **callback** - `callback (err)` ```javascript -var Database = require("odbc").Database - , db = new Database() +var db = require("odbc")() , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" ; @@ -118,6 +197,53 @@ db.open(cn, function (err) { }); ``` +#### .closeSync(callback) + +Synchronously close the currently opened database. + +```javascript +var db = require("odbc")() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +//Blocks until the connection is open +db.openSync(cn); + +//Blocks until the connection is closed +db.closeSync(); +``` + +#### .prepareSync(sql) + +Synchronously prepare a statement for execution. + +* **sql** - SQL string to prepare + +Returns a `Statement` object + +```javascript +var db = require("odbc")() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +//Blocks until the connection is open +db.openSync(cn); + +//Blocks while preparing the statement +var stmt = db.prepareSync("insert into hits (col1, col2) VALUES (?, ?)") + +//Bind some values to the statement +stmt.bindSync(['something', 42]); + +//Execute the statment asynchronously +stmt.execute(function (err, result) { + result.closeSync(); + + //Close the connection + db.closeSync(); +}); +``` + ---------- ### Pool @@ -207,9 +333,13 @@ db.open(connectionString, function(err) { testing ------- -There is a tests folder which contains scripts which are more examples than tests. -We will be working on bundling these tests into an actual test suite. Sorry about -the state of this. Please feel free to submit patches for this. +Tests can be run by executing `npm test` from within the root of the node-odbc +directory. You can also run the tests by executing `node run-tests.js` from +within the `/test` directory. + +By default, the tests are setup to run against a sqlite3 database which is +created at test time. This will require proper installation of the sqlite odbc +driver. On Ubuntu: `sudo apt-get install libsqliteodbc` tips ---- @@ -257,40 +387,19 @@ CPReuse = Threading = 0 ``` -complete --------- - -- Connection Management -- Querying -- Database Descriptions -- Binding Parameters (thanks to @gurzgri) - -todo ----- - -- Option to emit on each record to avoid collecting the entire dataset first and - increasing memory usage -- More error handling. -- Tests -- SQLGetData needs to support retrieving multiple chunks and concatenation in - the case of large column values - acknowledgements ---------------- -- orlandov's node-sqlite binding was the framework I used to figure out using - eio's thread pool to handle blocking calls since non blocking odbc doesn't - seem to appear until 3.8. - authors ------ -* Lee Smith (notwink@gmail.com) * Dan VerWeire (dverweire@gmail.com) +* Lee Smith (notwink@gmail.com) license ------- +Copyright (c) 2013 Dan VerWeire Copyright (c) 2010 Lee Smith Permission is hereby granted, free of charge, to any person obtaining a copy of From 7933afa15ac1f4af09b491a72bed0beb0b89bdc2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 22 Apr 2013 14:35:16 -0400 Subject: [PATCH 176/511] Add openSync to js interace, Pool updates to avoid multiple ODBC objects --- lib/odbc.js | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index da6e138d..56d093f6 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -32,13 +32,16 @@ module.exports.ODBCConnection = odbc.ODBCConnection; module.exports.ODBCStatement = odbc.ODBCStatement; module.exports.ODBCResult = odbc.ODBCResult; -function Database() { +function Database(options) { var self = this; - self.odbc = new odbc.ODBC(); + options = options || {}; + + self.odbc = (options.odbc) ? options.odbc : new odbc.ODBC(); self.queue = new SimpleQueue(); self.connected = false; + // self.__defineGetter__("connected", function () { // if (!self.conn) { // return false; @@ -66,6 +69,14 @@ Database.prototype.open = function (connectionString, cb) { }); }; +Database.prototype.openSync = function (connectionString) { + var self = this; + + self.conn = self.odbc.createConnectionSync(); + + return self.conn.openSync(connectionString); +} + Database.prototype.close = function (cb) { var self = this; @@ -159,17 +170,18 @@ Database.prototype.queryResult = function (sql, params, cb) { }; Database.prototype.querySync = function (sql, params) { - var self = this; - - if (!params) { - params = []; - } + var self = this, result; if (!self.connected) { throw ({ message : "Connection not open."}); } - var result = self.conn.querySync(sql, params); + if (params) { + result = self.conn.querySync(sql, params); + } + else { + result = self.conn.querySync(sql); + } var data = result.fetchAllSync(); @@ -389,6 +401,7 @@ function Pool () { self.index = Pool.count++; self.availablePool = {}; self.usedPool = {}; + self.odbc = new odbc.ODBC(); } Pool.prototype.open = function (connectionString, callback) { @@ -404,7 +417,7 @@ Pool.prototype.open = function (connectionString, callback) { callback(null, db); } else { - db = new Database(); + db = new Database({ odbc : self.odbc }); db.realClose = db.close; db.close = function (cb) { From 8879b859073e39e7b4a26700cce3e1efe72757c2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 22 Apr 2013 14:41:16 -0400 Subject: [PATCH 177/511] Updated contributors --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8cadc692..6961128e 100644 --- a/README.md +++ b/README.md @@ -387,19 +387,21 @@ CPReuse = Threading = 0 ``` -acknowledgements ----------------- - -authors +contributors ------ - * Dan VerWeire (dverweire@gmail.com) * Lee Smith (notwink@gmail.com) +* Bruno Bigras +* Christian Ensel +* Yorick +* Joachim Kainz +* Oleg Efimov license ------- Copyright (c) 2013 Dan VerWeire + Copyright (c) 2010 Lee Smith Permission is hereby granted, free of charge, to any person obtaining a copy of From 00af751027c5f09fbced34849e278f96c9863652 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Apr 2013 12:57:21 -0400 Subject: [PATCH 178/511] Return more accurate column value types This seems to be a pretty big speed-up; avoiding atoi() and atof(). Also, it is probably helpful to have floats as v8:Number and ints as v8:Integer --- src/odbc.cpp | 124 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 52 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index bafecfee..52b911da 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -330,49 +330,74 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, int ret; switch ((int) column.type) { - case SQL_NUMERIC : - case SQL_DECIMAL : case SQL_INTEGER : case SQL_SMALLINT : + case SQL_TINYINT : { + long value; + + ret = SQLGetData( + hStmt, + column.index, + SQL_C_SLONG, + &value, + sizeof(value), + &len); + + DEBUG_PRINTF("ODBC::GetColumnValue - Integer: index=%i name=%s type=%i len=%i ret=%i\n", + column.index, column.name, column.type, len, ret); + + if (ret == SQL_NULL_DATA || len < 0) { + return scope.Close(Null()); + } + else { + return scope.Close(Integer::New(value)); + } + } + break; + case SQL_NUMERIC : + case SQL_DECIMAL : case SQL_BIGINT : case SQL_FLOAT : case SQL_REAL : - case SQL_DOUBLE : - ret = SQLGetData( hStmt, - column.index, - SQL_C_CHAR, - (char *) buffer, - bufferLength, - &len); - - DEBUG_PRINTF("ODBC::GetColumnValue - Numeric: index=%i name=%s type=%i len=%i ret=%i\n", + case SQL_DOUBLE : { + double value; + + ret = SQLGetData( + hStmt, + column.index, + SQL_C_DOUBLE, + &value, + sizeof(value), + &len); + + DEBUG_PRINTF("ODBC::GetColumnValue - Integer: index=%i name=%s type=%i len=%i ret=%i\n", column.index, column.name, column.type, len, ret); - - if(ret == SQL_NULL_DATA || len < 0) { - return scope.Close(Null()); - //return Null(); - } - else { - return scope.Close(Number::New(atof((char *) buffer))); - //return Number::New(atof((char *) buffer)); + + if(ret == SQL_NULL_DATA || len < 0) { + return scope.Close(Null()); + } + else { + return scope.Close(Number::New(value)); + } } + break; case SQL_DATETIME : case SQL_TIMESTAMP : //I am not sure if this is locale-safe or cross database safe, but it //works for me on MSSQL #ifdef _WIN32 - ret = SQLGetData( hStmt, - column.index, - SQL_C_CHAR, - (char *) buffer, - bufferLength, - &len); + ret = SQLGetData( + hStmt, + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + &len); DEBUG_PRINTF("ODBC::GetColumnValue - W32 Timestamp: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); if(ret == SQL_NULL_DATA || len < 0) { - //return scope.Close(Null()); return Null(); } else { @@ -383,25 +408,24 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //at the specified time. timeInfo.tm_isdst = -1; - //return scope.Close(Date::New(double(mktime(&timeInfo)) * 1000)); return Date::New((double(mktime(&timeInfo)) * 1000)); } #else SQL_TIMESTAMP_STRUCT odbcTime; - ret = SQLGetData( hStmt, - column.index, - SQL_C_TYPE_TIMESTAMP, - &odbcTime, - bufferLength, - &len); + ret = SQLGetData( + hStmt, + column.index, + SQL_C_TYPE_TIMESTAMP, + &odbcTime, + bufferLength, + &len); DEBUG_PRINTF("ODBC::GetColumnValue - Unix Timestamp: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); if(ret == SQL_NULL_DATA || len < 0) { return scope.Close(Null()); - //return Null(); } else { timeInfo.tm_year = odbcTime.year - 1900; @@ -418,49 +442,45 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, return scope.Close(Date::New((double(timegm(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000))); -// return Date::New((double(timegm(&timeInfo)) * 1000) -// + (odbcTime.fraction / 1000000)); } #endif case SQL_BIT : //again, i'm not sure if this is cross database safe, but it works for //MSSQL - ret = SQLGetData( hStmt, - column.index, - SQL_C_CHAR, - (char *) buffer, - bufferLength, - &len); + ret = SQLGetData( + hStmt, + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + &len); DEBUG_PRINTF("ODBC::GetColumnValue - Bit: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); if(ret == SQL_NULL_DATA || len < 0) { return scope.Close(Null()); - //return Null(); } else { return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); - //return Boolean::New(( *buffer == '0') ? false : true ); } default : - ret = SQLGetData( hStmt, - column.index, - SQL_C_CHAR, - (char *) buffer, - bufferLength, - &len); + ret = SQLGetData( + hStmt, + column.index, + SQL_C_CHAR, + (char *) buffer, + bufferLength, + &len); DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); if(ret == SQL_NULL_DATA || len < 0) { return scope.Close(Null()); - //return Null(); } else { return scope.Close(String::New((char*) buffer)); - //return String::New((char*) buffer); } } } From 85f96af277541abf604d62e9fa1015d26073aacc Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Apr 2013 13:41:25 -0400 Subject: [PATCH 179/511] add benchmark for fetchAllSync --- test/bench-query-fetchAllSync.js | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test/bench-query-fetchAllSync.js diff --git a/test/bench-query-fetchAllSync.js b/test/bench-query-fetchAllSync.js new file mode 100644 index 00000000..31059f1e --- /dev/null +++ b/test/bench-query-fetchAllSync.js @@ -0,0 +1,45 @@ +var common = require("./common") +, odbc = require("../") +, db = new odbc.Database(); + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , iterations = 10000 + , time = new Date().getTime(); + + for (var x = 0; x < iterations; x++) { + db.queryResult("select 1 + 1 as test", cb); + } + + function cb (err, result) { + if (err) { + console.error(err); + return finish(); + } + + var data = result.fetchAllSync(); + result.closeSync(); + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return finish(); + } + } + + function finish() { + db.close(function () { + console.log("connection closed"); + }); + } +} \ No newline at end of file From 52e7be8bc9108ce1fcbaa053af9f8e9df73534e2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Apr 2013 13:42:07 -0400 Subject: [PATCH 180/511] increase query benchmark to 10000 iterations --- test/bench-query.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bench-query.js b/test/bench-query.js index 57c2a78a..c56f3c69 100644 --- a/test/bench-query.js +++ b/test/bench-query.js @@ -13,7 +13,7 @@ db.open(common.connectionString, function(err){ function issueQuery() { var count = 0 - , iterations = 1000 + , iterations = 10000 , time = new Date().getTime(); for (var x = 0; x < iterations; x++) { From 3682fcfd7a5eb2a861dc183ec21ee7aea6961772 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Apr 2013 16:45:18 -0400 Subject: [PATCH 181/511] fix memory leak --- src/odbc_connection.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index df8b31f5..551cf83d 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -698,6 +698,8 @@ Handle ODBCConnection::Query(const Arguments& args) { strcpy(data->sql, **sql); + delete sql; + data->conn = conn; work_req->data = data; From 7f5eba9cfd9bc77bc9d0b3227aa616a3543a50bc Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Apr 2013 16:45:51 -0400 Subject: [PATCH 182/511] GetColumnValue optimization --- src/odbc.cpp | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 52b911da..4f7de2ef 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -317,10 +317,8 @@ void ODBC::FreeColumns(Column* columns, short* colCount) { Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength) { - HandleScope scope; + //HandleScope scope; SQLLEN len = 0; - - struct tm timeInfo = { 0 }; //reset the buffer buffer[0] = '\0'; @@ -347,10 +345,12 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret); if (ret == SQL_NULL_DATA || len < 0) { - return scope.Close(Null()); + //return scope.Close(Null()); + return Null(); } else { - return scope.Close(Integer::New(value)); + //return scope.Close(Integer::New(value)); + return Integer::New(value); } } break; @@ -374,15 +374,18 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret); if(ret == SQL_NULL_DATA || len < 0) { - return scope.Close(Null()); + //return scope.Close(Null()); + return Null(); } else { - return scope.Close(Number::New(value)); + //return scope.Close(Number::New(value)); + return Number::New(value); } } break; case SQL_DATETIME : - case SQL_TIMESTAMP : + case SQL_TIMESTAMP : { + struct tm timeInfo = { 0 }; //I am not sure if this is locale-safe or cross database safe, but it //works for me on MSSQL #ifdef _WIN32 @@ -425,7 +428,8 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if(ret == SQL_NULL_DATA || len < 0) { - return scope.Close(Null()); + //return scope.Close(Null()); + return Null(); } else { timeInfo.tm_year = odbcTime.year - 1900; @@ -440,10 +444,13 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //at the specified time. timeInfo.tm_isdst = -1; - return scope.Close(Date::New((double(timegm(&timeInfo)) * 1000) - + (odbcTime.fraction / 1000000))); + //return scope.Close(Date::New((double(timegm(&timeInfo)) * 1000) + // + (odbcTime.fraction / 1000000))); + return Date::New((double(timegm(&timeInfo)) * 1000) + + (odbcTime.fraction / 1000000)); } #endif + } break; case SQL_BIT : //again, i'm not sure if this is cross database safe, but it works for //MSSQL @@ -459,10 +466,12 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if(ret == SQL_NULL_DATA || len < 0) { - return scope.Close(Null()); + //return scope.Close(Null()); + return Null(); } else { - return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); + //return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); + return Boolean::New(( *buffer == '0') ? false : true ); } default : ret = SQLGetData( @@ -477,10 +486,12 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); if(ret == SQL_NULL_DATA || len < 0) { - return scope.Close(Null()); + //return scope.Close(Null()); + return Null(); } else { - return scope.Close(String::New((char*) buffer)); + //return scope.Close(String::New((char*) buffer)); + return String::New((char*) buffer); } } } From 0c51d32cabbea2088477e3fa6a2a71a3f5d703d9 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Apr 2013 16:46:08 -0400 Subject: [PATCH 183/511] expose constants --- lib/odbc.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 56d093f6..2c753ce1 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -39,18 +39,21 @@ function Database(options) { self.odbc = (options.odbc) ? options.odbc : new odbc.ODBC(); self.queue = new SimpleQueue(); - + self.fetchMode = options.fetchMode || null; self.connected = false; - -// self.__defineGetter__("connected", function () { -// if (!self.conn) { -// return false; -// } -// -// return self.conn.connected; -// }); } +//Expose constants +Object.keys(odbc.ODBC).forEach(function (key) { + if (typeof odbc.ODBC[key] !== "function") { + //On the database prototype + Database.prototype[key] = odbc.ODBC[key]; + + //On the exports + module.exports[key] = odbc.ODBC[key]; + } +}); + Database.prototype.open = function (connectionString, cb) { var self = this; @@ -114,7 +117,7 @@ Database.prototype.query = function (sql, params, cb) { fetchMore(); function fetchMore() { - //TODO: keep calling back if there are more result sets + //TODO: pass fetchMode if it's not null result.fetchAll(function (err, data) { var moreResults = result.moreResultsSync(); From 9a5788ee8c88a940b34bb5ea61ffbc864e3abe03 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Apr 2013 16:50:02 -0400 Subject: [PATCH 184/511] 0.5.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f7c556ae..2691a9bc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.2", + "version": "0.5.3", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 55f7299d611e85f016c5516ede82f4bea556b018 Mon Sep 17 00:00:00 2001 From: paulhendrix Date: Mon, 5 Nov 2012 22:20:20 +0100 Subject: [PATCH 185/511] Added dynodbc module --- src/dynodbc.cpp | 165 +++++++++++++++++++++ src/dynodbc.h | 376 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 541 insertions(+) create mode 100644 src/dynodbc.cpp create mode 100644 src/dynodbc.h diff --git a/src/dynodbc.cpp b/src/dynodbc.cpp new file mode 100644 index 00000000..af38209e --- /dev/null +++ b/src/dynodbc.cpp @@ -0,0 +1,165 @@ + +#ifdef dynodbc + +#include "dynodbc.h" + +#ifdef _WIN32 + #include +#elif defined(__GNUC__) // GNU compiler + #include +#else +#error define your copiler +#endif + +#include +/* +#define RTLD_LAZY 1 +#define RTLD_NOW 2 +#define RTLD_GLOBAL 4 +*/ + +void* LoadSharedLibrary(char *pcDllname, int iMode = 2) +{ + std::string sDllName = pcDllname; +#ifdef _WIN32 + sDllName += ".dll"; + return (void*)LoadLibraryA(pcDllname); +#elif defined(__GNUC__) // GNU compiler + sDllName += ".so"; + return dlopen(sDllName.c_str(),iMode); +#endif +} + +void *GetFunction(void *Lib, char *Fnname) +{ +#if defined(_MSC_VER) // Microsoft compiler + return (void*)GetProcAddress((HINSTANCE)Lib,Fnname); +#elif defined(__GNUC__) // GNU compiler + return dlsym(Lib,Fnname); +#endif +} + +bool FreeSharedLibrary(void *hDLL) +{ +#if defined(_MSC_VER) // Microsoft compiler + return (FreeLibrary((HINSTANCE)hDLL)!=0); +#elif defined(__GNUC__) // GNU compiler + return dlclose(hDLL); +#endif +} + +pfnSQLGetData pSQLGetData; +pfnSQLGetFunctions pSQLGetFunctions; +pfnSQLAllocConnect pSQLAllocConnect; +pfnSQLAllocEnv pSQLAllocEnv; +pfnSQLAllocStmt pSQLAllocStmt; +pfnSQLBindCol pSQLBindCol; +pfnSQLCancel pSQLCancel; +pfnSQLColAttributes pSQLColAttributes; +pfnSQLConnect pSQLConnect; +pfnSQLDescribeCol pSQLDescribeCol; +pfnSQLDisconnect pSQLDisconnect; +pfnSQLError pSQLError; +pfnSQLExecDirect pSQLExecDirect; +pfnSQLExecute pSQLExecute; +pfnSQLFetch pSQLFetch; +pfnSQLGetDiagRec pSQLGetDiagRec; +pfnSQLFreeHandle pSQLFreeHandle; +pfnSQLFetchScroll pSQLFetchScroll; +pfnSQLColAttribute pSQLColAttribute; +pfnSQLSetConnectAttr pSQLSetConnectAttr; +pfnSQLDriverConnect pSQLDriverConnect; +pfnSQLAllocHandle pSQLAllocHandle; +pfnSQLRowCount pSQLRowCount; +pfnSQLNumResultCols pSQLNumResultCols; +pfnSQLEndTran pSQLEndTran; +pfnSQLTables pSQLTables; +pfnSQLColumns pSQLColumns; +pfnSQLBindParameter pSQLBindParameter; +pfnSQLPrimaryKeys pSQLPrimaryKeys; +pfnSQLSetEnvAttr pSQLSetEnvAttr ; +pfnSQLFreeConnect pSQLFreeConnect; +pfnSQLFreeEnv pSQLFreeEnv; +pfnSQLFreeStmt pSQLFreeStmt; +pfnSQLGetCursorName pSQLGetCursorName; +pfnSQLPrepare pSQLPrepare; +pfnSQLSetCursorName pSQLSetCursorName; +pfnSQLTransact pSQLTransact; +pfnSQLSetConnectOption pSQLSetConnectOption; +pfnSQLDrivers pSQLDrivers; +pfnSQLDataSources pSQLDataSources; +pfnSQLGetInfo pSQLGetInfo; +pfnSQLMoreResults pSQLMoreResults; + +//#define LOAD_ENTRY( hMod, Name ) (p##Name = (pfn##Name) GetProcAddress( (hMod), #Name )) +#define LOAD_ENTRY( hMod, Name ) (p##Name = (pfn##Name) GetFunction( (hMod), #Name )) + +static BOOL s_fODBCLoaded = FALSE; + +BOOL DynLoadODBC( char* odbcModuleName ) +{ + HMODULE hMod; + + if ( s_fODBCLoaded ) + return TRUE; + + // if ( (hMod = (HMODULE) LoadLibrary( odbcModuleName ))) { + if ( (hMod = (HMODULE) LoadSharedLibrary( odbcModuleName ))) { + + +//#if (ODBCVER < 0x0300) + if (LOAD_ENTRY( hMod, SQLGetData ) ) + if (LOAD_ENTRY( hMod, SQLGetFunctions ) ) + if (LOAD_ENTRY( hMod, SQLAllocConnect ) ) + if (LOAD_ENTRY( hMod, SQLAllocEnv ) ) + if (LOAD_ENTRY( hMod, SQLAllocStmt ) ) + if (LOAD_ENTRY( hMod, SQLColAttributes ) ) + if (LOAD_ENTRY( hMod, SQLError ) ) + if (LOAD_ENTRY( hMod, SQLFreeConnect ) ) + if (LOAD_ENTRY( hMod, SQLFreeEnv ) ) + if (LOAD_ENTRY( hMod, SQLTransact ) ) + if (LOAD_ENTRY( hMod, SQLSetConnectOption ) ) + if (LOAD_ENTRY( hMod, SQLDrivers ) ) + if (LOAD_ENTRY( hMod, SQLDataSources ) ) +//#endif + if (LOAD_ENTRY( hMod, SQLBindCol ) ) + if (LOAD_ENTRY( hMod, SQLCancel ) ) + if (LOAD_ENTRY( hMod, SQLConnect ) ) + if (LOAD_ENTRY( hMod, SQLDescribeCol ) ) + if (LOAD_ENTRY( hMod, SQLDisconnect ) ) + if (LOAD_ENTRY( hMod, SQLExecDirect ) ) + if (LOAD_ENTRY( hMod, SQLExecute ) ) + if (LOAD_ENTRY( hMod, SQLFetch ) ) + if (LOAD_ENTRY( hMod, SQLGetDiagRec ) ) + if (LOAD_ENTRY( hMod, SQLFreeHandle ) ) + if (LOAD_ENTRY( hMod, SQLFetchScroll ) ) + if (LOAD_ENTRY( hMod, SQLColAttribute ) ) + if (LOAD_ENTRY( hMod, SQLSetConnectAttr ) ) + if (LOAD_ENTRY( hMod, SQLDriverConnect ) ) + if (LOAD_ENTRY( hMod, SQLAllocHandle ) ) + if (LOAD_ENTRY( hMod, SQLRowCount ) ) + if (LOAD_ENTRY( hMod, SQLNumResultCols ) ) + if (LOAD_ENTRY( hMod, SQLEndTran ) ) + if (LOAD_ENTRY( hMod, SQLTables) ) + if (LOAD_ENTRY( hMod, SQLColumns) ) + if (LOAD_ENTRY( hMod, SQLBindParameter) ) + if (LOAD_ENTRY( hMod, SQLPrimaryKeys) ) + if (LOAD_ENTRY( hMod, SQLSetEnvAttr) ) + if (LOAD_ENTRY( hMod, SQLFreeStmt ) ) + if (LOAD_ENTRY( hMod, SQLNumResultCols ) ) + if (LOAD_ENTRY( hMod, SQLPrepare ) ) + if (LOAD_ENTRY( hMod, SQLRowCount ) ) + if (LOAD_ENTRY( hMod, SQLGetInfo ) ) + if (LOAD_ENTRY( hMod, SQLBindParameter ) ) + if (LOAD_ENTRY( hMod, SQLMoreResults ) + ) { + + s_fODBCLoaded = TRUE; + } + } + + return (s_fODBCLoaded); + +} + +#endif \ No newline at end of file diff --git a/src/dynodbc.h b/src/dynodbc.h new file mode 100644 index 00000000..4c03b17f --- /dev/null +++ b/src/dynodbc.h @@ -0,0 +1,376 @@ +#ifndef _DYNODBC_H_ +#define _DYNODBC_H_ + +#ifdef dynodbc + +#ifdef _WIN32 +#include +#endif +#include +#include + +typedef RETCODE (SQL_API * pfnSQLGetData)( + SQLHSTMT StatementHandle, + SQLUSMALLINT Col_or_Param_Num, + SQLSMALLINT TargetType, + SQLPOINTER TargetValuePtr, + SQLLEN BufferLength, + SQLLEN * StrLen_or_IndPtr); + +typedef RETCODE (SQL_API * pfnSQLGetFunctions)( + HDBC ConnectionHandle, + SQLUSMALLINT FunctionId, + SQLUSMALLINT * SupportedPtr); + +typedef RETCODE (SQL_API * pfnSQLAllocConnect)( + HENV henv, + HDBC FAR *phdbc); + +typedef RETCODE (SQL_API * pfnSQLAllocEnv)( + HENV FAR *phenv); + +typedef RETCODE (SQL_API * pfnSQLAllocStmt)( + HDBC hdbc, + HSTMT FAR *phstmt); + +typedef RETCODE (SQL_API * pfnSQLBindCol)( + HSTMT hstmt, + UWORD icol, + SWORD fCType, + PTR rgbValue, + SDWORD cbValueMax, + SDWORD FAR *pcbValue); + +typedef RETCODE (SQL_API * pfnSQLCancel)( + HSTMT hstmt); + +typedef RETCODE (SQL_API * pfnSQLColAttributes)( + HSTMT hstmt, + UWORD icol, + UWORD fDescType, + PTR rgbDesc, + SWORD cbDescMax, + SWORD FAR *pcbDesc, + SDWORD FAR *pfDesc); + + +typedef RETCODE (SQL_API * pfnSQLColAttribute)( + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, + SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength, SQLPOINTER NumericAttribute); + +typedef RETCODE (SQL_API * pfnSQLSetConnectAttr)( + SQLHDBC ConnectionHandle, + SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER StringLength); + +typedef RETCODE (SQL_API * pfnSQLDriverConnect)( + SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion); + +typedef RETCODE (SQL_API * pfnSQLAllocHandle)( + SQLSMALLINT HandleType, + SQLHANDLE InputHandle, SQLHANDLE *OutputHandle); + +typedef RETCODE (SQL_API * pfnSQLRowCount)( + SQLHSTMT StatementHandle, + SQLINTEGER *RowCount); + +typedef RETCODE (SQL_API * pfnSQLNumResultCols)( + SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount); + +typedef RETCODE (SQL_API * pfnSQLEndTran)( + SQLSMALLINT HandleType, SQLHANDLE Handle, + SQLSMALLINT CompletionType); + +typedef RETCODE (SQL_API * pfnSQLExecDirect)( + SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength); + +typedef RETCODE (SQL_API * pfnSQLTables)( + SQLHSTMT StatementHandle, + SQLCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLCHAR *TableName, SQLSMALLINT NameLength3, + SQLCHAR *TableType, SQLSMALLINT NameLength4); + +typedef RETCODE (SQL_API * pfnSQLColumns)( + SQLHSTMT StatementHandle, + SQLCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLCHAR *TableName, SQLSMALLINT NameLength3, + SQLCHAR *ColumnName, SQLSMALLINT NameLength4); + +typedef RETCODE (SQL_API * pfnSQLBindParameter)( + SQLHSTMT hstmt, + SQLUSMALLINT ipar, + SQLSMALLINT fParamType, + SQLSMALLINT fCType, + SQLSMALLINT fSqlType, + SQLUINTEGER cbColDef, + SQLSMALLINT ibScale, + SQLPOINTER rgbValue, + SQLINTEGER cbValueMax, + SQLINTEGER *pcbValue); + +typedef RETCODE (SQL_API * pfnSQLPrimaryKeys)( + SQLHSTMT hstmt, + SQLCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLCHAR *szTableName, + SQLSMALLINT cbTableName); + +typedef RETCODE (SQL_API * pfnSQLSetEnvAttr)( + SQLHENV EnvironmentHandle, + SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER StringLength); + + +typedef RETCODE (SQL_API * pfnSQLConnect)( + HDBC hdbc, + UCHAR FAR *szDSN, + SWORD cbDSN, + UCHAR FAR *szUID, + SWORD cbUID, + UCHAR FAR *szAuthStr, + SWORD cbAuthStr); + +typedef RETCODE (SQL_API * pfnSQLDescribeCol)( + HSTMT hstmt, + UWORD icol, + UCHAR FAR *szColName, + SWORD cbColNameMax, + SWORD FAR *pcbColName, + SWORD FAR *pfSqlType, + UDWORD FAR *pcbColDef, + SWORD FAR *pibScale, + SWORD FAR *pfNullable); + +typedef RETCODE (SQL_API * pfnSQLDisconnect)( + HDBC hdbc); + +typedef RETCODE (SQL_API * pfnSQLError)( + HENV henv, + HDBC hdbc, + HSTMT hstmt, + UCHAR FAR *szSqlState, + SDWORD FAR *pfNativeError, + UCHAR FAR *szErrorMsg, + SWORD cbErrorMsgMax, + SWORD FAR *pcbErrorMsg); + +typedef RETCODE (SQL_API * pfnSQLExecDirect)( + HSTMT hstmt, + UCHAR FAR *szSqlStr, + SDWORD cbSqlStr); + +typedef RETCODE (SQL_API * pfnSQLExecute)( + HSTMT hstmt); + +typedef RETCODE (SQL_API * pfnSQLFetch)( + HSTMT hstmt); + +typedef RETCODE (SQL_API * pfnSQLGetDiagRec)( + SQLSMALLINT HandleType, SQLHANDLE Handle, + SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, SQLCHAR *MessageText, + SQLSMALLINT BufferLength, SQLSMALLINT *TextLength); + +typedef RETCODE (SQL_API * pfnSQLFreeHandle)( + SQLSMALLINT HandleType, SQLHANDLE Handle); + +typedef RETCODE (SQL_API * pfnSQLFetchScroll)( + SQLHSTMT StatementHandle, + SQLSMALLINT FetchOrientation, SQLINTEGER FetchOffset); + + + +typedef RETCODE (SQL_API * pfnSQLColAttribute)( + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, + SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength, SQLPOINTER NumericAttribute); + + +typedef RETCODE (SQL_API * pfnSQLFreeConnect)( + HDBC hdbc); + +typedef RETCODE (SQL_API * pfnSQLFreeEnv)( + HENV henv); + +typedef RETCODE (SQL_API * pfnSQLFreeStmt)( + HSTMT hstmt, + UWORD fOption); + +typedef RETCODE (SQL_API * pfnSQLGetCursorName)( + HSTMT hstmt, + UCHAR FAR *szCursor, + SWORD cbCursorMax, + SWORD FAR *pcbCursor); + +typedef RETCODE (SQL_API * pfnSQLNumResultCols)( + HSTMT hstmt, + SWORD FAR *pccol); + +typedef RETCODE (SQL_API * pfnSQLPrepare)( + HSTMT hstmt, + UCHAR FAR *szSqlStr, + SDWORD cbSqlStr); + +typedef RETCODE (SQL_API * pfnSQLRowCount)( + HSTMT hstmt, + SDWORD FAR *pcrow); + +typedef RETCODE (SQL_API * pfnSQLSetCursorName)( + HSTMT hstmt, + UCHAR FAR *szCursor, + SWORD cbCursor); + +typedef RETCODE (SQL_API * pfnSQLTransact)( + HENV henv, + HDBC hdbc, + UWORD fType); + +typedef RETCODE (SQL_API * pfnSQLSetConnectOption)( + HDBC hdbc, + UWORD fOption, + UDWORD vParam); + +typedef RETCODE (SQL_API * pfnSQLDrivers)( + HENV henv, + UWORD fDirection, + UCHAR FAR *szDriverDesc, + SWORD cbDriverDescMax, + SWORD FAR *pcbDriverDesc, + UCHAR FAR *szDriverAttributes, + SWORD cbDrvrAttrMax, + SWORD FAR *pcbDrvrAttr); + +typedef RETCODE (SQL_API * pfnSQLBindParameter)( + HSTMT hstmt, + UWORD ipar, + SWORD fParamType, + SWORD fCType, + SWORD fSqlType, + UDWORD cbColDef, + SWORD ibScale, + PTR rgbValue, + SDWORD cbValueMax, + SDWORD FAR *pcbValue); + +typedef RETCODE (SQL_API * pfnSQLDataSources)( + HENV henv, + UWORD fDirection, + UCHAR FAR *szDSN, + SWORD cbDSNMax, + SWORD FAR *pcbDSN, + UCHAR FAR *szDescription, + SWORD cbDescriptionMax, + SWORD FAR *pcbDescription); + +typedef RETCODE (SQL_API * pfnSQLGetInfo)( + HDBC hdbc, + UWORD fInfoType, + PTR rgbInfoValue, + SWORD cbInfoValueMax, + SWORD FAR *pcbInfoValue); + +typedef RETCODE (SQL_API * pfnSQLMoreResults)( + HSTMT hstmt ); + + +extern pfnSQLGetData pSQLGetData; +extern pfnSQLGetFunctions pSQLGetFunctions; +extern pfnSQLAllocConnect pSQLAllocConnect; +extern pfnSQLAllocEnv pSQLAllocEnv; +extern pfnSQLAllocStmt pSQLAllocStmt; +extern pfnSQLBindCol pSQLBindCol; +extern pfnSQLCancel pSQLCancel; +extern pfnSQLColAttributes pSQLColAttributes; +extern pfnSQLConnect pSQLConnect; +extern pfnSQLDescribeCol pSQLDescribeCol; +extern pfnSQLDisconnect pSQLDisconnect; +extern pfnSQLError pSQLError; +extern pfnSQLExecDirect pSQLExecDirect; +extern pfnSQLExecute pSQLExecute; +extern pfnSQLFetch pSQLFetch; +extern pfnSQLGetDiagRec pSQLGetDiagRec; +extern pfnSQLFreeHandle pSQLFreeHandle; +extern pfnSQLFetchScroll pSQLFetchScroll; +extern pfnSQLFetchScroll pSQLFetchScroll; +extern pfnSQLColAttribute pSQLColAttribute; +extern pfnSQLSetConnectAttr pSQLSetConnectAttr; +extern pfnSQLDriverConnect pSQLDriverConnect; +extern pfnSQLAllocHandle pSQLAllocHandle; +extern pfnSQLRowCount pSQLRowCount; +extern pfnSQLNumResultCols pSQLNumResultCols; +extern pfnSQLEndTran pSQLEndTran; +extern pfnSQLExecDirect pSQLExecDirect; +extern pfnSQLTables pSQLTables; +extern pfnSQLColumns pSQLColumns; +extern pfnSQLBindParameter pSQLBindParameter; +extern pfnSQLPrimaryKeys pSQLPrimaryKeys; +extern pfnSQLSetEnvAttr pSQLSetEnvAttr; +extern pfnSQLFreeConnect pSQLFreeConnect; +extern pfnSQLFreeEnv pSQLFreeEnv; +extern pfnSQLFreeStmt pSQLFreeStmt; +extern pfnSQLGetCursorName pSQLGetCursorName; +extern pfnSQLNumResultCols pSQLNumResultCols; +extern pfnSQLPrepare pSQLPrepare; +extern pfnSQLRowCount pSQLRowCount; +extern pfnSQLSetCursorName pSQLSetCursorName; +extern pfnSQLTransact pSQLTransact; +extern pfnSQLSetConnectOption pSQLSetConnectOption; +extern pfnSQLDrivers pSQLDrivers; +extern pfnSQLDataSources pSQLDataSources; +extern pfnSQLBindParameter pSQLBindParameter; +extern pfnSQLGetInfo pSQLGetInfo; +extern pfnSQLMoreResults pSQLMoreResults; + +BOOL DynLoadODBC( char* odbcModuleName ); + +#define SQLAllocEnv pSQLAllocEnv +#define SQLAllocConnect pSQLAllocConnect +#define SQLSetConnectOption pSQLSetConnectOption +#define SQLAllocStmt pSQLAllocStmt +#define SQLGetFunctions pSQLGetFunctions +#define SQLError pSQLError +#define SQLGetData pSQLGetData +#define SQLMoreResults pSQLMoreResults +#define SQLPrepare pSQLPrepare +#define SQLExecute pSQLExecute +#define SQLGetDiagRec pSQLGetDiagRec +#define SQLFreeHandle pSQLFreeHandle +#define SQLFreeStmt pSQLFreeStmt +#define SQLFetchScroll pSQLFetchScroll +#define SQLFetch pSQLFetch +#define SQLBindCol pSQLBindCol +#define SQLColAttribute pSQLColAttribute +#define SQLGetInfo pSQLGetInfo +#define SQLDriverConnect pSQLDriverConnect +#define SQLAllocHandle pSQLAllocHandle +#define SQLDisconnect pSQLDisconnect +#define SQLRowCount pSQLRowCount +#define SQLNumResultCols pSQLNumResultCols +#define SQLSetConnectAttr pSQLSetConnectAttr +#define SQLEndTran pSQLEndTran +#define SQLExecDirect pSQLExecDirect +#define SQLTables pSQLTables +#define SQLColumns pSQLColumns +#define SQLBindParameter pSQLBindParameter +#define SQLPrimaryKeys pSQLPrimaryKeys +#define SQLSetEnvAttr pSQLSetEnvAttr +#endif +#endif // _DYNODBC_H_ + + From 1092ff701ff01f655568634ec0f36acafa4a8329 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 14:49:10 -0400 Subject: [PATCH 186/511] whitespace changes to dynodbc code --- src/dynodbc.cpp | 214 +++++++++++----------- src/dynodbc.h | 467 ++++++++++++++++++++++++------------------------ 2 files changed, 336 insertions(+), 345 deletions(-) diff --git a/src/dynodbc.cpp b/src/dynodbc.cpp index af38209e..bcaaf1f5 100644 --- a/src/dynodbc.cpp +++ b/src/dynodbc.cpp @@ -1,12 +1,11 @@ - #ifdef dynodbc #include "dynodbc.h" #ifdef _WIN32 - #include + #include #elif defined(__GNUC__) // GNU compiler - #include + #include #else #error define your copiler #endif @@ -20,76 +19,76 @@ void* LoadSharedLibrary(char *pcDllname, int iMode = 2) { - std::string sDllName = pcDllname; + std::string sDllName = pcDllname; #ifdef _WIN32 - sDllName += ".dll"; - return (void*)LoadLibraryA(pcDllname); + sDllName += ".dll"; + return (void*)LoadLibraryA(pcDllname); #elif defined(__GNUC__) // GNU compiler - sDllName += ".so"; - return dlopen(sDllName.c_str(),iMode); + sDllName += ".so"; + return dlopen(sDllName.c_str(),iMode); #endif } void *GetFunction(void *Lib, char *Fnname) { #if defined(_MSC_VER) // Microsoft compiler - return (void*)GetProcAddress((HINSTANCE)Lib,Fnname); + return (void*)GetProcAddress((HINSTANCE)Lib,Fnname); #elif defined(__GNUC__) // GNU compiler - return dlsym(Lib,Fnname); + return dlsym(Lib,Fnname); #endif } bool FreeSharedLibrary(void *hDLL) { #if defined(_MSC_VER) // Microsoft compiler - return (FreeLibrary((HINSTANCE)hDLL)!=0); + return (FreeLibrary((HINSTANCE)hDLL)!=0); #elif defined(__GNUC__) // GNU compiler - return dlclose(hDLL); + return dlclose(hDLL); #endif } -pfnSQLGetData pSQLGetData; -pfnSQLGetFunctions pSQLGetFunctions; -pfnSQLAllocConnect pSQLAllocConnect; -pfnSQLAllocEnv pSQLAllocEnv; -pfnSQLAllocStmt pSQLAllocStmt; -pfnSQLBindCol pSQLBindCol; -pfnSQLCancel pSQLCancel; -pfnSQLColAttributes pSQLColAttributes; -pfnSQLConnect pSQLConnect; -pfnSQLDescribeCol pSQLDescribeCol; -pfnSQLDisconnect pSQLDisconnect; -pfnSQLError pSQLError; -pfnSQLExecDirect pSQLExecDirect; -pfnSQLExecute pSQLExecute; -pfnSQLFetch pSQLFetch; -pfnSQLGetDiagRec pSQLGetDiagRec; -pfnSQLFreeHandle pSQLFreeHandle; -pfnSQLFetchScroll pSQLFetchScroll; -pfnSQLColAttribute pSQLColAttribute; -pfnSQLSetConnectAttr pSQLSetConnectAttr; -pfnSQLDriverConnect pSQLDriverConnect; -pfnSQLAllocHandle pSQLAllocHandle; -pfnSQLRowCount pSQLRowCount; -pfnSQLNumResultCols pSQLNumResultCols; -pfnSQLEndTran pSQLEndTran; -pfnSQLTables pSQLTables; -pfnSQLColumns pSQLColumns; -pfnSQLBindParameter pSQLBindParameter; -pfnSQLPrimaryKeys pSQLPrimaryKeys; -pfnSQLSetEnvAttr pSQLSetEnvAttr ; -pfnSQLFreeConnect pSQLFreeConnect; -pfnSQLFreeEnv pSQLFreeEnv; -pfnSQLFreeStmt pSQLFreeStmt; -pfnSQLGetCursorName pSQLGetCursorName; -pfnSQLPrepare pSQLPrepare; -pfnSQLSetCursorName pSQLSetCursorName; -pfnSQLTransact pSQLTransact; -pfnSQLSetConnectOption pSQLSetConnectOption; -pfnSQLDrivers pSQLDrivers; -pfnSQLDataSources pSQLDataSources; -pfnSQLGetInfo pSQLGetInfo; -pfnSQLMoreResults pSQLMoreResults; +pfnSQLGetData pSQLGetData; +pfnSQLGetFunctions pSQLGetFunctions; +pfnSQLAllocConnect pSQLAllocConnect; +pfnSQLAllocEnv pSQLAllocEnv; +pfnSQLAllocStmt pSQLAllocStmt; +pfnSQLBindCol pSQLBindCol; +pfnSQLCancel pSQLCancel; +pfnSQLColAttributes pSQLColAttributes; +pfnSQLConnect pSQLConnect; +pfnSQLDescribeCol pSQLDescribeCol; +pfnSQLDisconnect pSQLDisconnect; +pfnSQLError pSQLError; +pfnSQLExecDirect pSQLExecDirect; +pfnSQLExecute pSQLExecute; +pfnSQLFetch pSQLFetch; +pfnSQLGetDiagRec pSQLGetDiagRec; +pfnSQLFreeHandle pSQLFreeHandle; +pfnSQLFetchScroll pSQLFetchScroll; +pfnSQLColAttribute pSQLColAttribute; +pfnSQLSetConnectAttr pSQLSetConnectAttr; +pfnSQLDriverConnect pSQLDriverConnect; +pfnSQLAllocHandle pSQLAllocHandle; +pfnSQLRowCount pSQLRowCount; +pfnSQLNumResultCols pSQLNumResultCols; +pfnSQLEndTran pSQLEndTran; +pfnSQLTables pSQLTables; +pfnSQLColumns pSQLColumns; +pfnSQLBindParameter pSQLBindParameter; +pfnSQLPrimaryKeys pSQLPrimaryKeys; +pfnSQLSetEnvAttr pSQLSetEnvAttr ; +pfnSQLFreeConnect pSQLFreeConnect; +pfnSQLFreeEnv pSQLFreeEnv; +pfnSQLFreeStmt pSQLFreeStmt; +pfnSQLGetCursorName pSQLGetCursorName; +pfnSQLPrepare pSQLPrepare; +pfnSQLSetCursorName pSQLSetCursorName; +pfnSQLTransact pSQLTransact; +pfnSQLSetConnectOption pSQLSetConnectOption; +pfnSQLDrivers pSQLDrivers; +pfnSQLDataSources pSQLDataSources; +pfnSQLGetInfo pSQLGetInfo; +pfnSQLMoreResults pSQLMoreResults; //#define LOAD_ENTRY( hMod, Name ) (p##Name = (pfn##Name) GetProcAddress( (hMod), #Name )) #define LOAD_ENTRY( hMod, Name ) (p##Name = (pfn##Name) GetFunction( (hMod), #Name )) @@ -104,62 +103,59 @@ BOOL DynLoadODBC( char* odbcModuleName ) return TRUE; // if ( (hMod = (HMODULE) LoadLibrary( odbcModuleName ))) { - if ( (hMod = (HMODULE) LoadSharedLibrary( odbcModuleName ))) { + if ( (hMod = (HMODULE) LoadSharedLibrary( odbcModuleName ))) { - //#if (ODBCVER < 0x0300) - if (LOAD_ENTRY( hMod, SQLGetData ) ) - if (LOAD_ENTRY( hMod, SQLGetFunctions ) ) - if (LOAD_ENTRY( hMod, SQLAllocConnect ) ) - if (LOAD_ENTRY( hMod, SQLAllocEnv ) ) - if (LOAD_ENTRY( hMod, SQLAllocStmt ) ) - if (LOAD_ENTRY( hMod, SQLColAttributes ) ) - if (LOAD_ENTRY( hMod, SQLError ) ) - if (LOAD_ENTRY( hMod, SQLFreeConnect ) ) - if (LOAD_ENTRY( hMod, SQLFreeEnv ) ) - if (LOAD_ENTRY( hMod, SQLTransact ) ) - if (LOAD_ENTRY( hMod, SQLSetConnectOption ) ) - if (LOAD_ENTRY( hMod, SQLDrivers ) ) - if (LOAD_ENTRY( hMod, SQLDataSources ) ) -//#endif - if (LOAD_ENTRY( hMod, SQLBindCol ) ) - if (LOAD_ENTRY( hMod, SQLCancel ) ) - if (LOAD_ENTRY( hMod, SQLConnect ) ) - if (LOAD_ENTRY( hMod, SQLDescribeCol ) ) - if (LOAD_ENTRY( hMod, SQLDisconnect ) ) - if (LOAD_ENTRY( hMod, SQLExecDirect ) ) - if (LOAD_ENTRY( hMod, SQLExecute ) ) - if (LOAD_ENTRY( hMod, SQLFetch ) ) - if (LOAD_ENTRY( hMod, SQLGetDiagRec ) ) - if (LOAD_ENTRY( hMod, SQLFreeHandle ) ) - if (LOAD_ENTRY( hMod, SQLFetchScroll ) ) - if (LOAD_ENTRY( hMod, SQLColAttribute ) ) - if (LOAD_ENTRY( hMod, SQLSetConnectAttr ) ) - if (LOAD_ENTRY( hMod, SQLDriverConnect ) ) - if (LOAD_ENTRY( hMod, SQLAllocHandle ) ) - if (LOAD_ENTRY( hMod, SQLRowCount ) ) - if (LOAD_ENTRY( hMod, SQLNumResultCols ) ) - if (LOAD_ENTRY( hMod, SQLEndTran ) ) - if (LOAD_ENTRY( hMod, SQLTables) ) - if (LOAD_ENTRY( hMod, SQLColumns) ) - if (LOAD_ENTRY( hMod, SQLBindParameter) ) - if (LOAD_ENTRY( hMod, SQLPrimaryKeys) ) - if (LOAD_ENTRY( hMod, SQLSetEnvAttr) ) - if (LOAD_ENTRY( hMod, SQLFreeStmt ) ) - if (LOAD_ENTRY( hMod, SQLNumResultCols ) ) - if (LOAD_ENTRY( hMod, SQLPrepare ) ) - if (LOAD_ENTRY( hMod, SQLRowCount ) ) - if (LOAD_ENTRY( hMod, SQLGetInfo ) ) - if (LOAD_ENTRY( hMod, SQLBindParameter ) ) - if (LOAD_ENTRY( hMod, SQLMoreResults ) - ) { - - s_fODBCLoaded = TRUE; - } - } - - return (s_fODBCLoaded); - + if (LOAD_ENTRY( hMod, SQLGetData ) ) + if (LOAD_ENTRY( hMod, SQLGetFunctions ) ) + if (LOAD_ENTRY( hMod, SQLAllocConnect ) ) + if (LOAD_ENTRY( hMod, SQLAllocEnv ) ) + if (LOAD_ENTRY( hMod, SQLAllocStmt ) ) + if (LOAD_ENTRY( hMod, SQLColAttributes ) ) + if (LOAD_ENTRY( hMod, SQLError ) ) + if (LOAD_ENTRY( hMod, SQLFreeConnect ) ) + if (LOAD_ENTRY( hMod, SQLFreeEnv ) ) + if (LOAD_ENTRY( hMod, SQLTransact ) ) + if (LOAD_ENTRY( hMod, SQLSetConnectOption ) ) + if (LOAD_ENTRY( hMod, SQLDrivers ) ) + if (LOAD_ENTRY( hMod, SQLDataSources ) ) +//#endif + if (LOAD_ENTRY( hMod, SQLBindCol ) ) + if (LOAD_ENTRY( hMod, SQLCancel ) ) + if (LOAD_ENTRY( hMod, SQLConnect ) ) + if (LOAD_ENTRY( hMod, SQLDescribeCol ) ) + if (LOAD_ENTRY( hMod, SQLDisconnect ) ) + if (LOAD_ENTRY( hMod, SQLExecDirect ) ) + if (LOAD_ENTRY( hMod, SQLExecute ) ) + if (LOAD_ENTRY( hMod, SQLFetch ) ) + if (LOAD_ENTRY( hMod, SQLGetDiagRec ) ) + if (LOAD_ENTRY( hMod, SQLFreeHandle ) ) + if (LOAD_ENTRY( hMod, SQLFetchScroll ) ) + if (LOAD_ENTRY( hMod, SQLColAttribute ) ) + if (LOAD_ENTRY( hMod, SQLSetConnectAttr ) ) + if (LOAD_ENTRY( hMod, SQLDriverConnect ) ) + if (LOAD_ENTRY( hMod, SQLAllocHandle ) ) + if (LOAD_ENTRY( hMod, SQLRowCount ) ) + if (LOAD_ENTRY( hMod, SQLNumResultCols ) ) + if (LOAD_ENTRY( hMod, SQLEndTran ) ) + if (LOAD_ENTRY( hMod, SQLTables) ) + if (LOAD_ENTRY( hMod, SQLColumns) ) + if (LOAD_ENTRY( hMod, SQLBindParameter) ) + if (LOAD_ENTRY( hMod, SQLPrimaryKeys) ) + if (LOAD_ENTRY( hMod, SQLSetEnvAttr) ) + if (LOAD_ENTRY( hMod, SQLFreeStmt ) ) + if (LOAD_ENTRY( hMod, SQLNumResultCols ) ) + if (LOAD_ENTRY( hMod, SQLPrepare ) ) + if (LOAD_ENTRY( hMod, SQLRowCount ) ) + if (LOAD_ENTRY( hMod, SQLGetInfo ) ) + if (LOAD_ENTRY( hMod, SQLBindParameter ) ) + if (LOAD_ENTRY( hMod, SQLMoreResults ) + ) { + + s_fODBCLoaded = TRUE; + } + } + + return (s_fODBCLoaded); } - #endif \ No newline at end of file diff --git a/src/dynodbc.h b/src/dynodbc.h index 4c03b17f..1b4f02ea 100644 --- a/src/dynodbc.h +++ b/src/dynodbc.h @@ -1,5 +1,5 @@ -#ifndef _DYNODBC_H_ -#define _DYNODBC_H_ +#ifndef _SRC_DYNODBC_H_ +#define _SRC_DYNODBC_H_ #ifdef dynodbc @@ -10,332 +10,329 @@ #include typedef RETCODE (SQL_API * pfnSQLGetData)( - SQLHSTMT StatementHandle, - SQLUSMALLINT Col_or_Param_Num, - SQLSMALLINT TargetType, - SQLPOINTER TargetValuePtr, - SQLLEN BufferLength, - SQLLEN * StrLen_or_IndPtr); + SQLHSTMT StatementHandle, + SQLUSMALLINT Col_or_Param_Num, + SQLSMALLINT TargetType, + SQLPOINTER TargetValuePtr, + SQLLEN BufferLength, + SQLLEN * StrLen_or_IndPtr); typedef RETCODE (SQL_API * pfnSQLGetFunctions)( - HDBC ConnectionHandle, - SQLUSMALLINT FunctionId, - SQLUSMALLINT * SupportedPtr); + HDBC ConnectionHandle, + SQLUSMALLINT FunctionId, + SQLUSMALLINT * SupportedPtr); typedef RETCODE (SQL_API * pfnSQLAllocConnect)( - HENV henv, - HDBC FAR *phdbc); + HENV henv, + HDBC FAR *phdbc); typedef RETCODE (SQL_API * pfnSQLAllocEnv)( - HENV FAR *phenv); + HENV FAR *phenv); typedef RETCODE (SQL_API * pfnSQLAllocStmt)( - HDBC hdbc, - HSTMT FAR *phstmt); + HDBC hdbc, + HSTMT FAR *phstmt); typedef RETCODE (SQL_API * pfnSQLBindCol)( - HSTMT hstmt, - UWORD icol, - SWORD fCType, - PTR rgbValue, - SDWORD cbValueMax, - SDWORD FAR *pcbValue); + HSTMT hstmt, + UWORD icol, + SWORD fCType, + PTR rgbValue, + SDWORD cbValueMax, + SDWORD FAR *pcbValue); typedef RETCODE (SQL_API * pfnSQLCancel)( - HSTMT hstmt); + HSTMT hstmt); typedef RETCODE (SQL_API * pfnSQLColAttributes)( - HSTMT hstmt, - UWORD icol, - UWORD fDescType, - PTR rgbDesc, - SWORD cbDescMax, - SWORD FAR *pcbDesc, - SDWORD FAR *pfDesc); + HSTMT hstmt, + UWORD icol, + UWORD fDescType, + PTR rgbDesc, + SWORD cbDescMax, + SWORD FAR *pcbDesc, + SDWORD FAR *pfDesc); typedef RETCODE (SQL_API * pfnSQLColAttribute)( - SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, - SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, - SQLSMALLINT *StringLength, SQLPOINTER NumericAttribute); + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, + SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength, SQLPOINTER NumericAttribute); typedef RETCODE (SQL_API * pfnSQLSetConnectAttr)( - SQLHDBC ConnectionHandle, - SQLINTEGER Attribute, SQLPOINTER Value, - SQLINTEGER StringLength); + SQLHDBC ConnectionHandle, + SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER StringLength); typedef RETCODE (SQL_API * pfnSQLDriverConnect)( - SQLHDBC hdbc, - SQLHWND hwnd, - SQLCHAR *szConnStrIn, - SQLSMALLINT cbConnStrIn, - SQLCHAR *szConnStrOut, - SQLSMALLINT cbConnStrOutMax, - SQLSMALLINT *pcbConnStrOut, - SQLUSMALLINT fDriverCompletion); + SQLHDBC hdbc, + SQLHWND hwnd, + SQLCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion); typedef RETCODE (SQL_API * pfnSQLAllocHandle)( - SQLSMALLINT HandleType, - SQLHANDLE InputHandle, SQLHANDLE *OutputHandle); + SQLSMALLINT HandleType, + SQLHANDLE InputHandle, SQLHANDLE *OutputHandle); typedef RETCODE (SQL_API * pfnSQLRowCount)( - SQLHSTMT StatementHandle, - SQLINTEGER *RowCount); + SQLHSTMT StatementHandle, + SQLINTEGER *RowCount); typedef RETCODE (SQL_API * pfnSQLNumResultCols)( - SQLHSTMT StatementHandle, - SQLSMALLINT *ColumnCount); + SQLHSTMT StatementHandle, + SQLSMALLINT *ColumnCount); typedef RETCODE (SQL_API * pfnSQLEndTran)( - SQLSMALLINT HandleType, SQLHANDLE Handle, - SQLSMALLINT CompletionType); + SQLSMALLINT HandleType, SQLHANDLE Handle, + SQLSMALLINT CompletionType); typedef RETCODE (SQL_API * pfnSQLExecDirect)( - SQLHSTMT StatementHandle, - SQLCHAR *StatementText, SQLINTEGER TextLength); + SQLHSTMT StatementHandle, + SQLCHAR *StatementText, SQLINTEGER TextLength); typedef RETCODE (SQL_API * pfnSQLTables)( - SQLHSTMT StatementHandle, - SQLCHAR *CatalogName, SQLSMALLINT NameLength1, - SQLCHAR *SchemaName, SQLSMALLINT NameLength2, - SQLCHAR *TableName, SQLSMALLINT NameLength3, - SQLCHAR *TableType, SQLSMALLINT NameLength4); + SQLHSTMT StatementHandle, + SQLCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLCHAR *TableName, SQLSMALLINT NameLength3, + SQLCHAR *TableType, SQLSMALLINT NameLength4); typedef RETCODE (SQL_API * pfnSQLColumns)( - SQLHSTMT StatementHandle, - SQLCHAR *CatalogName, SQLSMALLINT NameLength1, - SQLCHAR *SchemaName, SQLSMALLINT NameLength2, - SQLCHAR *TableName, SQLSMALLINT NameLength3, - SQLCHAR *ColumnName, SQLSMALLINT NameLength4); + SQLHSTMT StatementHandle, + SQLCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLCHAR *TableName, SQLSMALLINT NameLength3, + SQLCHAR *ColumnName, SQLSMALLINT NameLength4); typedef RETCODE (SQL_API * pfnSQLBindParameter)( - SQLHSTMT hstmt, - SQLUSMALLINT ipar, - SQLSMALLINT fParamType, - SQLSMALLINT fCType, - SQLSMALLINT fSqlType, - SQLUINTEGER cbColDef, - SQLSMALLINT ibScale, - SQLPOINTER rgbValue, - SQLINTEGER cbValueMax, - SQLINTEGER *pcbValue); + SQLHSTMT hstmt, + SQLUSMALLINT ipar, + SQLSMALLINT fParamType, + SQLSMALLINT fCType, + SQLSMALLINT fSqlType, + SQLUINTEGER cbColDef, + SQLSMALLINT ibScale, + SQLPOINTER rgbValue, + SQLINTEGER cbValueMax, + SQLINTEGER *pcbValue); typedef RETCODE (SQL_API * pfnSQLPrimaryKeys)( - SQLHSTMT hstmt, - SQLCHAR *szCatalogName, - SQLSMALLINT cbCatalogName, - SQLCHAR *szSchemaName, - SQLSMALLINT cbSchemaName, - SQLCHAR *szTableName, - SQLSMALLINT cbTableName); + SQLHSTMT hstmt, + SQLCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLCHAR *szTableName, + SQLSMALLINT cbTableName); typedef RETCODE (SQL_API * pfnSQLSetEnvAttr)( - SQLHENV EnvironmentHandle, - SQLINTEGER Attribute, SQLPOINTER Value, - SQLINTEGER StringLength); + SQLHENV EnvironmentHandle, + SQLINTEGER Attribute, SQLPOINTER Value, + SQLINTEGER StringLength); typedef RETCODE (SQL_API * pfnSQLConnect)( - HDBC hdbc, - UCHAR FAR *szDSN, - SWORD cbDSN, - UCHAR FAR *szUID, - SWORD cbUID, - UCHAR FAR *szAuthStr, - SWORD cbAuthStr); + HDBC hdbc, + UCHAR FAR *szDSN, + SWORD cbDSN, + UCHAR FAR *szUID, + SWORD cbUID, + UCHAR FAR *szAuthStr, + SWORD cbAuthStr); typedef RETCODE (SQL_API * pfnSQLDescribeCol)( - HSTMT hstmt, - UWORD icol, - UCHAR FAR *szColName, - SWORD cbColNameMax, - SWORD FAR *pcbColName, - SWORD FAR *pfSqlType, - UDWORD FAR *pcbColDef, - SWORD FAR *pibScale, - SWORD FAR *pfNullable); + HSTMT hstmt, + UWORD icol, + UCHAR FAR *szColName, + SWORD cbColNameMax, + SWORD FAR *pcbColName, + SWORD FAR *pfSqlType, + UDWORD FAR *pcbColDef, + SWORD FAR *pibScale, + SWORD FAR *pfNullable); typedef RETCODE (SQL_API * pfnSQLDisconnect)( - HDBC hdbc); + HDBC hdbc); typedef RETCODE (SQL_API * pfnSQLError)( - HENV henv, - HDBC hdbc, - HSTMT hstmt, - UCHAR FAR *szSqlState, - SDWORD FAR *pfNativeError, - UCHAR FAR *szErrorMsg, - SWORD cbErrorMsgMax, - SWORD FAR *pcbErrorMsg); + HENV henv, + HDBC hdbc, + HSTMT hstmt, + UCHAR FAR *szSqlState, + SDWORD FAR *pfNativeError, + UCHAR FAR *szErrorMsg, + SWORD cbErrorMsgMax, + SWORD FAR *pcbErrorMsg); typedef RETCODE (SQL_API * pfnSQLExecDirect)( - HSTMT hstmt, - UCHAR FAR *szSqlStr, - SDWORD cbSqlStr); + HSTMT hstmt, + UCHAR FAR *szSqlStr, + SDWORD cbSqlStr); typedef RETCODE (SQL_API * pfnSQLExecute)( - HSTMT hstmt); + HSTMT hstmt); typedef RETCODE (SQL_API * pfnSQLFetch)( - HSTMT hstmt); + HSTMT hstmt); typedef RETCODE (SQL_API * pfnSQLGetDiagRec)( - SQLSMALLINT HandleType, SQLHANDLE Handle, - SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, - SQLINTEGER *NativeError, SQLCHAR *MessageText, - SQLSMALLINT BufferLength, SQLSMALLINT *TextLength); + SQLSMALLINT HandleType, SQLHANDLE Handle, + SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, SQLCHAR *MessageText, + SQLSMALLINT BufferLength, SQLSMALLINT *TextLength); typedef RETCODE (SQL_API * pfnSQLFreeHandle)( - SQLSMALLINT HandleType, SQLHANDLE Handle); + SQLSMALLINT HandleType, SQLHANDLE Handle); typedef RETCODE (SQL_API * pfnSQLFetchScroll)( - SQLHSTMT StatementHandle, - SQLSMALLINT FetchOrientation, SQLINTEGER FetchOffset); - - + SQLHSTMT StatementHandle, + SQLSMALLINT FetchOrientation, SQLINTEGER FetchOffset); typedef RETCODE (SQL_API * pfnSQLColAttribute)( - SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, - SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, - SQLSMALLINT *StringLength, SQLPOINTER NumericAttribute); + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, + SQLPOINTER CharacterAttribute, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLength, SQLPOINTER NumericAttribute); typedef RETCODE (SQL_API * pfnSQLFreeConnect)( - HDBC hdbc); + HDBC hdbc); typedef RETCODE (SQL_API * pfnSQLFreeEnv)( - HENV henv); + HENV henv); typedef RETCODE (SQL_API * pfnSQLFreeStmt)( - HSTMT hstmt, - UWORD fOption); + HSTMT hstmt, + UWORD fOption); typedef RETCODE (SQL_API * pfnSQLGetCursorName)( - HSTMT hstmt, - UCHAR FAR *szCursor, - SWORD cbCursorMax, - SWORD FAR *pcbCursor); + HSTMT hstmt, + UCHAR FAR *szCursor, + SWORD cbCursorMax, + SWORD FAR *pcbCursor); typedef RETCODE (SQL_API * pfnSQLNumResultCols)( - HSTMT hstmt, - SWORD FAR *pccol); + HSTMT hstmt, + SWORD FAR *pccol); typedef RETCODE (SQL_API * pfnSQLPrepare)( - HSTMT hstmt, - UCHAR FAR *szSqlStr, - SDWORD cbSqlStr); + HSTMT hstmt, + UCHAR FAR *szSqlStr, + SDWORD cbSqlStr); typedef RETCODE (SQL_API * pfnSQLRowCount)( - HSTMT hstmt, - SDWORD FAR *pcrow); + HSTMT hstmt, + SDWORD FAR *pcrow); typedef RETCODE (SQL_API * pfnSQLSetCursorName)( - HSTMT hstmt, - UCHAR FAR *szCursor, - SWORD cbCursor); + HSTMT hstmt, + UCHAR FAR *szCursor, + SWORD cbCursor); typedef RETCODE (SQL_API * pfnSQLTransact)( - HENV henv, - HDBC hdbc, - UWORD fType); + HENV henv, + HDBC hdbc, + UWORD fType); typedef RETCODE (SQL_API * pfnSQLSetConnectOption)( - HDBC hdbc, - UWORD fOption, - UDWORD vParam); + HDBC hdbc, + UWORD fOption, + UDWORD vParam); typedef RETCODE (SQL_API * pfnSQLDrivers)( - HENV henv, - UWORD fDirection, - UCHAR FAR *szDriverDesc, - SWORD cbDriverDescMax, - SWORD FAR *pcbDriverDesc, - UCHAR FAR *szDriverAttributes, - SWORD cbDrvrAttrMax, - SWORD FAR *pcbDrvrAttr); + HENV henv, + UWORD fDirection, + UCHAR FAR *szDriverDesc, + SWORD cbDriverDescMax, + SWORD FAR *pcbDriverDesc, + UCHAR FAR *szDriverAttributes, + SWORD cbDrvrAttrMax, + SWORD FAR *pcbDrvrAttr); typedef RETCODE (SQL_API * pfnSQLBindParameter)( - HSTMT hstmt, - UWORD ipar, - SWORD fParamType, - SWORD fCType, - SWORD fSqlType, - UDWORD cbColDef, - SWORD ibScale, - PTR rgbValue, - SDWORD cbValueMax, - SDWORD FAR *pcbValue); + HSTMT hstmt, + UWORD ipar, + SWORD fParamType, + SWORD fCType, + SWORD fSqlType, + UDWORD cbColDef, + SWORD ibScale, + PTR rgbValue, + SDWORD cbValueMax, + SDWORD FAR *pcbValue); typedef RETCODE (SQL_API * pfnSQLDataSources)( - HENV henv, - UWORD fDirection, - UCHAR FAR *szDSN, - SWORD cbDSNMax, - SWORD FAR *pcbDSN, - UCHAR FAR *szDescription, - SWORD cbDescriptionMax, - SWORD FAR *pcbDescription); + HENV henv, + UWORD fDirection, + UCHAR FAR *szDSN, + SWORD cbDSNMax, + SWORD FAR *pcbDSN, + UCHAR FAR *szDescription, + SWORD cbDescriptionMax, + SWORD FAR *pcbDescription); typedef RETCODE (SQL_API * pfnSQLGetInfo)( - HDBC hdbc, - UWORD fInfoType, - PTR rgbInfoValue, - SWORD cbInfoValueMax, - SWORD FAR *pcbInfoValue); + HDBC hdbc, + UWORD fInfoType, + PTR rgbInfoValue, + SWORD cbInfoValueMax, + SWORD FAR *pcbInfoValue); typedef RETCODE (SQL_API * pfnSQLMoreResults)( - HSTMT hstmt ); - - -extern pfnSQLGetData pSQLGetData; -extern pfnSQLGetFunctions pSQLGetFunctions; -extern pfnSQLAllocConnect pSQLAllocConnect; -extern pfnSQLAllocEnv pSQLAllocEnv; -extern pfnSQLAllocStmt pSQLAllocStmt; -extern pfnSQLBindCol pSQLBindCol; -extern pfnSQLCancel pSQLCancel; -extern pfnSQLColAttributes pSQLColAttributes; -extern pfnSQLConnect pSQLConnect; -extern pfnSQLDescribeCol pSQLDescribeCol; -extern pfnSQLDisconnect pSQLDisconnect; -extern pfnSQLError pSQLError; -extern pfnSQLExecDirect pSQLExecDirect; -extern pfnSQLExecute pSQLExecute; -extern pfnSQLFetch pSQLFetch; -extern pfnSQLGetDiagRec pSQLGetDiagRec; -extern pfnSQLFreeHandle pSQLFreeHandle; -extern pfnSQLFetchScroll pSQLFetchScroll; -extern pfnSQLFetchScroll pSQLFetchScroll; -extern pfnSQLColAttribute pSQLColAttribute; -extern pfnSQLSetConnectAttr pSQLSetConnectAttr; -extern pfnSQLDriverConnect pSQLDriverConnect; -extern pfnSQLAllocHandle pSQLAllocHandle; -extern pfnSQLRowCount pSQLRowCount; -extern pfnSQLNumResultCols pSQLNumResultCols; -extern pfnSQLEndTran pSQLEndTran; -extern pfnSQLExecDirect pSQLExecDirect; -extern pfnSQLTables pSQLTables; -extern pfnSQLColumns pSQLColumns; -extern pfnSQLBindParameter pSQLBindParameter; -extern pfnSQLPrimaryKeys pSQLPrimaryKeys; -extern pfnSQLSetEnvAttr pSQLSetEnvAttr; -extern pfnSQLFreeConnect pSQLFreeConnect; -extern pfnSQLFreeEnv pSQLFreeEnv; -extern pfnSQLFreeStmt pSQLFreeStmt; -extern pfnSQLGetCursorName pSQLGetCursorName; -extern pfnSQLNumResultCols pSQLNumResultCols; -extern pfnSQLPrepare pSQLPrepare; -extern pfnSQLRowCount pSQLRowCount; -extern pfnSQLSetCursorName pSQLSetCursorName; -extern pfnSQLTransact pSQLTransact; -extern pfnSQLSetConnectOption pSQLSetConnectOption; -extern pfnSQLDrivers pSQLDrivers; -extern pfnSQLDataSources pSQLDataSources; -extern pfnSQLBindParameter pSQLBindParameter; -extern pfnSQLGetInfo pSQLGetInfo; -extern pfnSQLMoreResults pSQLMoreResults; + HSTMT hstmt); + +extern pfnSQLGetData pSQLGetData; +extern pfnSQLGetFunctions pSQLGetFunctions; +extern pfnSQLAllocConnect pSQLAllocConnect; +extern pfnSQLAllocEnv pSQLAllocEnv; +extern pfnSQLAllocStmt pSQLAllocStmt; +extern pfnSQLBindCol pSQLBindCol; +extern pfnSQLCancel pSQLCancel; +extern pfnSQLColAttributes pSQLColAttributes; +extern pfnSQLConnect pSQLConnect; +extern pfnSQLDescribeCol pSQLDescribeCol; +extern pfnSQLDisconnect pSQLDisconnect; +extern pfnSQLError pSQLError; +extern pfnSQLExecDirect pSQLExecDirect; +extern pfnSQLExecute pSQLExecute; +extern pfnSQLFetch pSQLFetch; +extern pfnSQLGetDiagRec pSQLGetDiagRec; +extern pfnSQLFreeHandle pSQLFreeHandle; +extern pfnSQLFetchScroll pSQLFetchScroll; +extern pfnSQLFetchScroll pSQLFetchScroll; +extern pfnSQLColAttribute pSQLColAttribute; +extern pfnSQLSetConnectAttr pSQLSetConnectAttr; +extern pfnSQLDriverConnect pSQLDriverConnect; +extern pfnSQLAllocHandle pSQLAllocHandle; +extern pfnSQLRowCount pSQLRowCount; +extern pfnSQLNumResultCols pSQLNumResultCols; +extern pfnSQLEndTran pSQLEndTran; +extern pfnSQLExecDirect pSQLExecDirect; +extern pfnSQLTables pSQLTables; +extern pfnSQLColumns pSQLColumns; +extern pfnSQLBindParameter pSQLBindParameter; +extern pfnSQLPrimaryKeys pSQLPrimaryKeys; +extern pfnSQLSetEnvAttr pSQLSetEnvAttr; +extern pfnSQLFreeConnect pSQLFreeConnect; +extern pfnSQLFreeEnv pSQLFreeEnv; +extern pfnSQLFreeStmt pSQLFreeStmt; +extern pfnSQLGetCursorName pSQLGetCursorName; +extern pfnSQLNumResultCols pSQLNumResultCols; +extern pfnSQLPrepare pSQLPrepare; +extern pfnSQLRowCount pSQLRowCount; +extern pfnSQLSetCursorName pSQLSetCursorName; +extern pfnSQLTransact pSQLTransact; +extern pfnSQLSetConnectOption pSQLSetConnectOption; +extern pfnSQLDrivers pSQLDrivers; +extern pfnSQLDataSources pSQLDataSources; +extern pfnSQLBindParameter pSQLBindParameter; +extern pfnSQLGetInfo pSQLGetInfo; +extern pfnSQLMoreResults pSQLMoreResults; BOOL DynLoadODBC( char* odbcModuleName ); @@ -371,6 +368,4 @@ BOOL DynLoadODBC( char* odbcModuleName ); #define SQLPrimaryKeys pSQLPrimaryKeys #define SQLSetEnvAttr pSQLSetEnvAttr #endif -#endif // _DYNODBC_H_ - - +#endif // _SRC_DYNODBC_H_ From d2851cab3a7c43258de5a64519f7fee1a8192a70 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 16:53:10 -0400 Subject: [PATCH 187/511] fixes for compiling dynodbc on linux --- src/dynodbc.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/dynodbc.cpp b/src/dynodbc.cpp index bcaaf1f5..3369aee4 100644 --- a/src/dynodbc.cpp +++ b/src/dynodbc.cpp @@ -29,7 +29,7 @@ void* LoadSharedLibrary(char *pcDllname, int iMode = 2) #endif } -void *GetFunction(void *Lib, char *Fnname) +void* GetFunction(void *Lib, char *Fnname) { #if defined(_MSC_VER) // Microsoft compiler return (void*)GetProcAddress((HINSTANCE)Lib,Fnname); @@ -93,17 +93,25 @@ pfnSQLMoreResults pSQLMoreResults; //#define LOAD_ENTRY( hMod, Name ) (p##Name = (pfn##Name) GetProcAddress( (hMod), #Name )) #define LOAD_ENTRY( hMod, Name ) (p##Name = (pfn##Name) GetFunction( (hMod), #Name )) -static BOOL s_fODBCLoaded = FALSE; +static BOOL s_fODBCLoaded = false; BOOL DynLoadODBC( char* odbcModuleName ) { +#ifdef _WIN32 HMODULE hMod; +#elif defined(__GNUC__) // GNU compiler + void* hMod; +#endif if ( s_fODBCLoaded ) - return TRUE; + return true; // if ( (hMod = (HMODULE) LoadLibrary( odbcModuleName ))) { +#ifdef _WIN32 if ( (hMod = (HMODULE) LoadSharedLibrary( odbcModuleName ))) { +#elif defined(__GNUC__) // GNU compiler + if ( (hMod = (void *) LoadSharedLibrary( odbcModuleName ))) { +#endif //#if (ODBCVER < 0x0300) if (LOAD_ENTRY( hMod, SQLGetData ) ) @@ -152,7 +160,7 @@ BOOL DynLoadODBC( char* odbcModuleName ) if (LOAD_ENTRY( hMod, SQLMoreResults ) ) { - s_fODBCLoaded = TRUE; + s_fODBCLoaded = true; } } From 8092604d5eb82fcca69e5843b6ba9bb329e28942 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 16:54:13 -0400 Subject: [PATCH 188/511] expose dynodbc functionality to the public api --- lib/odbc.js | 13 +++++++++++++ src/odbc.cpp | 21 +++++++++++++++++++++ src/odbc.h | 4 +++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/odbc.js b/lib/odbc.js index 2c753ce1..0df230af 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -31,12 +31,25 @@ module.exports.ODBC = odbc.ODBC; module.exports.ODBCConnection = odbc.ODBCConnection; module.exports.ODBCStatement = odbc.ODBCStatement; module.exports.ODBCResult = odbc.ODBCResult; +module.exports.loadODBCLibrary = odbc.loadODBCLibrary; function Database(options) { var self = this; options = options || {}; + if (odbc.loadODBCLibrary) { + if (!options.library) { + throw new Error("You must specify a library when complied with dynodbc, " + + "otherwise this jams will segfault."); + } + + if (!odbc.loadODBCLibrary(options.library)) { + throw new Error("Could not load library. You may need to specify full " + + "path."); + } + } + self.odbc = (options.odbc) ? options.odbc : new odbc.ODBC(); self.queue = new SimpleQueue(); self.fetchMode = options.fetchMode || null; diff --git a/src/odbc.cpp b/src/odbc.cpp index 4f7de2ef..c2ccdd24 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -27,6 +27,10 @@ #include "odbc_result.h" #include "odbc_statement.h" +#ifdef dynodbc +#include "dynodbc.h" +#endif + #ifdef _WIN32 #include "strptime.h" #endif @@ -789,7 +793,24 @@ Local ODBC::GetAllRecordsSync (HENV hENV, scope.Close(rows); } +#ifdef dynodbc +Handle ODBC::LoadODBCLibrary(const Arguments& args) { + HandleScope scope; + + REQ_STR_ARG(0, js_library); + + bool result = DynLoadODBC(*js_library); + + return scope.Close((result) ? True() : False()); +} +#endif + extern "C" void init (v8::Handle target) { +#ifdef dynodbc + target->Set(String::NewSymbol("loadODBCLibrary"), + FunctionTemplate::New(ODBC::LoadODBCLibrary)->GetFunction()); +#endif + ODBC::Init(target); ODBCResult::Init(target); ODBCConnection::Init(target); diff --git a/src/odbc.h b/src/odbc.h index 11685f88..114a18be 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -71,7 +71,9 @@ class ODBC : public node::ObjectWrap { static Local GetSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, char* message); static Local GetSQLDiagRecError (HDBC hDBC); static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); - +#ifdef dynodbc + static Handle LoadODBCLibrary(const Arguments& args); +#endif static Parameter* GetParametersFromArray (Local values, int* paramCount); void Free(); From d8dec45c49225b41fbab509c157a21fba18db479 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 16:55:04 -0400 Subject: [PATCH 189/511] include dynodbc in binding.gyp --- binding.gyp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/binding.gyp b/binding.gyp index 612c5ec7..ad9fad26 100644 --- a/binding.gyp +++ b/binding.gyp @@ -6,19 +6,21 @@ 'src/odbc.cpp', 'src/odbc_connection.cpp', 'src/odbc_statement.cpp', - 'src/odbc_result.cpp' + 'src/odbc_result.cpp', + 'src/dynodbc.cpp' ], 'defines' : [ + ], 'conditions' : [ [ 'OS == "linux"', { - 'libraries' : [ - '-lodbc' - ], - 'cflags' : [ - '-g' - ] - }], + 'libraries' : [ + '-lodbc' + ], + 'cflags' : [ + '-g' + ] + }], [ 'OS == "mac"', { 'libraries' : [ '-lodbc' From 135b11739ba135e9304428df112a933a6be48bd0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 17:32:25 -0400 Subject: [PATCH 190/511] support setting library globally on the module --- lib/odbc.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 0df230af..65ecdf5a 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -39,12 +39,12 @@ function Database(options) { options = options || {}; if (odbc.loadODBCLibrary) { - if (!options.library) { + if (!options.library && !module.exports.library) { throw new Error("You must specify a library when complied with dynodbc, " + "otherwise this jams will segfault."); } - if (!odbc.loadODBCLibrary(options.library)) { + if (!odbc.loadODBCLibrary(options.library || module.exports.library)) { throw new Error("Could not load library. You may need to specify full " + "path."); } From 020d68683305d8d7e338a2f1bee2872d6b672bc7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 17:33:22 -0400 Subject: [PATCH 191/511] remove duplicate pfnSQLBindParameter, dump dlopen() error messages --- src/dynodbc.cpp | 9 ++++++++- src/dynodbc.h | 26 +++++++++++++------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/dynodbc.cpp b/src/dynodbc.cpp index 3369aee4..3dd8f1bb 100644 --- a/src/dynodbc.cpp +++ b/src/dynodbc.cpp @@ -1,6 +1,7 @@ #ifdef dynodbc #include "dynodbc.h" +#include #ifdef _WIN32 #include @@ -25,7 +26,13 @@ void* LoadSharedLibrary(char *pcDllname, int iMode = 2) return (void*)LoadLibraryA(pcDllname); #elif defined(__GNUC__) // GNU compiler sDllName += ".so"; - return dlopen(sDllName.c_str(),iMode); + void* handle = dlopen(sDllName.c_str(),iMode); + + if (!handle) { + printf("node-odbc: error loading ODBC library: %s\n", dlerror()); + } + + return handle; #endif } diff --git a/src/dynodbc.h b/src/dynodbc.h index 1b4f02ea..ded4b262 100644 --- a/src/dynodbc.h +++ b/src/dynodbc.h @@ -119,7 +119,7 @@ typedef RETCODE (SQL_API * pfnSQLBindParameter)( SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLINTEGER cbValueMax, - SQLINTEGER *pcbValue); + SQLLEN *pcbValue); typedef RETCODE (SQL_API * pfnSQLPrimaryKeys)( SQLHSTMT hstmt, @@ -254,17 +254,17 @@ typedef RETCODE (SQL_API * pfnSQLDrivers)( SWORD cbDrvrAttrMax, SWORD FAR *pcbDrvrAttr); -typedef RETCODE (SQL_API * pfnSQLBindParameter)( - HSTMT hstmt, - UWORD ipar, - SWORD fParamType, - SWORD fCType, - SWORD fSqlType, - UDWORD cbColDef, - SWORD ibScale, - PTR rgbValue, - SDWORD cbValueMax, - SDWORD FAR *pcbValue); +// typedef RETCODE (SQL_API * pfnSQLBindParameter)( +// HSTMT hstmt, +// UWORD ipar, +// SWORD fParamType, +// SWORD fCType, +// SWORD fSqlType, +// UDWORD cbColDef, +// SWORD ibScale, +// PTR rgbValue, +// SDWORD cbValueMax, +// SDWORD FAR *pcbValue); typedef RETCODE (SQL_API * pfnSQLDataSources)( HENV henv, @@ -315,7 +315,7 @@ extern pfnSQLEndTran pSQLEndTran; extern pfnSQLExecDirect pSQLExecDirect; extern pfnSQLTables pSQLTables; extern pfnSQLColumns pSQLColumns; -extern pfnSQLBindParameter pSQLBindParameter; +// extern pfnSQLBindParameter pSQLBindParameter; extern pfnSQLPrimaryKeys pSQLPrimaryKeys; extern pfnSQLSetEnvAttr pSQLSetEnvAttr; extern pfnSQLFreeConnect pSQLFreeConnect; From 358ac4af6d9a596b1250c59088f10deae8d18885 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 17:33:45 -0400 Subject: [PATCH 192/511] only include sql header files if not using dynodbc --- src/odbc.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/odbc.h b/src/odbc.h index 114a18be..25d62b1c 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -22,9 +22,13 @@ #include #include +#ifdef dynodbc +#include "dynodbc.h" +#else #include #include #include +#endif using namespace v8; using namespace node; From 03f4a54883364844789c3dc36cdaf17344efc8ed Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 17:35:01 -0400 Subject: [PATCH 193/511] add a couple library paths for testing --- test/common.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/common.js b/test/common.js index 962ea529..8755d5cf 100644 --- a/test/common.js +++ b/test/common.js @@ -1,3 +1,7 @@ +var odbc = require("../"); +//odbc.library = '/usr/lib/odbc/libsqlite3odbc-0.91'; +//odbc.library = '/usr/lib/x86_64-linux-gnu/odbc/libtdsodbc'; + exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; //exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; //exports.connectionString = process.env.ODBC_CONNETION_STRING; From d00973c511c275d8ed59cbf53867cdb5b5080e74 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 17:47:00 -0400 Subject: [PATCH 194/511] 0.5.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2691a9bc..6e703979 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.3", + "version": "0.5.4", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 7d04d6d265b85fb3d0e67319e4f04f7552f716fa Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 17:49:06 -0400 Subject: [PATCH 195/511] add @paulhendrix to contributors --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6961128e..12017edc 100644 --- a/README.md +++ b/README.md @@ -396,6 +396,7 @@ contributors * Yorick * Joachim Kainz * Oleg Efimov +* paulhendrix license ------- From 7bc1716e268d9fe646e1e50d81769f4b3857dc9a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Apr 2013 20:01:53 -0400 Subject: [PATCH 196/511] implement ODBCResult::FetchSync --- lib/odbc.js | 25 +++++++++++- src/odbc_result.cpp | 96 +++++++++++++++++++++++++++++++++++++++++++++ src/odbc_result.h | 1 + 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/lib/odbc.js b/lib/odbc.js index 65ecdf5a..5dece916 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -90,7 +90,13 @@ Database.prototype.openSync = function (connectionString) { self.conn = self.odbc.createConnectionSync(); - return self.conn.openSync(connectionString); + var result = self.conn.openSync(connectionString); + + if (result) { + self.connected = true; + } + + return result; } Database.prototype.close = function (cb) { @@ -185,6 +191,23 @@ Database.prototype.queryResult = function (sql, params, cb) { }); }; +Database.prototype.queryResultSync = function (sql, params) { + var self = this, result; + + if (!self.connected) { + throw ({ message : "Connection not open."}); + } + + if (params) { + result = self.conn.querySync(sql, params); + } + else { + result = self.conn.querySync(sql); + } + + return result; +}; + Database.prototype.querySync = function (sql, params) { var self = this, result; diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index c7ff7caf..3eb751c7 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -52,6 +52,7 @@ void ODBCResult::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "moreResultsSync", MoreResultsSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchSync", FetchSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAllSync", FetchAllSync); // Attach the Database Constructor to the target object @@ -278,6 +279,101 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { return; } +/* + * FetchSync + */ + +Handle ODBCResult::FetchSync(const Arguments& args) { + DEBUG_PRINTF("ODBCResult::FetchSync\n"); + + HandleScope scope; + + ODBCResult* objResult = ObjectWrap::Unwrap(args.Holder()); + + Local objError; + bool moreWork = true; + bool error = false; + int fetchMode = FETCH_OBJECT; + + if (args.Length() == 1 && args[0]->IsObject()) { + + Local obj = args[0]->ToObject(); + + if (obj->Has(OPTION_FETCH_MODE) && obj->Get(OPTION_FETCH_MODE)->IsInt32()) { + fetchMode = obj->Get(OPTION_FETCH_MODE)->ToInt32()->Value(); + } + else { + fetchMode = FETCH_OBJECT; + } + } + + SQLRETURN ret = SQLFetch(objResult->m_hSTMT); + + if (objResult->colCount == 0) { + objResult->columns = ODBC::GetColumns( + objResult->m_hSTMT, + &objResult->colCount); + } + + //check to see if the result has no columns + if (objResult->colCount == 0) { + moreWork = false; + } + //check to see if there was an error + else if (ret == SQL_ERROR) { + moreWork = false; + error = true; + + objError = ODBC::GetSQLError( + objResult->m_hENV, + objResult->m_hDBC, + objResult->m_hSTMT, + (char *) "Error in ODBCResult::UV_AfterFetch"); + } + //check to see if we are at the end of the recordset + else if (ret == SQL_NO_DATA) { + moreWork = false; + } + + if (moreWork) { + Handle data; + + if (fetchMode == FETCH_ARRAY) { + data = ODBC::GetRecordArray( + objResult->m_hSTMT, + objResult->columns, + &objResult->colCount, + objResult->buffer, + objResult->bufferLength); + } + else { + data = ODBC::GetRecordTuple( + objResult->m_hSTMT, + objResult->columns, + &objResult->colCount, + objResult->buffer, + objResult->bufferLength); + } + + return scope.Close(data); + } + else { + ODBC::FreeColumns(objResult->columns, &objResult->colCount); + + Handle args[2]; + + //if there was an error, pass that as arg[0] otherwise Null + if (error) { + ThrowException(objError); + + return scope.Close(Null()); + } + else { + return scope.Close(Null()); + } + } +} + /* * FetchAll */ diff --git a/src/odbc_result.h b/src/odbc_result.h index 50711de0..c9b55e51 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -52,6 +52,7 @@ class ODBCResult : public node::ObjectWrap { //sync methods static Handle CloseSync(const Arguments& args); static Handle MoreResultsSync(const Arguments& args); + static Handle FetchSync(const Arguments& args); static Handle FetchAllSync(const Arguments& args); struct fetch_work_data { From c5761a9ce86062cd5fb71a2cc67cb9ea215cf5e2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Apr 2013 13:41:22 -0400 Subject: [PATCH 197/511] Avoid copying strings in synchronous methods --- src/odbc_connection.cpp | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 551cf83d..8bb95747 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -272,15 +272,8 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { Local objError; SQLRETURN ret; bool err = false; - char* connectionString; char connstr[1024]; - //allocate memory for the connection string - connectionString = (char *) malloc(connection.length() +1); - - //copy the connection string to the work data - strcpy(connectionString, *connection); - uv_mutex_lock(&ODBC::g_odbcMutex); //TODO: make this configurable @@ -290,8 +283,8 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { ret = SQLDriverConnect( conn->m_hDBC, NULL, - (SQLCHAR*) connectionString, - strlen(connectionString), + (SQLCHAR*) *connection, + connection.length(), (SQLCHAR*) connstr, 1024, NULL, @@ -333,8 +326,6 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { } uv_mutex_unlock(&ODBC::g_odbcMutex); - - free(connectionString); if (err) { ThrowException(objError); @@ -890,7 +881,6 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { SQLRETURN ret; HSTMT hSTMT; int paramCount = 0; - char* sqlString; bool noResultObject = false; //Check arguments for different variations of calling this function @@ -961,10 +951,6 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { ); } //Done checking arguments - - sqlString = (char *) malloc(sql->length() +1); - - strcpy(sqlString, **sql); uv_mutex_lock(&ODBC::g_odbcMutex); @@ -980,15 +966,15 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { // execute the query directly ret = SQLExecDirect( hSTMT, - (SQLCHAR *) sqlString, - strlen(sqlString)); + (SQLCHAR *) **sql, + sql->length()); } else { // prepare statement, bind parameters and execute statement ret = SQLPrepare( hSTMT, - (SQLCHAR *) sqlString, - strlen(sqlString)); + (SQLCHAR *) **sql, + sql->length()); if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { for (int i = 0; i < paramCount; i++) { @@ -1034,8 +1020,6 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { free(params); } - - free(sqlString); //check to see if there was an error during execution if(ret == SQL_ERROR) { From d486110c7add7df03f20929fa2f1222d86b7885d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Apr 2013 13:41:49 -0400 Subject: [PATCH 198/511] Make sure result.closeSync returns true --- src/odbc_result.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 3eb751c7..0d734c63 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -642,7 +642,6 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { /* * CloseSync * - * When calling a Close method, */ Handle ODBCResult::CloseSync(const Arguments& args) { @@ -673,7 +672,7 @@ Handle ODBCResult::CloseSync(const Arguments& args) { uv_mutex_unlock(&ODBC::g_odbcMutex); } - return scope.Close(Undefined()); + return scope.Close(True()); } Handle ODBCResult::MoreResultsSync(const Arguments& args) { From 0b13be84ebc74dcfb07a50b205dfe7a7fc5b8d10 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Apr 2013 13:43:24 -0400 Subject: [PATCH 199/511] Implement ODBCStatement::ExecuteSync() and ExecuteDirectSync() --- src/odbc_statement.cpp | 115 +++++++++++++++++++++++++++++++++++++++-- src/odbc_statement.h | 2 + 2 files changed, 113 insertions(+), 4 deletions(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 9cc4eaff..7c9c0e78 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -47,7 +47,10 @@ void ODBCStatement::Init(v8::Handle target) { // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "execute", Execute); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeSync", ExecuteSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirect", ExecuteDirect); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirectSync", ExecuteDirectSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare); NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepareSync", PrepareSync); @@ -114,7 +117,6 @@ Handle ODBCStatement::New(const Arguments& args) { /* * Execute - * */ Handle ODBCStatement::Execute(const Arguments& args) { @@ -179,7 +181,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { } else { Local args[3]; - bool canFreeHandle; + bool canFreeHandle = false; args[0] = External::New(self->m_hENV); args[1] = External::New(self->m_hDBC); @@ -231,6 +233,66 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { scope.Close(Undefined()); } +/* + * ExecuteSync + * + */ + +Handle ODBCStatement::ExecuteSync(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::ExecuteSync\n"); + + HandleScope scope; + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + SQLRETURN ret = SQLExecute(stmt->m_hSTMT); + + if (stmt->paramCount) { + Parameter prm; + + //free parameter memory + for (int i = 0; i < stmt->paramCount; i++) { + if (prm = stmt->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + stmt->paramCount = 0; + + free(stmt->params); + } + + if(ret == SQL_ERROR) { + ThrowException(ODBC::GetSQLError( + stmt->m_hENV, + stmt->m_hDBC, + stmt->m_hSTMT, + (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" + )); + + return scope.Close(Null()); + } + else { + Local args[3]; + bool canFreeHandle = false; + + args[0] = External::New(stmt->m_hENV); + args[1] = External::New(stmt->m_hDBC); + args[2] = External::New(stmt->m_hSTMT); + args[3] = External::New(&canFreeHandle); + + Local js_result(ODBCResult::constructor_template-> + GetFunction()->NewInstance(4, args)); + + return scope.Close(js_result); + } +} + /* * ExecuteDirect * @@ -338,6 +400,51 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { scope.Close(Undefined()); } +/* + * ExecuteDirectSync + * + */ + +Handle ODBCStatement::ExecuteDirectSync(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::ExecuteDirectSync\n"); + + HandleScope scope; + + REQ_STR_ARG(0, sql); + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + SQLRETURN ret = SQLExecDirect( + stmt->m_hSTMT, + (SQLCHAR *) *sql, + sql.length()); + + if(ret == SQL_ERROR) { + ThrowException(ODBC::GetSQLError( + stmt->m_hENV, + stmt->m_hDBC, + stmt->m_hSTMT, + (char *) "[node-odbc] Error in ODBCStatement::ExecuteDirectSync" + )); + + return scope.Close(Null()); + } + else { + Local args[3]; + bool canFreeHandle = false; + + args[0] = External::New(stmt->m_hENV); + args[1] = External::New(stmt->m_hDBC); + args[2] = External::New(stmt->m_hSTMT); + args[3] = External::New(&canFreeHandle); + + Persistent js_result(ODBCResult::constructor_template-> + GetFunction()->NewInstance(4, args)); + + return scope.Close(js_result); + } +} + /* * PrepareSync * @@ -448,7 +555,7 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { //First thing, let's check if the execution of the query returned any errors if(data->result == SQL_ERROR) { - ODBC::CallbackSQLError( + ODBC::CallbackSQLError( data->stmt->m_hENV, data->stmt->m_hDBC, data->stmt->m_hSTMT, @@ -695,7 +802,7 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { */ Handle ODBCStatement::CloseSync(const Arguments& args) { - DEBUG_PRINTF("ODBCStatement::Execute\n"); + DEBUG_PRINTF("ODBCStatement::CloseSync\n"); HandleScope scope; diff --git a/src/odbc_statement.h b/src/odbc_statement.h index 1f2d8dd1..7c55f705 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -59,6 +59,8 @@ class ODBCStatement : public node::ObjectWrap { static Handle BindSync(const Arguments& args); static Handle PrepareSync(const Arguments& args); static Handle CloseSync(const Arguments& args); + static Handle ExecuteSync(const Arguments& args); + static Handle ExecuteDirectSync(const Arguments& args); struct Fetch_Request { Persistent callback; From f0b634a1d1df10646641e95209191ce450481590 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Apr 2013 13:43:54 -0400 Subject: [PATCH 200/511] Insure that ODBCStatement methods can be queued --- lib/odbc.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/odbc.js b/lib/odbc.js index 5dece916..d1ef867d 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -384,6 +384,8 @@ odbc.ODBCStatement.prototype._bind = odbc.ODBCStatement.prototype.bind; odbc.ODBCStatement.prototype.execute = function (cb) { var self = this; + self.queue = self.queue || new SimpleQueue(); + self.queue.push(function (next) { self._execute(function (err, result) { cb(err, result); @@ -396,6 +398,8 @@ odbc.ODBCStatement.prototype.execute = function (cb) { odbc.ODBCStatement.prototype.executeDirect = function (sql, cb) { var self = this; + self.queue = self.queue || new SimpleQueue(); + self.queue.push(function (next) { self._executeDirect(sql, function (err, result) { cb(err, result); @@ -408,6 +412,8 @@ odbc.ODBCStatement.prototype.executeDirect = function (sql, cb) { odbc.ODBCStatement.prototype.prepare = function (sql, cb) { var self = this; + self.queue = self.queue || new SimpleQueue(); + self.queue.push(function (next) { self._prepare(sql, function (err) { cb(err); @@ -420,6 +426,8 @@ odbc.ODBCStatement.prototype.prepare = function (sql, cb) { odbc.ODBCStatement.prototype.bind = function (ary, cb) { var self = this; + self.queue = self.queue || new SimpleQueue(); + self.queue.push(function (next) { self._bind(ary, function (err) { cb(err); From 23e9e1617c211194e12bb4e170d4e3376f793011 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Apr 2013 13:44:24 -0400 Subject: [PATCH 201/511] added test for statement executeSync --- test/test-binding-statement-executeSync.js | 81 ++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 test/test-binding-statement-executeSync.js diff --git a/test/test-binding-statement-executeSync.js b/test/test-binding-statement-executeSync.js new file mode 100644 index 00000000..20500a37 --- /dev/null +++ b/test/test-binding-statement-executeSync.js @@ -0,0 +1,81 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.ODBC() + , assert = require("assert") + , exitCode = 0 + ; + +db.createConnection(function (err, conn) { + conn.open(common.connectionString, function (err) { + conn.createStatement(function (err, stmt) { + var r, result, caughtError; + + //try excuting without preparing or binding. + try { + result = stmt.executeSync(); + } + catch (e) { + caughtError = e; + } + + try { + assert.ok(caughtError); + } + catch (e) { + console.log(e.message); + exitCode = 1; + } + + //try incorrectly binding a string and then executeSync + try { + r = stmt.bind("select 1 + 1 as col1"); + } + catch (e) { + caughtError = e; + } + + try { + assert.equal(caughtError.message, "Argument 1 must be an Array"); + + r = stmt.prepareSync("select 1 + ? as col1"); + assert.equal(r, true, "prepareSync did not return true"); + + r = stmt.bindSync([2]); + assert.equal(r, true, "bindSync did not return true"); + + result = stmt.executeSync(); + assert.equal(result.constructor.name, "ODBCResult"); + + r = result.fetchAllSync(); + assert.deepEqual(r, [ { col1: 3 } ]); + + r = result.closeSync(); + assert.equal(r, true, "closeSync did not return true"); + + result = stmt.executeSync(); + assert.equal(result.constructor.name, "ODBCResult"); + + r = result.fetchAllSync(); + assert.deepEqual(r, [ { col1: 3 } ]); + + console.log(r); + } + catch (e) { + console.log(e); + + exitCode = 1; + } + + conn.close(function () { + if (exitCode) { + console.log("failed"); + } + else { + console.log("success"); + } + + process.exit(exitCode); + }); + }); + }); +}); From abbd3d6e3ef6d3c8209f49a7b5333544191d0922 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Apr 2013 13:57:03 -0400 Subject: [PATCH 202/511] Updated readme --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 12017edc..5179f964 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,41 @@ By default, the tests are setup to run against a sqlite3 database which is created at test time. This will require proper installation of the sqlite odbc driver. On Ubuntu: `sudo apt-get install libsqliteodbc` +build options +------------- + +If you would like to enable debugging messages to be displayed you can add the +flag `DEBUG` to the defines section of the `binding.gyp` file and then execute +`node-gyp rebuild`. + +```javascript + +'defines' : [ + "DEBUG" +], + +``` + +You may also enable the ability to load a specific ODBC driver and bypass the +ODBC driver management layer. A performance increase of ~5Kqps was seen using +this method with the libsqlite3odbc driver. To do this, specify the `dynodbc` +flag in the defines section of the `binding.gyp` file. You will also need to +remove any library references in `binding.gyp`. Then execute `node-gyp +rebuild`. + +```javascript + +'defines' : [ + "dynodbc" +], +'conditions' : [ + [ 'OS == "linux"', { + 'libraries' : [ + //remove this: '-lodbc' + ], + +``` + tips ---- ### Using node < v0.10 on Linux From 0855800d2c85c93e1134f9c475153d5f0e56f37f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 12:14:48 -0400 Subject: [PATCH 203/511] Fix internal handling of canFreeHandle on ODBCResult::New() ODBCResult instances which are created from an ODBCStatement instance should not be allowed to destroy themselves (or free the hSTMT to be more specific) because the ODBCStatement can still be used to do stuff with that hSTMT. Only ODBCResult instances that are created from an ODBCConnection instance are allowed to actually destroy the hSTMT. Previously, we were not properly derefencing the canFreeHandle flag passed to ODBCResult::New() so it was evaluating to true in every case. This was causing the ODBCResult::CloseSync() method to always destroy the handle and thus cause the behaviour described in #32. --- lib/odbc.js | 8 ++- src/odbc_result.cpp | 9 ++- src/odbc_statement.cpp | 8 ++- test/test-prepareSync-multiple-execution.js | 76 +++++++++++++++++++++ 4 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 test/test-prepareSync-multiple-execution.js diff --git a/lib/odbc.js b/lib/odbc.js index d1ef867d..1aa07c9b 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -140,14 +140,18 @@ Database.prototype.query = function (sql, params, cb) { result.fetchAll(function (err, data) { var moreResults = result.moreResultsSync(); + //close the result before calling back + //if there are not more result sets + if (!moreResults) { + result.closeSync(); + } + cb(err, data, moreResults); if (moreResults) { return fetchMore(); } else { - result.closeSync(); - return next(); } }); diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 0d734c63..4336b6e1 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -98,10 +98,10 @@ Handle ODBCResult::New(const Arguments& args) { HENV hENV = static_cast(js_henv->Value()); HDBC hDBC = static_cast(js_hdbc->Value()); HSTMT hSTMT = static_cast(js_hstmt->Value()); - bool canFreeHandle = static_cast(js_canFreeHandle->Value()); + bool* canFreeHandle = static_cast(js_canFreeHandle->Value()); //create a new OBCResult object - ODBCResult* objODBCResult = new ODBCResult(hENV, hDBC, hSTMT, canFreeHandle); + 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, @@ -645,7 +645,7 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { */ Handle ODBCResult::CloseSync(const Arguments& args) { - DEBUG_PRINTF("ODBCResult::Close\n"); + DEBUG_PRINTF("ODBCResult::CloseSync\n"); HandleScope scope; @@ -653,6 +653,9 @@ Handle ODBCResult::CloseSync(const Arguments& args) { ODBCResult* result = ObjectWrap::Unwrap(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(); } diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 7c9c0e78..e008f1e3 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -650,7 +650,13 @@ Handle ODBCStatement::BindSync(const Arguments& args) { return scope.Close(True()); } else { - //TODO: throw an error object + ThrowException(ODBC::GetSQLError( + stmt->m_hENV, + stmt->m_hDBC, + stmt->m_hSTMT, + (char *) "[node-odbc] Error in ODBCStatement::BindSync" + )); + return scope.Close(False()); } diff --git a/test/test-prepareSync-multiple-execution.js b/test/test-prepareSync-multiple-execution.js new file mode 100644 index 00000000..6684c514 --- /dev/null +++ b/test/test-prepareSync-multiple-execution.js @@ -0,0 +1,76 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +var count = 0; +var iterations = 10; + +db.open(common.connectionString, function(err) { + if(err) { + console.log(err) + + return finish(1); + } + + common.createTables(db, function (err, data) { + if (err) { + console.log(err); + + return finish(2); + } + + var stmt = db.prepareSync("insert into " + common.tableName + " (colint, coltext) VALUES (?, ?)"); + assert.equal(stmt.constructor.name, "ODBCStatement"); + + recursive(stmt); + }); +}); + +function finish(retValue) { + console.log("finish exit value: %s", retValue); + + db.close(function (err) { + if (err) console.log (err); + + process.exit(retValue || 0); + }); +} + +function recursive (stmt) { + try { + var result = stmt.bindSync([4, 'hello world']); + assert.equal(result, true); + } + catch (e) { + console.log(e.message); + finish(3); + } + + stmt.execute(function (err, result) { + if (err) { + console.log(err.message); + + return finish(4); + } + + result.closeSync(); + count += 1; + + console.log("count %s, iterations %s", count, iterations); + + if (count <= iterations) { + setTimeout(function(){ + recursive(stmt); + },100); + } + else { + console.log(db.querySync("select * from " + common.tableName)); + + common.dropTables(db, function () { + return finish(0); + }); + } + }); +} From 7a73458ca4da0478b4161920e59a7edceab237c0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 12:44:58 -0400 Subject: [PATCH 204/511] Fix debug messages --- src/odbc_statement.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index e008f1e3..a745623a 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -622,7 +622,7 @@ Handle ODBCStatement::BindSync(const Arguments& args) { prm = stmt->params[i]; DEBUG_PRINTF( - "ODBC::BindSync - param[%i]: c_type=%i type=%i " + "ODBCStatement::BindSync - param[%i]: c_type=%i type=%i " "buffer_length=%i size=%i length=%i &length=%X decimals=%i value=%s\n", i, prm.c_type, prm.type, prm.buffer_length, prm.size, prm.length, &stmt->params[i].length, prm.decimals, prm.buffer @@ -733,7 +733,7 @@ void ODBCStatement::UV_Bind(uv_work_t* req) { prm = data->stmt->params[i]; DEBUG_PRINTF( - "ODBC::UV_Bind - param[%i]: c_type=%i type=%i " + "ODBCStatement::UV_Bind - param[%i]: c_type=%i type=%i " "buffer_length=%i size=%i length=%i &length=%X decimals=%i value=%s\n", i, prm.c_type, prm.type, prm.buffer_length, prm.size, prm.length, &data->stmt->params[i].length, prm.decimals, prm.buffer From d2e94b39780aaa6f47d6bbedc53e5952ff78f6d9 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 12:45:28 -0400 Subject: [PATCH 205/511] Test: drop tables before starting new test --- test/test-prepareSync-multiple-execution.js | 22 +++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/test/test-prepareSync-multiple-execution.js b/test/test-prepareSync-multiple-execution.js index 6684c514..8f2f5dce 100644 --- a/test/test-prepareSync-multiple-execution.js +++ b/test/test-prepareSync-multiple-execution.js @@ -14,17 +14,19 @@ db.open(common.connectionString, function(err) { return finish(1); } - common.createTables(db, function (err, data) { - if (err) { - console.log(err); + common.dropTables(db, function () { + common.createTables(db, function (err, data) { + if (err) { + console.log(err); + + return finish(2); + } - return finish(2); - } - - var stmt = db.prepareSync("insert into " + common.tableName + " (colint, coltext) VALUES (?, ?)"); - assert.equal(stmt.constructor.name, "ODBCStatement"); - - recursive(stmt); + var stmt = db.prepareSync("insert into " + common.tableName + " (colint, coltext) VALUES (?, ?)"); + assert.equal(stmt.constructor.name, "ODBCStatement"); + + recursive(stmt); + }); }); }); From 15d4f96cb7c6de2c491c5ec3a10bd01a46e95da6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 12:45:47 -0400 Subject: [PATCH 206/511] 0.5.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6e703979..576192b4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.4", + "version": "0.5.5", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From e5a8a488b86d96f2a1bcd237363910d0b78101ff Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 13:55:18 -0400 Subject: [PATCH 207/511] Implement ODBCStatement::ExecuteNonQuery() --- lib/odbc.js | 15 ++++ src/odbc_statement.cpp | 161 +++++++++++++++++++++++++++++++++++++++++ src/odbc_statement.h | 9 ++- 3 files changed, 183 insertions(+), 2 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 1aa07c9b..96f3cf0e 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -382,6 +382,7 @@ Database.prototype.prepareSync = function (sql, cb) { //Proxy all of the asynchronous functions so that they are queued odbc.ODBCStatement.prototype._execute = odbc.ODBCStatement.prototype.execute; odbc.ODBCStatement.prototype._executeDirect = odbc.ODBCStatement.prototype.executeDirect; +odbc.ODBCStatement.prototype._executeNonQuery = odbc.ODBCStatement.prototype.executeNonQuery; odbc.ODBCStatement.prototype._prepare = odbc.ODBCStatement.prototype.prepare; odbc.ODBCStatement.prototype._bind = odbc.ODBCStatement.prototype.bind; @@ -413,6 +414,20 @@ odbc.ODBCStatement.prototype.executeDirect = function (sql, cb) { }); }; +odbc.ODBCStatement.prototype.executeNonQuery = function (cb) { + var self = this; + + self.queue = self.queue || new SimpleQueue(); + + self.queue.push(function (next) { + self._executeNonQuery(function (err, result) { + cb(err, result); + + return next(); + }); + }); +}; + odbc.ODBCStatement.prototype.prepare = function (sql, cb) { var self = this; diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index a745623a..ed01507d 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -52,6 +52,9 @@ void ODBCStatement::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirect", ExecuteDirect); NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirectSync", ExecuteDirectSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeNonQuery", Execute); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeNonQuerySync", ExecuteNonQuerySync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare); NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepareSync", PrepareSync); @@ -293,6 +296,164 @@ Handle ODBCStatement::ExecuteSync(const Arguments& args) { } } +/* + * ExecuteNonQuery + */ + +Handle ODBCStatement::ExecuteNonQuery(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::ExecuteNonQuery\n"); + + HandleScope scope; + + REQ_FUN_ARG(0, cb); + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + execute_work_data* data = + (execute_work_data *) calloc(1, sizeof(execute_work_data)); + + data->cb = Persistent::New(cb); + + data->stmt = stmt; + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_ExecuteNonQuery, + (uv_after_work_cb)UV_AfterExecuteNonQuery); + + stmt->Ref(); + + return scope.Close(Undefined()); +} + +void ODBCStatement::UV_ExecuteNonQuery(uv_work_t* req) { + DEBUG_PRINTF("ODBCStatement::ExecuteNonQuery\n"); + + execute_work_data* data = (execute_work_data *)(req->data); + + SQLRETURN ret; + + ret = SQLExecute(data->stmt->m_hSTMT); + + data->result = ret; +} + +void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCStatement::ExecuteNonQuery\n"); + + execute_work_data* data = (execute_work_data *)(req->data); + + HandleScope scope; + + //an easy reference to the statment object + ODBCStatement* self = data->stmt->self(); + + //First thing, let's check if the execution of the query returned any errors + if(data->result == SQL_ERROR) { + ODBC::CallbackSQLError( + self->m_hENV, + self->m_hDBC, + self->m_hSTMT, + data->cb); + } + else { + Local args[2]; + + args[0] = Local::New(Null()); + args[1] = Local::New(True()); + + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + } + + TryCatch try_catch; + + self->Unref(); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + + if (data->stmt->paramCount) { + Parameter prm; + + //free parameter memory + for (int i = 0; i < data->stmt->paramCount; i++) { + if (prm = data->stmt->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + data->stmt->paramCount = 0; + + free(data->stmt->params); + } + + free(data); + free(req); + + scope.Close(Undefined()); +} + +/* + * ExecuteNonQuerySync + * + */ + +Handle ODBCStatement::ExecuteNonQuerySync(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::ExecuteNonQuerySync\n"); + + HandleScope scope; + + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + + SQLRETURN ret = SQLExecute(stmt->m_hSTMT); + + if (stmt->paramCount) { + Parameter prm; + + //free parameter memory + for (int i = 0; i < stmt->paramCount; i++) { + if (prm = stmt->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + stmt->paramCount = 0; + + free(stmt->params); + } + + if(ret == SQL_ERROR) { + ThrowException(ODBC::GetSQLError( + stmt->m_hENV, + stmt->m_hDBC, + stmt->m_hSTMT, + (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" + )); + + return scope.Close(Null()); + } + else { + return scope.Close(True()); + } +} + /* * ExecuteDirect * diff --git a/src/odbc_statement.h b/src/odbc_statement.h index 7c55f705..c0269179 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -47,6 +47,10 @@ class ODBCStatement : public node::ObjectWrap { static void UV_ExecuteDirect(uv_work_t* work_req); static void UV_AfterExecuteDirect(uv_work_t* work_req, int status); + static Handle ExecuteNonQuery(const Arguments& args); + static void UV_ExecuteNonQuery(uv_work_t* work_req); + static void UV_AfterExecuteNonQuery(uv_work_t* work_req, int status); + static Handle Prepare(const Arguments& args); static void UV_Prepare(uv_work_t* work_req); static void UV_AfterPrepare(uv_work_t* work_req, int status); @@ -56,11 +60,12 @@ class ODBCStatement : public node::ObjectWrap { static void UV_AfterBind(uv_work_t* work_req, int status); //sync methods - static Handle BindSync(const Arguments& args); - static Handle PrepareSync(const Arguments& args); static Handle CloseSync(const Arguments& args); static Handle ExecuteSync(const Arguments& args); static Handle ExecuteDirectSync(const Arguments& args); + static Handle ExecuteNonQuerySync(const Arguments& args); + static Handle PrepareSync(const Arguments& args); + static Handle BindSync(const Arguments& args); struct Fetch_Request { Persistent callback; From 270a68e003ac10db2bc6da5a945951b90e200509 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 14:37:38 -0400 Subject: [PATCH 208/511] Return number of rows affected from ODBCStatement::ExecuteNonQuery[Sync] --- src/odbc_statement.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index ed01507d..c7094f13 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -361,10 +361,18 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { data->cb); } else { + SQLLEN rowCount = 0; + + SQLRETURN ret = SQLRowCount(self->m_hSTMT, &rowCount); + + if (!SQL_SUCCEEDED(ret)) { + rowCount = 0; + } + Local args[2]; args[0] = Local::New(Null()); - args[1] = Local::New(True()); + args[1] = Local::New(Number::New(rowCount)); data->cb->Call(Context::GetCurrent()->Global(), 2, args); } @@ -450,7 +458,15 @@ Handle ODBCStatement::ExecuteNonQuerySync(const Arguments& args) { return scope.Close(Null()); } else { - return scope.Close(True()); + SQLLEN rowCount = 0; + + SQLRETURN ret = SQLRowCount(stmt->m_hSTMT, &rowCount); + + if (!SQL_SUCCEEDED(ret)) { + rowCount = 0; + } + + return scope.Close(Number::New(rowCount)); } } From f64ba5be8f2e09b2ee27868c554892d15900f30e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 15:04:29 -0400 Subject: [PATCH 209/511] Accept close options in ODBCStatement::CloseSync() --- src/odbc_statement.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index c7094f13..9a8abc1e 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -989,9 +989,23 @@ Handle ODBCStatement::CloseSync(const Arguments& args) { HandleScope scope; + OPT_INT_ARG(0, closeOption, SQL_DESTROY); + ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); - stmt->Free(); + DEBUG_PRINTF("ODBCStatement::CloseSync closeOption=%i\n", + closeOption); + + if (closeOption == SQL_DESTROY) { + stmt->Free(); + } + else { + uv_mutex_lock(&ODBC::g_odbcMutex); + + SQLFreeStmt(stmt->m_hSTMT, closeOption); + + uv_mutex_unlock(&ODBC::g_odbcMutex); + } return scope.Close(True()); } From 25c95d0fe042c28c792e05cdfb88ffc41017533e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 15:08:53 -0400 Subject: [PATCH 210/511] test: log some information in test-describe-column --- test/test-describe-column.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/test/test-describe-column.js b/test/test-describe-column.js index 9bbd3c76..091b5a1d 100644 --- a/test/test-describe-column.js +++ b/test/test-describe-column.js @@ -5,15 +5,31 @@ var common = require("./common") ; db.open(common.connectionString, function(err) { + if (err) console.log(err); + assert.equal(err, null); - common.dropTables(db, function () { - common.createTables(db, function () { + console.log("connected"); + + common.dropTables(db, function (err) { + if (err) console.log(err.message); + + console.log("tables dropped"); + + common.createTables(db, function (err) { + if (err) console.log(err.message); + + console.log("tables created"); + db.describe({ database : common.databaseName, table : common.tableName, column : 'COLDATETIME' }, function (err, data) { + if (err) console.log(err.message); + + console.log(data); + db.close(function () { assert.equal(err, null); assert.ok(data.length, "No records returned when attempting to describe the column COLDATETIME"); From a412805221ec6da3d450dee507df3e186a2a8f32 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 18:03:03 -0400 Subject: [PATCH 211/511] ODBCResult: remove unuse variable --- src/odbc_result.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 4336b6e1..9ca4189c 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -359,9 +359,7 @@ Handle ODBCResult::FetchSync(const Arguments& args) { } else { ODBC::FreeColumns(objResult->columns, &objResult->colCount); - - Handle args[2]; - + //if there was an error, pass that as arg[0] otherwise Null if (error) { ThrowException(objError); From a1f69d1d8095874710cbf15413d7b97f5c4f49af Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 18:03:10 -0400 Subject: [PATCH 212/511] 0.5.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 576192b4..deb80a33 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.5", + "version": "0.5.6", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From ac63fb2180d6f569c7fc5a21c609f935451ceaf0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 20:11:44 -0400 Subject: [PATCH 213/511] Allocate canFreeHandle on the heap before instantiating instances of ODBCResult --- src/odbc_connection.cpp | 10 +++++----- src/odbc_result.cpp | 3 +++ src/odbc_statement.cpp | 16 ++++++++-------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 8bb95747..dfd4c55b 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -806,12 +806,12 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { } else { Local args[4]; - bool canFreeHandle = true; + bool* canFreeHandle = new bool(true); args[0] = External::New(data->conn->m_hENV); args[1] = External::New(data->conn->m_hDBC); args[2] = External::New(data->hSTMT); - args[3] = External::New(&canFreeHandle); + args[3] = External::New(canFreeHandle); Persistent js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); @@ -1047,12 +1047,12 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { } else { Local args[4]; - bool canFreeHandle; + bool* canFreeHandle = new bool(true); args[0] = External::New(conn->m_hENV); args[1] = External::New(conn->m_hDBC); args[2] = External::New(hSTMT); - args[3] = External::New(&canFreeHandle); + args[3] = External::New(canFreeHandle); Persistent js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); @@ -1331,4 +1331,4 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { else { return scope.Close(True()); } -} \ No newline at end of file +} diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 9ca4189c..1db2d3f4 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -110,6 +110,9 @@ Handle ODBCResult::New(const Arguments& args) { objODBCResult->m_canFreeHandle ); + //free the pointer to canFreeHandle + delete canFreeHandle; + //specify the buffer length objODBCResult->bufferLength = MAX_VALUE_SIZE - 1; diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 9a8abc1e..98bef71e 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -184,12 +184,12 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { } else { Local args[3]; - bool canFreeHandle = false; + bool* canFreeHandle = new bool(false); args[0] = External::New(self->m_hENV); args[1] = External::New(self->m_hDBC); args[2] = External::New(self->m_hSTMT); - args[3] = External::New(&canFreeHandle); + args[3] = External::New(canFreeHandle); Persistent js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); @@ -282,12 +282,12 @@ Handle ODBCStatement::ExecuteSync(const Arguments& args) { } else { Local args[3]; - bool canFreeHandle = false; + bool* canFreeHandle = new bool(false); args[0] = External::New(stmt->m_hENV); args[1] = External::New(stmt->m_hDBC); args[2] = External::New(stmt->m_hSTMT); - args[3] = External::New(&canFreeHandle); + args[3] = External::New(canFreeHandle); Local js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); @@ -544,12 +544,12 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { } else { Local args[3]; - bool canFreeHandle = false; + bool* canFreeHandle = new bool(false); args[0] = External::New(self->m_hENV); args[1] = External::New(self->m_hDBC); args[2] = External::New(self->m_hSTMT); - args[3] = External::New(&canFreeHandle); + args[3] = External::New(canFreeHandle); Persistent js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); @@ -608,12 +608,12 @@ Handle ODBCStatement::ExecuteDirectSync(const Arguments& args) { } else { Local args[3]; - bool canFreeHandle = false; + bool* canFreeHandle = new bool(false); args[0] = External::New(stmt->m_hENV); args[1] = External::New(stmt->m_hDBC); args[2] = External::New(stmt->m_hSTMT); - args[3] = External::New(&canFreeHandle); + args[3] = External::New(canFreeHandle); Persistent js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); From d555b337b15dcd5272072083bd3b6ea21af4b03b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 21:32:01 -0400 Subject: [PATCH 214/511] Only free ODBCStatement parameters on Free(), Bind() and BindSync() If parameters have been previously been bound, then the should be freed before any new parameters are created. This would happen in Bind() or BindSync(). They should also be freed in the dtor of the ODBCStatment class. This will allow us to bind once and repeatedly execte() and insert the same values in case anyone needs to do this for some reason. --- src/odbc_statement.cpp | 153 ++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 80 deletions(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 98bef71e..dec085d0 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -75,6 +75,29 @@ ODBCStatement::~ODBCStatement() { } void ODBCStatement::Free() { + DEBUG_PRINTF("ODBCStatement::Free\n"); + //if we previously had parameters, then be sure to free them + if (paramCount) { + int count = paramCount; + paramCount = 0; + + Parameter prm; + + //free parameter memory + for (int i = 0; i < count; i++) { + if (prm = params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + free(params); + } + if (m_hSTMT) { uv_mutex_lock(&ODBC::g_odbcMutex); @@ -90,6 +113,7 @@ void ODBCStatement::Free() { } Handle ODBCStatement::New(const Arguments& args) { + DEBUG_PRINTF("ODBCStatement::New\n"); HandleScope scope; REQ_EXT_ARG(0, js_henv); @@ -113,6 +137,9 @@ Handle ODBCStatement::New(const Arguments& args) { //set the initial colCount to 0 stmt->colCount = 0; + //initialize the paramCount + stmt->paramCount = 0; + stmt->Wrap(args.Holder()); return scope.Close(args.Holder()); @@ -210,26 +237,6 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { data->cb.Dispose(); - if (data->stmt->paramCount) { - Parameter prm; - - //free parameter memory - for (int i = 0; i < data->stmt->paramCount; i++) { - if (prm = data->stmt->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; - } - } - } - - data->stmt->paramCount = 0; - - free(data->stmt->params); - } - free(data); free(req); @@ -249,26 +256,6 @@ Handle ODBCStatement::ExecuteSync(const Arguments& args) { ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); SQLRETURN ret = SQLExecute(stmt->m_hSTMT); - - if (stmt->paramCount) { - Parameter prm; - - //free parameter memory - for (int i = 0; i < stmt->paramCount; i++) { - if (prm = stmt->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; - } - } - } - - stmt->paramCount = 0; - - free(stmt->params); - } if(ret == SQL_ERROR) { ThrowException(ODBC::GetSQLError( @@ -387,26 +374,6 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { data->cb.Dispose(); - if (data->stmt->paramCount) { - Parameter prm; - - //free parameter memory - for (int i = 0; i < data->stmt->paramCount; i++) { - if (prm = data->stmt->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; - } - } - } - - data->stmt->paramCount = 0; - - free(data->stmt->params); - } - free(data); free(req); @@ -426,26 +393,6 @@ Handle ODBCStatement::ExecuteNonQuerySync(const Arguments& args) { ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); SQLRETURN ret = SQLExecute(stmt->m_hSTMT); - - if (stmt->paramCount) { - Parameter prm; - - //free parameter memory - for (int i = 0; i < stmt->paramCount; i++) { - if (prm = stmt->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; - } - } - } - - stmt->paramCount = 0; - - free(stmt->params); - } if(ret == SQL_ERROR) { ThrowException(ODBC::GetSQLError( @@ -788,6 +735,29 @@ Handle ODBCStatement::BindSync(const Arguments& args) { stmt->m_hSTMT ); + //if we previously had parameters, then be sure to free them + //before allocating more + if (stmt->paramCount) { + int count = stmt->paramCount; + stmt->paramCount = 0; + + Parameter prm; + + //free parameter memory + for (int i = 0; i < count; i++) { + if (prm = stmt->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + free(stmt->params); + } + stmt->params = ODBC::GetParametersFromArray( Local::Cast(args[0]), &stmt->paramCount); @@ -865,6 +835,29 @@ Handle ODBCStatement::Bind(const Arguments& args) { bind_work_data* data = (bind_work_data *) calloc(1, sizeof(bind_work_data)); + //if we previously had parameters, then be sure to free them + //before allocating more + if (stmt->paramCount) { + int count = stmt->paramCount; + stmt->paramCount = 0; + + Parameter prm; + + //free parameter memory + for (int i = 0; i < count; i++) { + if (prm = stmt->params[i], prm.buffer != NULL) { + switch (prm.c_type) { + case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; + case SQL_C_DOUBLE: delete (double *)prm.buffer; break; + case SQL_C_BIT: delete (bool *)prm.buffer; break; + } + } + } + + free(stmt->params); + } + data->stmt = stmt; DEBUG_PRINTF("ODBCStatement::Bind m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", From e7072bb5e7eda821bf2e1fec345b74a992d13c6e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 22:24:53 -0400 Subject: [PATCH 215/511] Initialize timeInfo struct to avoid compiler warnings --- src/odbc.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index c2ccdd24..3e72a23d 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -374,7 +374,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, sizeof(value), &len); - DEBUG_PRINTF("ODBC::GetColumnValue - Integer: index=%i name=%s type=%i len=%i ret=%i\n", + DEBUG_PRINTF("ODBC::GetColumnValue - Number: index=%i name=%s type=%i len=%i ret=%i\n", column.index, column.name, column.type, len, ret); if(ret == SQL_NULL_DATA || len < 0) { @@ -389,7 +389,19 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, break; case SQL_DATETIME : case SQL_TIMESTAMP : { - struct tm timeInfo = { 0 }; + struct tm timeInfo = { + tm_sec : 0 + , tm_min : 0 + , tm_hour : 0 + , tm_mday : 0 + , tm_mon : 0 + , tm_year : 0 + , tm_wday : 0 + , tm_yday : 0 + , tm_isdst : 0 + , tm_gmtoff : 0 + , tm_zone : 0 + }; //I am not sure if this is locale-safe or cross database safe, but it //works for me on MSSQL #ifdef _WIN32 From e92e1292cab81210db9578d7a58f301e06b27a99 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 22:25:25 -0400 Subject: [PATCH 216/511] Properly return from CloseSync() --- src/odbc_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index dfd4c55b..413bd312 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -448,7 +448,7 @@ Handle ODBCConnection::CloseSync(const Arguments& args) { uv_unref(uv_default_loop()); #endif - scope.Close(True()); + return scope.Close(True()); } /* From c8b6d7c20a9efd0a411e7663756a42eec073c301 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 22:25:44 -0400 Subject: [PATCH 217/511] Fix debug message --- src/odbc_result.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 1db2d3f4..764ab209 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -680,12 +680,11 @@ Handle ODBCResult::CloseSync(const Arguments& args) { } Handle ODBCResult::MoreResultsSync(const Arguments& args) { - DEBUG_PRINTF("ODBCResult::MoreResults\n"); + DEBUG_PRINTF("ODBCResult::MoreResultsSync\n"); HandleScope scope; ODBCResult* result = ObjectWrap::Unwrap(args.Holder()); - //result->colCount = 0; SQLRETURN ret = SQLMoreResults(result->m_hSTMT); From 8994c78761ec9cbe78c39bd06bcc23ade9240565 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 29 Apr 2013 23:09:27 -0400 Subject: [PATCH 218/511] Test: add test for rebinding values of parameter arrays Not sure how or if this will work out --- test/test-binding-statement-rebinding.js | 51 ++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 test/test-binding-statement-rebinding.js diff --git a/test/test-binding-statement-rebinding.js b/test/test-binding-statement-rebinding.js new file mode 100644 index 00000000..b87769fc --- /dev/null +++ b/test/test-binding-statement-rebinding.js @@ -0,0 +1,51 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.ODBC() + , assert = require("assert") + , exitCode = 0 + ; + +db.createConnection(function (err, conn) { + conn.open(common.connectionString, function (err) { + conn.createStatement(function (err, stmt) { + var r, result, caughtError; + + var a = ['hello', 'world']; + + stmt.prepareSync('select ? as col1, ? as col2'); + + stmt.bindSync(a); + + result = stmt.executeSync(); + + console.log(result.fetchAllSync()); + result.closeSync(); + + a[0] = 'goodbye'; + a[1] = 'steven'; + + result = stmt.executeSync(); + + r = result.fetchAllSync(); + + try { + assert.deepEqual(r, [ { col1: 'goodbye', col2: 'steven' } ]); + } + catch (e) { + console.log(e); + exitCode = 1; + } + + conn.close(function () { + if (exitCode) { + console.log("failed"); + } + else { + console.log("success"); + } + + process.exit(exitCode); + }); + }); + }); +}); From 3a68c43dbf59f474a5ec988029953310248ef08f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 30 Apr 2013 11:05:45 -0400 Subject: [PATCH 219/511] Lock mutex when creating hENV --- src/odbc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index c2ccdd24..e6e39785 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -123,8 +123,12 @@ Handle ODBC::New(const Arguments& args) { dbo->Wrap(args.Holder()); dbo->m_hEnv = NULL; + uv_mutex_lock(&ODBC::g_odbcMutex); + int ret = SQLAllocEnv( &dbo->m_hEnv ); + uv_mutex_unlock(&ODBC::g_odbcMutex); + //TODO: check if ret succeeded, if not, throw error to javascript land if (!SQL_SUCCEEDED(ret)) { //TODO: do something. From d3030d398c8ec070bbd77ae3a4d4ba1e146b78fd Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 30 Apr 2013 11:10:26 -0400 Subject: [PATCH 220/511] Lock mutex around SQLAllocHandle --- src/odbc_connection.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 413bd312..d3acc798 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -464,11 +464,15 @@ Handle ODBCConnection::CreateStatementSync(const Arguments& args) { HSTMT hSTMT; + uv_mutex_lock(&ODBC::g_odbcMutex); + SQLAllocHandle( SQL_HANDLE_STMT, conn->m_hDBC, &hSTMT); + uv_mutex_unlock(&ODBC::g_odbcMutex); + Local params[3]; params[0] = External::New(conn->m_hENV); params[1] = External::New(conn->m_hDBC); From 51956ee6b87e44a15b5484d38d31318a74c3258b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 30 Apr 2013 11:17:22 -0400 Subject: [PATCH 221/511] 0.5.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index deb80a33..628e468a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.6", + "version": "0.5.7", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 63fe1f899a8a4423b62e4adf09f937a68831873f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 30 Apr 2013 22:26:19 -0400 Subject: [PATCH 222/511] More closely wrap handle allocs and frees with uv_mutex_lock This is an ettempt to ensure that we are only blocking when we absolutely have to. Previously we were blocking during the entire connect proccess. This may speed some things up or it may break stuff. Revert this commit if neccessary. --- src/odbc_connection.cpp | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index d3acc798..785a8043 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -167,8 +167,6 @@ void ODBCConnection::UV_Open(uv_work_t* req) { ODBCConnection* self = data->conn->self(); - uv_mutex_lock(&ODBC::g_odbcMutex); - //TODO: make this configurable SQLSetConnectOption( self->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); @@ -188,9 +186,13 @@ void ODBCConnection::UV_Open(uv_work_t* req) { if (SQL_SUCCEEDED(ret)) { HSTMT hStmt; + uv_mutex_lock(&ODBC::g_odbcMutex); + //allocate a temporary statment ret = SQLAllocStmt(self->m_hDBC, &hStmt); + uv_mutex_unlock(&ODBC::g_odbcMutex); + //try to determine if the driver can handle //multiple recordsets ret = SQLGetFunctions( @@ -202,12 +204,14 @@ void ODBCConnection::UV_Open(uv_work_t* req) { self->canHaveMoreResults = 0; } + uv_mutex_lock(&ODBC::g_odbcMutex); + //free the handle ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt); + + uv_mutex_unlock(&ODBC::g_odbcMutex); } - uv_mutex_unlock(&ODBC::g_odbcMutex); - data->result = ret; } @@ -274,8 +278,6 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { bool err = false; char connstr[1024]; - uv_mutex_lock(&ODBC::g_odbcMutex); - //TODO: make this configurable SQLSetConnectOption( conn->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); @@ -298,9 +300,13 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { else { HSTMT hStmt; + uv_mutex_lock(&ODBC::g_odbcMutex); + //allocate a temporary statment ret = SQLAllocStmt(conn->m_hDBC, &hStmt); + uv_mutex_unlock(&ODBC::g_odbcMutex); + //try to determine if the driver can handle //multiple recordsets ret = SQLGetFunctions( @@ -312,9 +318,13 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { conn->canHaveMoreResults = 0; } + uv_mutex_lock(&ODBC::g_odbcMutex); + //free the handle ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt); + uv_mutex_unlock(&ODBC::g_odbcMutex); + conn->self()->connected = true; //only uv_ref if the connection was successful @@ -324,8 +334,6 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { uv_ref(uv_default_loop()); #endif } - - uv_mutex_unlock(&ODBC::g_odbcMutex); if (err) { ThrowException(objError); @@ -525,8 +533,6 @@ void ODBCConnection::UV_CreateStatement(uv_work_t* req) { //get our work data create_statement_work_data* data = (create_statement_work_data *)(req->data); - - uv_mutex_lock(&ODBC::g_odbcMutex); DEBUG_PRINTF("ODBCConnection::UV_CreateStatement m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", data->conn->m_hENV, @@ -534,18 +540,20 @@ void ODBCConnection::UV_CreateStatement(uv_work_t* req) { data->hSTMT ); + uv_mutex_lock(&ODBC::g_odbcMutex); + //allocate a new statment handle SQLAllocHandle( SQL_HANDLE_STMT, data->conn->m_hDBC, &data->hSTMT); + uv_mutex_unlock(&ODBC::g_odbcMutex); + DEBUG_PRINTF("ODBCConnection::UV_CreateStatement m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", data->conn->m_hENV, data->conn->m_hDBC, data->hSTMT ); - - uv_mutex_unlock(&ODBC::g_odbcMutex); } void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { From 44bca4551bc752ea2b56341b6282c0d775d8d82c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 30 Apr 2013 22:29:47 -0400 Subject: [PATCH 223/511] Fix up remainging compiler warnings. --- src/odbc.cpp | 3 ++- src/odbc_connection.cpp | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index d7b38859..3d22096e 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -806,7 +806,8 @@ Local ODBC::GetAllRecordsSync (HENV hENV, count++; } //TODO: what do we do about errors!?! - scope.Close(rows); + //we throw them + return scope.Close(rows); } #ifdef dynodbc diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 785a8043..7d4427d8 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -666,6 +666,9 @@ Handle ODBCConnection::Query(const Arguments& args) { if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { sql = new String::Utf8Value(obj->Get(OPTION_SQL)->ToString()); } + else { + sql = new String::Utf8Value(String::New("")); + } if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { data->params = ODBC::GetParametersFromArray( @@ -937,6 +940,9 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { sql = new String::Utf8Value(obj->Get(OPTION_SQL)->ToString()); } + else { + sql = new String::Utf8Value(String::New("")); + } if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { params = ODBC::GetParametersFromArray( From 115f2ea6f9dddb7a248be1494bdcf0ee076a74aa Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 30 Apr 2013 22:36:01 -0400 Subject: [PATCH 224/511] 0.5.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 628e468a..d04c18d6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.7", + "version": "0.5.8", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From c04b7cba43e987279af0114a833f1d6d753d0a38 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 14:59:24 -0400 Subject: [PATCH 225/511] expose db.closeSync() --- lib/odbc.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/odbc.js b/lib/odbc.js index 96f3cf0e..0406f374 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -112,6 +112,12 @@ Database.prototype.close = function (cb) { }); }; +Database.prototype.closeSync = function (cb) { + var self = this; + + return self.conn.closeSync(); +} + Database.prototype.query = function (sql, params, cb) { var self = this; From e849f4a4477f60b1ebd017b9f3c6a908b5d3fa9a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 15:00:13 -0400 Subject: [PATCH 226/511] Set fetchMode on result objects if fetchMode was passed to Database() constructor --- lib/odbc.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 0406f374..884cad73 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -131,8 +131,7 @@ Database.prototype.query = function (sql, params, cb) { } self.queue.push(function (next) { - //ODBCConnection.query() is the fastest-path querying mechanism. - var cbActual = function (err, result) { + function cbQuery (err, result) { if (err) { cb(err, [], false); @@ -142,7 +141,10 @@ Database.prototype.query = function (sql, params, cb) { fetchMore(); function fetchMore() { - //TODO: pass fetchMode if it's not null + if (self.fetchMode) { + result.fetchMode = self.fetchMode; + } + result.fetchAll(function (err, data) { var moreResults = result.moreResultsSync(); @@ -165,10 +167,10 @@ Database.prototype.query = function (sql, params, cb) { } if (params) { - self.conn.query(sql, params, cbActual); + self.conn.query(sql, params, cbQuery); } else { - self.conn.query(sql, cbActual); + self.conn.query(sql, cbQuery); } }); }; @@ -194,6 +196,10 @@ Database.prototype.queryResult = function (sql, params, cb) { return next(); } + if (self.fetchMode) { + result.fetchMode = self.fetchMode; + } + cb(err, result); return next(); @@ -215,6 +221,10 @@ Database.prototype.queryResultSync = function (sql, params) { result = self.conn.querySync(sql); } + if (self.fetchMode) { + result.fetchMode = self.fetchMode; + } + return result; }; @@ -232,6 +242,10 @@ Database.prototype.querySync = function (sql, params) { result = self.conn.querySync(sql); } + if (self.fetchMode) { + result.fetchMode = self.fetchMode; + } + var data = result.fetchAllSync(); result.closeSync(); From b8dabb06953ae317675e6da131c0ea1ef8bc60ff Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 15:00:55 -0400 Subject: [PATCH 227/511] Add fetchMode property to ODBCResult and use as default --- src/odbc_result.cpp | 46 +++++++++++++++++++++++++++++++-------------- src/odbc_result.h | 7 ++++++- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 764ab209..d1aebe72 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -55,6 +55,9 @@ void ODBCResult::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchSync", FetchSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAllSync", FetchAllSync); + // Properties + instance_template->SetAccessor(String::New("fetchMode"), FetchModeGetter, FetchModeSetter); + // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCResult"), constructor_template->GetFunction()); @@ -122,12 +125,33 @@ Handle ODBCResult::New(const Arguments& args) { //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()); } +Handle ODBCResult::FetchModeGetter(Local property, const AccessorInfo &info) { + HandleScope scope; + + ODBCResult *obj = ObjectWrap::Unwrap(info.Holder()); + + return scope.Close(Integer::New(obj->m_fetchMode)); +} + +void ODBCResult::FetchModeSetter(Local property, Local value, const AccessorInfo &info) { + HandleScope scope; + + ODBCResult *obj = ObjectWrap::Unwrap(info.Holder()); + + if (value->IsNumber()) { + obj->m_fetchMode = value->Int32Value(); + } +} + /* * Fetch */ @@ -145,6 +169,9 @@ Handle ODBCResult::Fetch(const Arguments& args) { Local 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::Cast(args[0]); } @@ -156,9 +183,6 @@ Handle ODBCResult::Fetch(const Arguments& args) { if (obj->Has(OPTION_FETCH_MODE) && obj->Get(OPTION_FETCH_MODE)->IsInt32()) { data->fetchMode = obj->Get(OPTION_FETCH_MODE)->ToInt32()->Value(); } - else { - data->fetchMode = FETCH_OBJECT; - } } else { return ThrowException(Exception::TypeError( @@ -296,18 +320,14 @@ Handle ODBCResult::FetchSync(const Arguments& args) { Local objError; bool moreWork = true; bool error = false; - int fetchMode = FETCH_OBJECT; + int fetchMode = objResult->m_fetchMode; if (args.Length() == 1 && args[0]->IsObject()) { - Local obj = args[0]->ToObject(); if (obj->Has(OPTION_FETCH_MODE) && obj->Get(OPTION_FETCH_MODE)->IsInt32()) { fetchMode = obj->Get(OPTION_FETCH_MODE)->ToInt32()->Value(); } - else { - fetchMode = FETCH_OBJECT; - } } SQLRETURN ret = SQLFetch(objResult->m_hSTMT); @@ -391,10 +411,11 @@ Handle ODBCResult::FetchAll(const Arguments& args) { fetch_work_data* data = (fetch_work_data *) calloc(1, sizeof(fetch_work_data)); Local cb; - + + data->fetchMode = objODBCResult->m_fetchMode; + if (args.Length() == 1 && args[0]->IsFunction()) { cb = Local::Cast(args[0]); - data->fetchMode = FETCH_OBJECT; } else if (args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction()) { cb = Local::Cast(args[1]); @@ -404,9 +425,6 @@ Handle ODBCResult::FetchAll(const Arguments& args) { if (obj->Has(OPTION_FETCH_MODE) && obj->Get(OPTION_FETCH_MODE)->IsInt32()) { data->fetchMode = obj->Get(OPTION_FETCH_MODE)->ToInt32()->Value(); } - else { - data->fetchMode = FETCH_OBJECT; - } } else { return ThrowException(Exception::TypeError( @@ -559,7 +577,7 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { SQLRETURN ret; int count = 0; int errorCount = 0; - int fetchMode = FETCH_OBJECT; + int fetchMode = self->m_fetchMode; if (args.Length() == 1 && args[0]->IsObject()) { Local obj = args[0]->ToObject(); diff --git a/src/odbc_result.h b/src/odbc_result.h index c9b55e51..15ef4112 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -55,6 +55,10 @@ class ODBCResult : public node::ObjectWrap { static Handle FetchSync(const Arguments& args); static Handle FetchAllSync(const Arguments& args); + //property getter/setters + static Handle FetchModeGetter(Local property, const AccessorInfo &info); + static void FetchModeSetter(Local property, Local value, const AccessorInfo &info); + struct fetch_work_data { Persistent cb; ODBCResult *objResult; @@ -74,7 +78,8 @@ class ODBCResult : public node::ObjectWrap { HDBC m_hDBC; HSTMT m_hSTMT; bool m_canFreeHandle; - + int m_fetchMode; + uint16_t *buffer; int bufferLength; Column *columns; From 39dc4248dc604fd4c18f3ca885c6d9bd04e67525 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 15:01:33 -0400 Subject: [PATCH 228/511] Fix thread locking in ODBCConnection::Open[Sync] --- src/odbc_connection.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 7d4427d8..a0993a58 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -96,6 +96,10 @@ void ODBCConnection::Free() { } } +/* + * New + */ + Handle ODBCConnection::New(const Arguments& args) { DEBUG_PRINTF("ODBCConnection::New\n"); HandleScope scope; @@ -167,12 +171,16 @@ void ODBCConnection::UV_Open(uv_work_t* req) { ODBCConnection* self = data->conn->self(); + uv_mutex_lock(&ODBC::g_odbcMutex); + //TODO: make this configurable + //NOTE: SQLSetConnectOption requires the thread to be locked SQLSetConnectOption( self->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); - + char connstr[1024]; //Attempt to connect + //NOTE: SQLDriverConnect requires the thread to be locked int ret = SQLDriverConnect( self->m_hDBC, NULL, @@ -182,16 +190,12 @@ void ODBCConnection::UV_Open(uv_work_t* req) { 1024, NULL, SQL_DRIVER_NOPROMPT); - + if (SQL_SUCCEEDED(ret)) { HSTMT hStmt; - uv_mutex_lock(&ODBC::g_odbcMutex); - //allocate a temporary statment ret = SQLAllocStmt(self->m_hDBC, &hStmt); - - uv_mutex_unlock(&ODBC::g_odbcMutex); //try to determine if the driver can handle //multiple recordsets @@ -204,13 +208,11 @@ void ODBCConnection::UV_Open(uv_work_t* req) { self->canHaveMoreResults = 0; } - uv_mutex_lock(&ODBC::g_odbcMutex); - //free the handle ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt); - - uv_mutex_unlock(&ODBC::g_odbcMutex); } + + uv_mutex_unlock(&ODBC::g_odbcMutex); data->result = ret; } @@ -278,10 +280,14 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { bool err = false; char connstr[1024]; + uv_mutex_lock(&ODBC::g_odbcMutex); + //TODO: make this configurable + //NOTE: SQLSetConnectOption requires the thread to be locked SQLSetConnectOption( conn->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); //Attempt to connect + //NOTE: SQLDriverConnect requires the thread to be locked ret = SQLDriverConnect( conn->m_hDBC, NULL, @@ -300,12 +306,8 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { else { HSTMT hStmt; - uv_mutex_lock(&ODBC::g_odbcMutex); - //allocate a temporary statment ret = SQLAllocStmt(conn->m_hDBC, &hStmt); - - uv_mutex_unlock(&ODBC::g_odbcMutex); //try to determine if the driver can handle //multiple recordsets @@ -318,13 +320,9 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { conn->canHaveMoreResults = 0; } - uv_mutex_lock(&ODBC::g_odbcMutex); - //free the handle ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt); - uv_mutex_unlock(&ODBC::g_odbcMutex); - conn->self()->connected = true; //only uv_ref if the connection was successful @@ -335,6 +333,8 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { #endif } + uv_mutex_unlock(&ODBC::g_odbcMutex); + if (err) { ThrowException(objError); return scope.Close(False()); From d010b84e5d9d1b4d73e488a347f772d7d842032f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 15:08:44 -0400 Subject: [PATCH 229/511] Updated tests and new tests --- test/bench-querySync-fetchArray.js | 31 +++++++++++++++++++++++ test/test-multi-open-close.js | 2 +- test/test-multi-openSync-closeSync.js | 20 +++++++++++++++ test/test-query-select-fetchMode-array.js | 18 +++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 test/bench-querySync-fetchArray.js create mode 100644 test/test-multi-openSync-closeSync.js create mode 100644 test/test-query-select-fetchMode-array.js diff --git a/test/bench-querySync-fetchArray.js b/test/bench-querySync-fetchArray.js new file mode 100644 index 00000000..fc52ce8e --- /dev/null +++ b/test/bench-querySync-fetchArray.js @@ -0,0 +1,31 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database({ fetchMode : odbc.FETCH_ARRAY }); + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , iterations = 10000 + , time = new Date().getTime(); + + for (var x = 0; x < iterations; x++) { + var data = db.querySync("select 1 + 1 as test"); + count += 1; + } + + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + + db.close(function () { + console.log("connection closed"); + }); +} \ No newline at end of file diff --git a/test/test-multi-open-close.js b/test/test-multi-open-close.js index 9d6097e9..87a0f917 100644 --- a/test/test-multi-open-close.js +++ b/test/test-multi-open-close.js @@ -2,7 +2,7 @@ var common = require("./common") , odbc = require("../") , openCallback = 0 , closeCallback = 0 - , openCount = 100 + , openCount = 1000 , connections = [] ; diff --git a/test/test-multi-openSync-closeSync.js b/test/test-multi-openSync-closeSync.js new file mode 100644 index 00000000..381c7573 --- /dev/null +++ b/test/test-multi-openSync-closeSync.js @@ -0,0 +1,20 @@ +var common = require("./common") + , odbc = require("../") + , openCallback = 0 + , closeCallback = 0 + , openCount = 1000 + , connections = [] + ; + +for (var x = 0; x < openCount; x++ ) { + var db = new odbc.Database(); + connections.push(db); + + db.openSync(common.connectionString); +} + +connections.forEach(function (db) { + db.closeSync(); +}); + +console.log('Done'); diff --git a/test/test-query-select-fetchMode-array.js b/test/test-query-select-fetchMode-array.js new file mode 100644 index 00000000..f42f9897 --- /dev/null +++ b/test/test-query-select-fetchMode-array.js @@ -0,0 +1,18 @@ +var common = require("./common") + , odbc = require("../") + , db = odbc({ fetchMode : odbc.FETCH_ARRAY }) + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + assert.equal(err, null); + assert.equal(db.connected, true); + + db.query("select 1 as COLINT, 'some test' as COLTEXT ", function (err, data) { + assert.equal(err, null); + + db.close(function () { + assert.deepEqual(data, [[1,"some test"]]); + }); + }); +}); From 35658d8158c926ebed52171d11bb37cc5a52b86a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 17:51:31 -0400 Subject: [PATCH 230/511] Added benchmarks for comparing prepare vs not --- test/bench-prepare-bind-execute-closeSync.js | 56 ++++++++++++++++++++ test/bench-prepare-bind-executeNonQuery.js | 54 +++++++++++++++++++ test/bench-prepare-not.js | 45 ++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 test/bench-prepare-bind-execute-closeSync.js create mode 100644 test/bench-prepare-bind-executeNonQuery.js create mode 100644 test/bench-prepare-not.js diff --git a/test/bench-prepare-bind-execute-closeSync.js b/test/bench-prepare-bind-execute-closeSync.js new file mode 100644 index 00000000..ffdec813 --- /dev/null +++ b/test/bench-prepare-bind-execute-closeSync.js @@ -0,0 +1,56 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , iterations = 10000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery3(function () { + finish(); + }); +}); + +function issueQuery3(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select 1 + ? as test'); + + for (var x = 0; x < iterations; x++) { + stmt.bind([2], function (err) { + if (err) { + console.log(err); + return finish(); + } + + stmt.execute(cb); + }); + } + + function cb (err, result) { + if (err) { + console.error(err); + return finish(); + } + + result.closeSync(); + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - Bind - Execute - CloseSync", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} diff --git a/test/bench-prepare-bind-executeNonQuery.js b/test/bench-prepare-bind-executeNonQuery.js new file mode 100644 index 00000000..efa694f9 --- /dev/null +++ b/test/bench-prepare-bind-executeNonQuery.js @@ -0,0 +1,54 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , iterations = 10000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery2(function () { + finish(); + }); +}); + +function issueQuery2(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select 1 + ? as test'); + + for (var x = 0; x < iterations; x++) { + stmt.bind([2], function (err) { + if (err) { + console.log(err); + return finish(); + } + + stmt.executeNonQuery(cb); + }); + } + + function cb (err, data) { + if (err) { + console.error(err); + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - Bind - ExecuteNonQuery ", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} diff --git a/test/bench-prepare-not.js b/test/bench-prepare-not.js new file mode 100644 index 00000000..484db622 --- /dev/null +++ b/test/bench-prepare-not.js @@ -0,0 +1,45 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , iterations = 10000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery1(function () { + finish(); + }); +}); + +function issueQuery1(done) { + var count = 0 + , time = new Date().getTime(); + + for (var x = 0; x < iterations; x++) { + db.query("select 1 + ? as test", [1], cb); + } + + function cb (err, data) { + if (err) { + console.error(err); + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Query", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} From 3bbcbf8984cd111bba975df67bd79c42de302af1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 20:52:12 -0400 Subject: [PATCH 231/511] Updated benchmarks and start of separate bind queue --- lib/odbc.js | 5 +- test/bench-prepare-bind-execute-closeSync.js | 30 ++++++----- test/bench-prepare-bind-executeNonQuery.js | 22 ++++---- ...ench-prepare-bindSync-execute-closeSync.js | 52 +++++++++++++++++++ .../bench-prepare-bindSync-executeNonQuery.js | 50 ++++++++++++++++++ test/bench-prepare-not.js | 2 +- 6 files changed, 135 insertions(+), 26 deletions(-) create mode 100644 test/bench-prepare-bindSync-execute-closeSync.js create mode 100644 test/bench-prepare-bindSync-executeNonQuery.js diff --git a/lib/odbc.js b/lib/odbc.js index 884cad73..bfa6fae9 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -465,9 +465,9 @@ odbc.ODBCStatement.prototype.prepare = function (sql, cb) { odbc.ODBCStatement.prototype.bind = function (ary, cb) { var self = this; - self.queue = self.queue || new SimpleQueue(); + self.bindQueue = self.bindQueue || new SimpleQueue(); - self.queue.push(function (next) { + self.bindQueue.push(function (next) { self._bind(ary, function (err) { cb(err); @@ -477,7 +477,6 @@ odbc.ODBCStatement.prototype.bind = function (ary, cb) { }; - module.exports.Pool = Pool; Pool.count = 0; diff --git a/test/bench-prepare-bind-execute-closeSync.js b/test/bench-prepare-bind-execute-closeSync.js index ffdec813..19aad5a0 100644 --- a/test/bench-prepare-bind-execute-closeSync.js +++ b/test/bench-prepare-bind-execute-closeSync.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 10000 + , iterations = 100000 ; db.open(common.connectionString, function(err){ @@ -19,17 +19,21 @@ function issueQuery3(done) { var count = 0 , time = new Date().getTime(); - var stmt = db.prepareSync('select 1 + ? as test'); - + var stmt = db.prepareSync('select ? as test'); + for (var x = 0; x < iterations; x++) { - stmt.bind([2], function (err) { - if (err) { - console.log(err); - return finish(); - } - - stmt.execute(cb); - }); + (function (x) { + stmt.bind([x], function (err) { + if (err) { + console.log(err); + return finish(); + } + + //console.log(x); + + stmt.execute(cb); + }); + })(x); } function cb (err, result) { @@ -38,8 +42,10 @@ function issueQuery3(done) { return finish(); } - result.closeSync(); + //console.log(result.fetchAllSync()); + result.closeSync(); + if (++count == iterations) { var elapsed = new Date().getTime() - time; diff --git a/test/bench-prepare-bind-executeNonQuery.js b/test/bench-prepare-bind-executeNonQuery.js index efa694f9..1be5e9f7 100644 --- a/test/bench-prepare-bind-executeNonQuery.js +++ b/test/bench-prepare-bind-executeNonQuery.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 10000 + , iterations = 100000 ; db.open(common.connectionString, function(err){ @@ -19,17 +19,19 @@ function issueQuery2(done) { var count = 0 , time = new Date().getTime(); - var stmt = db.prepareSync('select 1 + ? as test'); + var stmt = db.prepareSync('select ? as test'); for (var x = 0; x < iterations; x++) { - stmt.bind([2], function (err) { - if (err) { - console.log(err); - return finish(); - } - - stmt.executeNonQuery(cb); - }); + (function (x) { + stmt.bind([x], function (err) { + if (err) { + console.log(err); + return finish(); + } + + stmt.executeNonQuery(cb); + }); + })(x); } function cb (err, data) { diff --git a/test/bench-prepare-bindSync-execute-closeSync.js b/test/bench-prepare-bindSync-execute-closeSync.js new file mode 100644 index 00000000..1fdb2e1f --- /dev/null +++ b/test/bench-prepare-bindSync-execute-closeSync.js @@ -0,0 +1,52 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , iterations = 100000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery3(function () { + finish(); + }); +}); + +function issueQuery3(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select ? as test'); + + for (var x = 0; x < iterations; x++) { + (function (x) { + stmt.bindSync([x]); + stmt.execute(cb); + })(x); + } + + function cb (err, result) { + if (err) { + console.error(err); + return finish(); + } + + result.closeSync(); + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - Bind - Execute - CloseSync", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} diff --git a/test/bench-prepare-bindSync-executeNonQuery.js b/test/bench-prepare-bindSync-executeNonQuery.js new file mode 100644 index 00000000..40aad267 --- /dev/null +++ b/test/bench-prepare-bindSync-executeNonQuery.js @@ -0,0 +1,50 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , iterations = 100000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery2(function () { + finish(); + }); +}); + +function issueQuery2(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select ? as test'); + + for (var x = 0; x < iterations; x++) { + (function (x) { + stmt.bindSync([x]); + stmt.executeNonQuery(cb); + })(x); + } + + function cb (err, data) { + if (err) { + console.error(err); + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - Bind - ExecuteNonQuery ", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} diff --git a/test/bench-prepare-not.js b/test/bench-prepare-not.js index 484db622..a880e802 100644 --- a/test/bench-prepare-not.js +++ b/test/bench-prepare-not.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 10000 + , iterations = 100000 ; db.open(common.connectionString, function(err){ From 31cb18dabb765d0473e17962fe8eac882ed242d7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 21:38:42 -0400 Subject: [PATCH 232/511] Add next() function to SimpleQueue --- lib/simple-queue.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/simple-queue.js b/lib/simple-queue.js index df51f07f..a6f784e1 100644 --- a/lib/simple-queue.js +++ b/lib/simple-queue.js @@ -18,7 +18,15 @@ SimpleQueue.prototype.push = function (fn) { SimpleQueue.prototype.maybeNext = function () { var self = this; - if (!self.executing && self.fifo.length) { + if (!self.executing) { + self.next(); + } +}; + +SimpleQueue.prototype.next = function () { + var self = this; + + if (self.fifo.length) { var fn = self.fifo.shift(); self.executing = true; From f95a36076a4e0b07d846f6960de687a1304c20c0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 21:39:20 -0400 Subject: [PATCH 233/511] Only execute the next queued bind() call after an execute() call --- lib/odbc.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index bfa6fae9..2999f445 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -415,6 +415,12 @@ odbc.ODBCStatement.prototype.execute = function (cb) { self._execute(function (err, result) { cb(err, result); + //NOTE: We only execute the next queued bind call after + // we have called execute() or executeNonQuery(). This ensures + // that we don't call a bind() a bunch of times without ever + // actually executing that bind. Not + self.bindQueue && self.bindQueue.next(); + return next(); }); }); @@ -443,6 +449,8 @@ odbc.ODBCStatement.prototype.executeNonQuery = function (cb) { self._executeNonQuery(function (err, result) { cb(err, result); + self.bindQueue && self.bindQueue.next(); + return next(); }); }); @@ -467,11 +475,13 @@ odbc.ODBCStatement.prototype.bind = function (ary, cb) { self.bindQueue = self.bindQueue || new SimpleQueue(); - self.bindQueue.push(function (next) { + self.bindQueue.push(function () { self._bind(ary, function (err) { cb(err); - return next(); + //NOTE: we do not call next() here because + //we want to pop the next bind call only + //after the next execute call }); }); }; From e8c48fdc4c18aa87549d44b724377e73bc826512 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 21:39:40 -0400 Subject: [PATCH 234/511] Add tests for bind/execute --- .../test-prepare-bind-executeNonQuery.js | 63 +++++++++++++++++ .../test-prepare-bindSync-executeNonQuery.js | 50 ++++++++++++++ test/test-prepare-bind-execute-closeSync.js | 67 +++++++++++++++++++ ...test-prepare-bindSync-execute-closeSync.js | 57 ++++++++++++++++ 4 files changed, 237 insertions(+) create mode 100644 test/disabled/test-prepare-bind-executeNonQuery.js create mode 100644 test/disabled/test-prepare-bindSync-executeNonQuery.js create mode 100644 test/test-prepare-bind-execute-closeSync.js create mode 100644 test/test-prepare-bindSync-execute-closeSync.js diff --git a/test/disabled/test-prepare-bind-executeNonQuery.js b/test/disabled/test-prepare-bind-executeNonQuery.js new file mode 100644 index 00000000..4f5aa835 --- /dev/null +++ b/test/disabled/test-prepare-bind-executeNonQuery.js @@ -0,0 +1,63 @@ +var common = require("./common") + , odbc = require("../") + , assert = require("assert") + , db = new odbc.Database() + , iterations = 100000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery2(function () { + finish(); + }); +}); + +function issueQuery2(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select ? as test'); + + for (var x = 0; x < iterations; x++) { + (function (x) { + stmt.bind([x], function (err) { + if (err) { + console.log(err); + return finish(); + } + + stmt.executeNonQuery(function (err, result) { + cb(err, result, x); + }); + }); + })(x); + } + + function cb (err, data, x) { + if (err) { + console.error(err); + return finish(); + } + + //TODO: there's nothing to assert in this case. + //we actually need to insert data and then get + //the data back out and then assert. + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - Bind - ExecuteNonQuery ", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} diff --git a/test/disabled/test-prepare-bindSync-executeNonQuery.js b/test/disabled/test-prepare-bindSync-executeNonQuery.js new file mode 100644 index 00000000..40aad267 --- /dev/null +++ b/test/disabled/test-prepare-bindSync-executeNonQuery.js @@ -0,0 +1,50 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , iterations = 100000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery2(function () { + finish(); + }); +}); + +function issueQuery2(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select ? as test'); + + for (var x = 0; x < iterations; x++) { + (function (x) { + stmt.bindSync([x]); + stmt.executeNonQuery(cb); + })(x); + } + + function cb (err, data) { + if (err) { + console.error(err); + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - Bind - ExecuteNonQuery ", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} diff --git a/test/test-prepare-bind-execute-closeSync.js b/test/test-prepare-bind-execute-closeSync.js new file mode 100644 index 00000000..abaa95fa --- /dev/null +++ b/test/test-prepare-bind-execute-closeSync.js @@ -0,0 +1,67 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + , iterations = 1000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery3(function () { + finish(); + }); +}); + +function issueQuery3(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select ? as test'); + + for (var x = 0; x < iterations; x++) { + (function (x) { + stmt.bind([x], function (err) { + if (err) { + console.log(err); + return finish(); + } + + //console.log(x); + + stmt.execute(function (err, result) { + cb(err, result, x); + }); + }); + })(x); + } + + function cb (err, result, x) { + if (err) { + console.error(err); + return finish(); + } + + var a = result.fetchAllSync(); + + assert.deepEqual(a, [{ test : x }]); + + result.closeSync(); + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - Bind - Execute - CloseSync", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} diff --git a/test/test-prepare-bindSync-execute-closeSync.js b/test/test-prepare-bindSync-execute-closeSync.js new file mode 100644 index 00000000..bb9282b9 --- /dev/null +++ b/test/test-prepare-bindSync-execute-closeSync.js @@ -0,0 +1,57 @@ +var common = require("./common") + , odbc = require("../") + , assert = require("assert") + , db = new odbc.Database() + , iterations = 100000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery3(function () { + finish(); + }); +}); + +function issueQuery3(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select ? as test'); + + for (var x = 0; x < iterations; x++) { + (function (x) { + stmt.bindSync([x]); + stmt.execute(function (err, result) { + cb(err, result, x); + }); + })(x); + } + + function cb (err, result, x) { + if (err) { + console.error(err); + return finish(); + } + + assert.deepEqual(result.fetchAllSync(), [ { test : x } ]); + + result.closeSync(); + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - Bind - Execute - CloseSync", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} From 57408b03d6802c65c73e1d772c51a1a33b755049 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 22:01:03 -0400 Subject: [PATCH 235/511] Accept params array to stmt.execute() and stmt.executeNonQuery --- lib/odbc.js | 90 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 2999f445..7c0bd53f 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -406,23 +406,46 @@ odbc.ODBCStatement.prototype._executeNonQuery = odbc.ODBCStatement.prototype.exe odbc.ODBCStatement.prototype._prepare = odbc.ODBCStatement.prototype.prepare; odbc.ODBCStatement.prototype._bind = odbc.ODBCStatement.prototype.bind; -odbc.ODBCStatement.prototype.execute = function (cb) { +odbc.ODBCStatement.prototype.execute = function (params, cb) { var self = this; self.queue = self.queue || new SimpleQueue(); + if (!cb) { + cb = params; + params = null; + } + self.queue.push(function (next) { - self._execute(function (err, result) { - cb(err, result); - - //NOTE: We only execute the next queued bind call after - // we have called execute() or executeNonQuery(). This ensures - // that we don't call a bind() a bunch of times without ever - // actually executing that bind. Not - self.bindQueue && self.bindQueue.next(); - - return next(); - }); + //If params were passed to this function, then bind them and + //then execute. + if (params) { + self._bind(params, function (err) { + if (err) { + return cb(err); + } + + self._execute(function (err, result) { + cb(err, result); + + return next(); + }); + }); + } + //Otherwise execute and pop the next bind call + else { + self._execute(function (err, result) { + cb(err, result); + + //NOTE: We only execute the next queued bind call after + // we have called execute() or executeNonQuery(). This ensures + // that we don't call a bind() a bunch of times without ever + // actually executing that bind. Not + self.bindQueue && self.bindQueue.next(); + + return next(); + }); + } }); }; @@ -440,19 +463,46 @@ odbc.ODBCStatement.prototype.executeDirect = function (sql, cb) { }); }; -odbc.ODBCStatement.prototype.executeNonQuery = function (cb) { +odbc.ODBCStatement.prototype.executeNonQuery = function (params, cb) { var self = this; self.queue = self.queue || new SimpleQueue(); + if (!cb) { + cb = params; + params = null; + } + self.queue.push(function (next) { - self._executeNonQuery(function (err, result) { - cb(err, result); - - self.bindQueue && self.bindQueue.next(); - - return next(); - }); + //If params were passed to this function, then bind them and + //then executeNonQuery. + if (params) { + self._bind(params, function (err) { + if (err) { + return cb(err); + } + + self._executeNonQuery(function (err, result) { + cb(err, result); + + return next(); + }); + }); + } + //Otherwise executeNonQuery and pop the next bind call + else { + self._executeNonQuery(function (err, result) { + cb(err, result); + + //NOTE: We only execute the next queued bind call after + // we have called execute() or executeNonQuery(). This ensures + // that we don't call a bind() a bunch of times without ever + // actually executing that bind. Not + self.bindQueue && self.bindQueue.next(); + + return next(); + }); + } }); }; From d0f1e84b5298d69fd962c5efb9f1ef138a503903 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 22:01:20 -0400 Subject: [PATCH 236/511] Added new prepare benchmarks --- test/bench-prepare-execute-closeSync.js | 53 +++++++++++++++++++++++++ test/bench-prepare-executeNonQuery.js | 49 +++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 test/bench-prepare-execute-closeSync.js create mode 100644 test/bench-prepare-executeNonQuery.js diff --git a/test/bench-prepare-execute-closeSync.js b/test/bench-prepare-execute-closeSync.js new file mode 100644 index 00000000..8bcf4327 --- /dev/null +++ b/test/bench-prepare-execute-closeSync.js @@ -0,0 +1,53 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , iterations = 100000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery3(function () { + finish(); + }); +}); + +function issueQuery3(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select ? as test'); + + for (var x = 0; x < iterations; x++) { + (function (x) { + stmt.execute([x], cb); + })(x); + } + + function cb (err, result) { + if (err) { + console.error(err); + return finish(); + } + + //console.log(result.fetchAllSync()); + + result.closeSync(); + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - Execute - CloseSync", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} diff --git a/test/bench-prepare-executeNonQuery.js b/test/bench-prepare-executeNonQuery.js new file mode 100644 index 00000000..309a33a4 --- /dev/null +++ b/test/bench-prepare-executeNonQuery.js @@ -0,0 +1,49 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , iterations = 100000 + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery2(function () { + finish(); + }); +}); + +function issueQuery2(done) { + var count = 0 + , time = new Date().getTime(); + + var stmt = db.prepareSync('select ? as test'); + + for (var x = 0; x < iterations; x++) { + (function (x) { + stmt.executeNonQuery([x], cb); + })(x); + } + + function cb (err, data) { + if (err) { + console.error(err); + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec : Prepare - ExecuteNonQuery ", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return done(); + } + } +} + +function finish() { + db.close(function () { + console.log("connection closed"); + }); +} From 1cb90cd953823c9cb7c62930e20b57ae5411f0a7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 1 May 2013 22:32:54 -0400 Subject: [PATCH 237/511] 0.5.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d04c18d6..d043028e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.8", + "version": "0.5.9", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 7529f5403967cd1904a7aac3900091991447f815 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 2 May 2013 14:05:00 -0400 Subject: [PATCH 238/511] After executeNonQuery[Sync] call SQLFreeStmt(..., SQL_CLOSE) Hopefully this will avoid any invalid cursor state messages --- src/odbc_statement.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index dec085d0..5a98a61f 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -356,6 +356,10 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { rowCount = 0; } + uv_mutex_lock(&ODBC::g_odbcMutex); + SQLFreeStmt(self->m_hSTMT, SQL_CLOSE); + uv_mutex_unlock(&ODBC::g_odbcMutex); + Local args[2]; args[0] = Local::New(Null()); @@ -413,6 +417,10 @@ Handle ODBCStatement::ExecuteNonQuerySync(const Arguments& args) { rowCount = 0; } + uv_mutex_lock(&ODBC::g_odbcMutex); + SQLFreeStmt(stmt->m_hSTMT, SQL_CLOSE); + uv_mutex_unlock(&ODBC::g_odbcMutex); + return scope.Close(Number::New(rowCount)); } } From c982ceb8c31f4f64420a9f6d488af3e0e9bb1b19 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 2 May 2013 16:54:49 -0400 Subject: [PATCH 239/511] Fix executeNonQuery functino on constructor template --- src/odbc_statement.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 5a98a61f..863c311a 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -52,7 +52,7 @@ void ODBCStatement::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirect", ExecuteDirect); NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirectSync", ExecuteDirectSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeNonQuery", Execute); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeNonQuery", ExecuteNonQuery); NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeNonQuerySync", ExecuteNonQuerySync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare); From 6179ac774111a7a5fd3eb98bf18279f8199e24e6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 2 May 2013 17:00:07 -0400 Subject: [PATCH 240/511] test: executeSync() instead of async --- test/test-prepare-bindSync-execute-closeSync.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/test-prepare-bindSync-execute-closeSync.js b/test/test-prepare-bindSync-execute-closeSync.js index bb9282b9..7025a058 100644 --- a/test/test-prepare-bindSync-execute-closeSync.js +++ b/test/test-prepare-bindSync-execute-closeSync.js @@ -25,18 +25,13 @@ function issueQuery3(done) { for (var x = 0; x < iterations; x++) { (function (x) { stmt.bindSync([x]); - stmt.execute(function (err, result) { - cb(err, result, x); - }); + var result = stmt.executeSync() + cb(result, x); + })(x); } - function cb (err, result, x) { - if (err) { - console.error(err); - return finish(); - } - + function cb (result, x) { assert.deepEqual(result.fetchAllSync(), [ { test : x } ]); result.closeSync(); From 2849d4d7e27af248b39e056cec60bc8ee5488610 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 2 May 2013 22:03:56 -0400 Subject: [PATCH 241/511] properly pass params to conn.query in queryResult --- lib/odbc.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 7c0bd53f..d841303b 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -180,16 +180,23 @@ Database.prototype.queryResult = function (sql, params, cb) { if (typeof(params) == 'function') { cb = params; - params = []; + params = null; } if (!self.connected) { - return cb({ message : "Connection not open."}, [], false); + return cb({ message : "Connection not open."}, null); } self.queue.push(function (next) { //ODBCConnection.query() is the fastest-path querying mechanism. - self.conn.query(sql, params, function (err, result) { + if (params) { + self.conn.query(sql, params, cbQuery); + } + else { + self.conn.query(sql, cbQuery); + } + + function cbQuery (err, result) { if (err) { cb(err, null); @@ -203,7 +210,7 @@ Database.prototype.queryResult = function (sql, params, cb) { cb(err, result); return next(); - }); + } }); }; From 4dbbf849753749ba5a752b9cb1a293fcc608276e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 2 May 2013 22:04:31 -0400 Subject: [PATCH 242/511] fix SQLRowCount declaration in dynodbc --- src/dynodbc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dynodbc.h b/src/dynodbc.h index ded4b262..d608f25c 100644 --- a/src/dynodbc.h +++ b/src/dynodbc.h @@ -81,7 +81,7 @@ typedef RETCODE (SQL_API * pfnSQLAllocHandle)( typedef RETCODE (SQL_API * pfnSQLRowCount)( SQLHSTMT StatementHandle, - SQLINTEGER *RowCount); + SQLLEN *RowCount); typedef RETCODE (SQL_API * pfnSQLNumResultCols)( SQLHSTMT StatementHandle, @@ -227,7 +227,7 @@ typedef RETCODE (SQL_API * pfnSQLPrepare)( typedef RETCODE (SQL_API * pfnSQLRowCount)( HSTMT hstmt, - SDWORD FAR *pcrow); + SQLLEN FAR *pcrow); typedef RETCODE (SQL_API * pfnSQLSetCursorName)( HSTMT hstmt, From 18f0dfa2b4dd451526ed25aff2610abece845e93 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 2 May 2013 22:06:05 -0400 Subject: [PATCH 243/511] 0.5.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d043028e..f69fc52a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.9", + "version": "0.5.10", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 07ec4c85275c7dbb8a5d8bbc9e7549c9c3483c82 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 3 May 2013 12:00:27 -0400 Subject: [PATCH 244/511] dynodbc: if dlsym fails, printf the function not loaded --- src/dynodbc.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dynodbc.cpp b/src/dynodbc.cpp index 3dd8f1bb..9042b332 100644 --- a/src/dynodbc.cpp +++ b/src/dynodbc.cpp @@ -41,7 +41,11 @@ void* GetFunction(void *Lib, char *Fnname) #if defined(_MSC_VER) // Microsoft compiler return (void*)GetProcAddress((HINSTANCE)Lib,Fnname); #elif defined(__GNUC__) // GNU compiler - return dlsym(Lib,Fnname); + void * tmp = dlsym(Lib, Fnname); + if (!tmp) { + printf("node-odbc: error loading function: %s\n", Fnname); + } + return tmp; #endif } @@ -173,4 +177,4 @@ BOOL DynLoadODBC( char* odbcModuleName ) return (s_fODBCLoaded); } -#endif \ No newline at end of file +#endif From 63220a7cb628a9c359445e6df3a57273b470529f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 3 May 2013 12:27:37 -0400 Subject: [PATCH 245/511] Update prefered way to execute a prepared statment --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5179f964..317f9f40 100644 --- a/README.md +++ b/README.md @@ -232,11 +232,8 @@ db.openSync(cn); //Blocks while preparing the statement var stmt = db.prepareSync("insert into hits (col1, col2) VALUES (?, ?)") -//Bind some values to the statement -stmt.bindSync(['something', 42]); - -//Execute the statment asynchronously -stmt.execute(function (err, result) { +//Bind and Execute the statment asynchronously +stmt.execute(['something', 42], function (err, result) { result.closeSync(); //Close the connection From 6f9259cb6a8ceb30aa8c93fedbcac18353a81cf2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 3 May 2013 14:16:11 -0400 Subject: [PATCH 246/511] Disable loading of SQLDrivers in dynodbc mode --- src/dynodbc.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/dynodbc.cpp b/src/dynodbc.cpp index 9042b332..abaad03c 100644 --- a/src/dynodbc.cpp +++ b/src/dynodbc.cpp @@ -136,7 +136,16 @@ BOOL DynLoadODBC( char* odbcModuleName ) if (LOAD_ENTRY( hMod, SQLFreeEnv ) ) if (LOAD_ENTRY( hMod, SQLTransact ) ) if (LOAD_ENTRY( hMod, SQLSetConnectOption ) ) - if (LOAD_ENTRY( hMod, SQLDrivers ) ) +/* + * NOTE: This is commented out because it wouldn't be used + * in a direct-to-driver situation and we currently never + * call SQLDrivers. But if we ever do we may need to have + * some type of flag do determine if we should try to load + * this function if the user is not doing a direct-to-driver + * and is specifying a specific libodbc library. + */ +// if (LOAD_ENTRY( hMod, SQLDrivers ) ) + if (LOAD_ENTRY( hMod, SQLDataSources ) ) //#endif if (LOAD_ENTRY( hMod, SQLBindCol ) ) From 95787125523eef10d90158f2b5df994a362eeaad Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 7 May 2013 23:16:08 -0400 Subject: [PATCH 247/511] Attempt to determine if number is double or int in ODBC::GetParametersFromArray() --- src/odbc.cpp | 49 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 3d22096e..88905420 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -619,18 +620,42 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { *number); } else if (value->IsNumber()) { - double *number = new double(value->NumberValue()); - params[i].c_type = SQL_C_DOUBLE; - params[i].type = SQL_DECIMAL; - params[i].buffer = number; - params[i].length = 0; - params[i].decimals = 6; //idk, i just chose this randomly. - params[i].size = 10; //also just a guess - - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", - i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length); + double *number = new double(value->NumberValue()); + + double tmp = 0.0; + if (modf(*number, &tmp) == 0.0) { + //This is actually an integer + //delete the double number because we will not be using it + delete number; + + params[i].c_type = SQL_C_SBIGINT; + params[i].type = SQL_BIGINT; + params[i].buffer = new int64_t(value->IntegerValue()); + params[i].buffer_length = sizeof(int64_t); + params[i].length = params[i].buffer_length; + params[i].decimals = 0; + params[i].size = sizeof(int64_t); + + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber() - IntegerValue(): params[%i] " + "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + i, params[i].c_type, params[i].type, + params[i].buffer_length, params[i].size, params[i].length); + } + else { + //This is actually a float + params[i].c_type = SQL_C_DOUBLE; + params[i].type = SQL_DECIMAL; + params[i].buffer = number; + params[i].buffer_length = sizeof(double); + params[i].length = params[i].buffer_length; + params[i].decimals = 0; + params[i].size = sizeof(double); + + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber() - NumberValue() : params[%i] " + "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + i, params[i].c_type, params[i].type, + params[i].buffer_length, params[i].size, params[i].length); + } } else if (value->IsBoolean()) { bool *boolean = new bool(value->BooleanValue()); From 7e82d4bd13eba2fd0e71d465548b7e3e6eb6bc19 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 7 May 2013 23:17:04 -0400 Subject: [PATCH 248/511] test: update param test to test double vs int detection --- test/test-param-select-with-numbers-only.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/test-param-select-with-numbers-only.js b/test/test-param-select-with-numbers-only.js index 3450f07f..ad500d55 100644 --- a/test/test-param-select-with-numbers-only.js +++ b/test/test-param-select-with-numbers-only.js @@ -7,15 +7,17 @@ var common = require("./common") db.open(common.connectionString, function (err) { assert.equal(err, null); - db.query("select ? as INTCOL1, ? as INTCOL2, ? as INTCOL3 " - , [5, 3, 1] + db.query("select ? as INTCOL1, ? as INTCOL2, ? as INTCOL3, ? as FLOATCOL4, ? as FLOATYINT" + , [5, 3, 1, 1.23456789012345, 12345.000] , function (err, data, more) { db.close(function () { assert.equal(err, null); assert.deepEqual(data, [{ INTCOL1: 5, INTCOL2: 3, - INTCOL3: 1 + INTCOL3: 1, + FLOATCOL4 : 1.23456789012345, + FLOATYINT : 12345 }]); }); }); From 4297ff115ccba6fded6cb3543111a3baafddfe76 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 7 May 2013 23:17:40 -0400 Subject: [PATCH 249/511] 0.5.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f69fc52a..13a2322a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.10", + "version": "0.5.11", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From cb3dd33e227d1cc3548dc93506cb171a86d9c850 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 7 May 2013 23:30:04 -0400 Subject: [PATCH 250/511] Simplify number handling to just process as double I am not entirely sure if there is any point in detecting if the number is actually a double or an int. The overhead of detecting which to use may outweigh that of using it. Will have to test further, but if we want to go back to using the double/int determination, then revert this commit --- src/odbc.cpp | 47 ++++++++++++----------------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 88905420..ba55cee3 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -16,7 +16,6 @@ */ #include -#include #include #include #include @@ -622,40 +621,18 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { else if (value->IsNumber()) { double *number = new double(value->NumberValue()); - double tmp = 0.0; - if (modf(*number, &tmp) == 0.0) { - //This is actually an integer - //delete the double number because we will not be using it - delete number; - - params[i].c_type = SQL_C_SBIGINT; - params[i].type = SQL_BIGINT; - params[i].buffer = new int64_t(value->IntegerValue()); - params[i].buffer_length = sizeof(int64_t); - params[i].length = params[i].buffer_length; - params[i].decimals = 0; - params[i].size = sizeof(int64_t); - - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber() - IntegerValue(): params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", - i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length); - } - else { - //This is actually a float - params[i].c_type = SQL_C_DOUBLE; - params[i].type = SQL_DECIMAL; - params[i].buffer = number; - params[i].buffer_length = sizeof(double); - params[i].length = params[i].buffer_length; - params[i].decimals = 0; - params[i].size = sizeof(double); - - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber() - NumberValue() : params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", - i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length); - } + params[i].c_type = SQL_C_DOUBLE; + params[i].type = SQL_DECIMAL; + params[i].buffer = number; + params[i].buffer_length = sizeof(double); + params[i].length = params[i].buffer_length; + params[i].decimals = 0; + params[i].size = sizeof(double); + + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] " + "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + i, params[i].c_type, params[i].type, + params[i].buffer_length, params[i].size, params[i].length); } else if (value->IsBoolean()) { bool *boolean = new bool(value->BooleanValue()); From 43b26c7069dd5c9a15d223ba1bc55951735b8a08 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 8 May 2013 00:10:41 -0400 Subject: [PATCH 251/511] Add ODBCResult::GetColumnNamesSync This is so that we can retrieve the column names in case we are fetching in a array mode. --- src/odbc_result.cpp | 26 ++++++++++++++++++++++++++ src/odbc_result.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index d1aebe72..3acba2c9 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -54,6 +54,7 @@ void ODBCResult::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchSync", FetchSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAllSync", FetchAllSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "getColumnNamesSync", GetColumnNamesSync); // Properties instance_template->SetAccessor(String::New("fetchMode"), FetchModeGetter, FetchModeSetter); @@ -708,3 +709,28 @@ Handle ODBCResult::MoreResultsSync(const Arguments& args) { return scope.Close(SQL_SUCCEEDED(ret) ? True() : False()); } + +/* + * GetColumnNamesSync + */ + +Handle ODBCResult::GetColumnNamesSync(const Arguments& args) { + DEBUG_PRINTF("ODBCResult::GetColumnNamesSync\n"); + + HandleScope scope; + + ODBCResult* self = ObjectWrap::Unwrap(args.Holder()); + + Local cols = Array::New(); + + if (self->colCount == 0) { + self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); + } + + for (int i = 0; i < self->colCount; i++) { + cols->Set(Integer::New(i), + String::New((const char *) self->columns[i].name)); + } + + return scope.Close(cols); +} \ No newline at end of file diff --git a/src/odbc_result.h b/src/odbc_result.h index 15ef4112..da4a3f8f 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -54,6 +54,7 @@ class ODBCResult : public node::ObjectWrap { static Handle MoreResultsSync(const Arguments& args); static Handle FetchSync(const Arguments& args); static Handle FetchAllSync(const Arguments& args); + static Handle GetColumnNamesSync(const Arguments& args); //property getter/setters static Handle FetchModeGetter(Local property, const AccessorInfo &info); From a17cd0b35b0306aaf2d87c5365a8b9ad670f8497 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 8 May 2013 00:12:02 -0400 Subject: [PATCH 252/511] 0.5.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13a2322a..8b487405 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.11", + "version": "0.5.12", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From df1f2f70ca479f8c89b2947351cd1b699f963e82 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 8 May 2013 01:23:08 -0400 Subject: [PATCH 253/511] Modify ODBC::GetSQLDiagRecError to take handleType and handle --- src/odbc.cpp | 46 ++++++++++++++++++++++++----------------- src/odbc.h | 2 +- src/odbc_connection.cpp | 10 ++++----- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index ba55cee3..300997d4 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -129,9 +129,12 @@ Handle ODBC::New(const Arguments& args) { uv_mutex_unlock(&ODBC::g_odbcMutex); - //TODO: check if ret succeeded, if not, throw error to javascript land if (!SQL_SUCCEEDED(ret)) { - //TODO: do something. + DEBUG_PRINTF("ODBC::New - ERROR ALLOCATING ENV HANDLE!!\n"); + + Local objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_ENV, dbo->m_hEnv); + + ThrowException(objError); } return scope.Close(args.Holder()); @@ -182,11 +185,7 @@ void ODBC::UV_CreateConnection(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); //allocate a new connection handle - int ret = SQLAllocConnect(data->dbo->m_hEnv, &data->hDBC); - - if (!SQL_SUCCEEDED(ret)) { - //TODO: do something. - } + data->result = SQLAllocHandle(SQL_HANDLE_DBC, data->dbo->m_hEnv, &data->hDBC); uv_mutex_unlock(&ODBC::g_odbcMutex); } @@ -197,17 +196,26 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { create_connection_work_data* data = (create_connection_work_data *)(req->data); - Local args[2]; - args[0] = External::New(data->dbo->m_hEnv); - args[1] = External::New(data->hDBC); - - Persistent js_result(ODBCConnection::constructor_template-> - GetFunction()->NewInstance(2, args)); + if (!SQL_SUCCEEDED(data->result)) { + Local args[1]; + + args[0] = ODBC::GetSQLDiagRecError(SQL_HANDLE_ENV, data->dbo->m_hEnv); + + data->cb->Call(Context::GetCurrent()->Global(), 1, args); + } + else { + Local args[2]; + args[0] = External::New(data->dbo->m_hEnv); + args[1] = External::New(data->hDBC); + + Persistent js_result(ODBCConnection::constructor_template-> + GetFunction()->NewInstance(2, args)); - args[0] = Local::New(Null()); - args[1] = Local::New(js_result); + args[0] = Local::New(Null()); + args[1] = Local::New(js_result); - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + } data->dbo->Unref(); data->cb.Dispose(); @@ -711,7 +719,7 @@ Local ODBC::GetSQLError (HENV hENV, * GetSQLDiagRecError */ -Local ODBC::GetSQLDiagRecError (HDBC hDBC) { +Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle) { HandleScope scope; Local objError = Object::New(); @@ -725,8 +733,8 @@ Local ODBC::GetSQLDiagRecError (HDBC hDBC) { do { ret = SQLGetDiagRec( - SQL_HANDLE_DBC, - hDBC, + handleType, + handle, ++i, (SQLCHAR *) errorSQLState, &native, diff --git a/src/odbc.h b/src/odbc.h index 25d62b1c..628be12d 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -73,7 +73,7 @@ class ODBC : public node::ObjectWrap { static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle CallbackSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, Persistent cb); static Local GetSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, char* message); - static Local GetSQLDiagRecError (HDBC hDBC); + static Local GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle); static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); #ifdef dynodbc static Handle LoadODBCLibrary(const Arguments& args); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index a0993a58..b88fc34f 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -230,7 +230,7 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { if (data->result) { err = true; - Local objError = ODBC::GetSQLDiagRecError(data->conn->self()->m_hDBC); + Local objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, data->conn->self()->m_hDBC); argv[0] = objError; } @@ -301,7 +301,7 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { if (!SQL_SUCCEEDED(ret)) { err = true; - objError = ODBC::GetSQLDiagRecError(conn->self()->m_hDBC); + objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, conn->self()->m_hDBC); } else { HSTMT hStmt; @@ -1278,7 +1278,7 @@ Handle ODBCConnection::BeginTransactionSync(const Arguments& args) { SQL_NTS); if (!SQL_SUCCEEDED(ret)) { - Local objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); + Local objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, conn->m_hDBC); ThrowException(objError); @@ -1319,7 +1319,7 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { if (!SQL_SUCCEEDED(ret)) { error = true; - objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); + objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, conn->m_hDBC); } //Reset the connection back to autocommit @@ -1338,7 +1338,7 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { //be restarting the connection or something to deal with this state error = true; - objError = ODBC::GetSQLDiagRecError(conn->m_hDBC); + objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, conn->m_hDBC); } if (error) { From 650948cc99ca3ec7f0363828cf9d7fed7505e827 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 8 May 2013 01:23:57 -0400 Subject: [PATCH 254/511] Replace SQLAlloc* with SQLAllocHandle --- src/odbc.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 300997d4..f44e5fa2 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -125,7 +125,7 @@ Handle ODBC::New(const Arguments& args) { uv_mutex_lock(&ODBC::g_odbcMutex); - int ret = SQLAllocEnv( &dbo->m_hEnv ); + int ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &dbo->m_hEnv); uv_mutex_unlock(&ODBC::g_odbcMutex); @@ -137,6 +137,8 @@ Handle ODBC::New(const Arguments& args) { ThrowException(objError); } + SQLSetEnvAttr(dbo->m_hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER); + return scope.Close(args.Holder()); } @@ -241,7 +243,7 @@ Handle ODBC::CreateConnectionSync(const Arguments& args) { uv_mutex_lock(&ODBC::g_odbcMutex); //allocate a new connection handle - SQLRETURN ret = SQLAllocConnect(dbo->m_hEnv, &hDBC); + SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_DBC, dbo->m_hEnv, &hDBC); if (!SQL_SUCCEEDED(ret)) { //TODO: do something! From 3068d1805a34143da7e16889bf312b7522b941f4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 8 May 2013 01:29:35 -0400 Subject: [PATCH 255/511] Replace remaining SQLAllocStmt calls --- src/odbc_connection.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index b88fc34f..50134260 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -195,7 +195,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { HSTMT hStmt; //allocate a temporary statment - ret = SQLAllocStmt(self->m_hDBC, &hStmt); + ret = SQLAllocHandle(SQL_HANDLE_STMT, self->m_hDBC, &hStmt); //try to determine if the driver can handle //multiple recordsets @@ -307,7 +307,7 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { HSTMT hStmt; //allocate a temporary statment - ret = SQLAllocStmt(conn->m_hDBC, &hStmt); + ret = SQLAllocHandle(SQL_HANDLE_STMT, conn->m_hDBC, &hStmt); //try to determine if the driver can handle //multiple recordsets @@ -1151,7 +1151,7 @@ void ODBCConnection::UV_Tables(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); - SQLAllocStmt(data->conn->m_hDBC, &data->hSTMT ); + SQLAllocHandle(SQL_HANDLE_STMT, data->conn->m_hDBC, &data->hSTMT ); uv_mutex_unlock(&ODBC::g_odbcMutex); @@ -1241,7 +1241,7 @@ void ODBCConnection::UV_Columns(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); - SQLAllocStmt(data->conn->m_hDBC, &data->hSTMT ); + SQLAllocHandle(SQL_HANDLE_STMT, data->conn->m_hDBC, &data->hSTMT ); uv_mutex_unlock(&ODBC::g_odbcMutex); From 1ba30bdd13f7e6946f415389b2502e30cb880f63 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 8 May 2013 16:50:36 -0400 Subject: [PATCH 256/511] Proxy GetSQLError through GetDiagRec... --- src/odbc.cpp | 58 ++++++++++++++++++++++++++++++++-------------------- src/odbc.h | 3 ++- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index f44e5fa2..000fce97 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -695,26 +695,23 @@ Local ODBC::GetSQLError (HENV hENV, char* message) { HandleScope scope; - Local objError = Object::New(); - - char errorMessage[512]; - char errorSQLState[128]; + SQLSMALLINT handleType; + SQLHANDLE handle; - SQLError( - hENV, - hDBC, - hSTMT, - (SQLCHAR *) errorSQLState, - NULL, - (SQLCHAR *) errorMessage, - sizeof(errorMessage), - NULL); - - objError->Set(String::New("state"), String::New(errorSQLState)); - objError->Set(String::New("error"), String::New(message)); - objError->Set(String::New("message"), String::New(errorMessage)); + if (hSTMT) { + handleType = SQL_HANDLE_STMT; + handle = hSTMT; + } + else if (hDBC) { + handleType = SQL_HANDLE_DBC; + handle = hDBC; + } + else { + handleType = SQL_HANDLE_ENV; + handle = hENV; + } - return scope.Close(objError); + return scope.Close(GetSQLDiagRecError(handleType, handle, message)); } /* @@ -724,7 +721,16 @@ Local ODBC::GetSQLError (HENV hENV, Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle) { HandleScope scope; - Local objError = Object::New(); + return scope.Close(GetSQLDiagRecError( + handleType, + handle, + (char *) "[node-odbc] Error in some module")); +} + +Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle, char* message) { + HandleScope scope; + + Local errors = Array::New(); SQLINTEGER i = 0; SQLINTEGER native; @@ -736,7 +742,7 @@ Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle do { ret = SQLGetDiagRec( handleType, - handle, + &handle, ++i, (SQLCHAR *) errorSQLState, &native, @@ -745,13 +751,21 @@ Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle &len); if (SQL_SUCCEEDED(ret)) { - objError->Set(String::New("error"), String::New("[node-odbc] SQL_ERROR")); + Local objError = Object::New(); + + objError->Set(String::New("error"), String::New(message)); objError->Set(String::New("message"), String::New(errorMessage)); objError->Set(String::New("state"), String::New(errorSQLState)); + + errors->Set(Number::New(i), objError); } } while( ret == SQL_SUCCESS ); - return scope.Close(objError); + Local objReturn = Object::New(); + + objReturn->Set(String::New("errors"), errors); + + return scope.Close(objReturn); } /* diff --git a/src/odbc.h b/src/odbc.h index 628be12d..6f8b4204 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -73,7 +73,8 @@ class ODBC : public node::ObjectWrap { static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle CallbackSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, Persistent cb); static Local GetSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, char* message); - static Local GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle); + static Handle GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle); + static Handle GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle, char* message); static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); #ifdef dynodbc static Handle LoadODBCLibrary(const Arguments& args); From d799f9b63ecb464878edca4839777f73fd1fd47e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 8 May 2013 16:58:04 -0400 Subject: [PATCH 257/511] Fix function signatures and add debug --- src/odbc.cpp | 2 ++ src/odbc.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 000fce97..949869f9 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -730,6 +730,8 @@ Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle, char* message) { HandleScope scope; + DEBUG_PRINTF("ODBC::GetSQLDiagRecError : handleType=%i, handle=%p\n", handleType, handle); + Local errors = Array::New(); SQLINTEGER i = 0; diff --git a/src/odbc.h b/src/odbc.h index 6f8b4204..02803141 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -73,8 +73,8 @@ class ODBC : public node::ObjectWrap { static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle CallbackSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, Persistent cb); static Local GetSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, char* message); - static Handle GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle); - static Handle GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle, char* message); + static Local GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle); + static Local GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle, char* message); static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); #ifdef dynodbc static Handle LoadODBCLibrary(const Arguments& args); From fa20989da3c102e6fd0e7f1f95acb849b544b356 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 8 May 2013 19:05:13 -0400 Subject: [PATCH 258/511] Properly loop through SQLGetDiagRec --- src/odbc.cpp | 37 +++++++++++++++++++++++-------------- src/odbc_connection.cpp | 9 +++------ 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 949869f9..8f36e1ef 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -724,7 +724,7 @@ Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle return scope.Close(GetSQLDiagRecError( handleType, handle, - (char *) "[node-odbc] Error in some module")); + (char *) "[node-odbc] SQL_ERROR")); } Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle, char* message) { @@ -732,42 +732,51 @@ Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle DEBUG_PRINTF("ODBC::GetSQLDiagRecError : handleType=%i, handle=%p\n", handleType, handle); - Local errors = Array::New(); + Local objError = Object::New(); SQLINTEGER i = 0; SQLINTEGER native; + SQLSMALLINT len; + SQLINTEGER numfields; SQLRETURN ret; char errorSQLState[7]; char errorMessage[256]; - do { + SQLGetDiagField( + handleType, + handle, + 1, + SQL_DIAG_NUMBER, + &numfields, + SQL_IS_INTEGER, + &len); + + for (i = 0; i < numfields; i++){ + DEBUG_PRINTF("ODBC::GetSQLDiagRecError : calling SQLGetDiagRec; i=%i, numfields=%i\n", i, numfields); + ret = SQLGetDiagRec( handleType, - &handle, - ++i, + handle, + i + 1, (SQLCHAR *) errorSQLState, &native, (SQLCHAR *) errorMessage, sizeof(errorMessage), &len); + + DEBUG_PRINTF("ODBC::GetSQLDiagRecError : after SQLGetDiagRec; i=%i\n", i); if (SQL_SUCCEEDED(ret)) { - Local objError = Object::New(); + DEBUG_PRINTF("ODBC::GetSQLDiagRecError : errorMessage=%s, errorSQLState=%s\n", errorMessage, errorSQLState); objError->Set(String::New("error"), String::New(message)); objError->Set(String::New("message"), String::New(errorMessage)); objError->Set(String::New("state"), String::New(errorSQLState)); - - errors->Set(Number::New(i), objError); } - } while( ret == SQL_SUCCESS ); - - Local objReturn = Object::New(); - - objReturn->Set(String::New("errors"), errors); + } - return scope.Close(objReturn); + return scope.Close(objError); } /* diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 50134260..ba24e316 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -1041,14 +1041,11 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { //check to see if there was an error during execution if(ret == SQL_ERROR) { - objError = ODBC::GetSQLError( - conn->m_hENV, - conn->m_hDBC, + ThrowException(ODBC::GetSQLDiagRecError( + SQL_HANDLE_STMT, hSTMT, (char *) "[node-odbc] Error in ODBCConnection::QuerySync" - ); - - ThrowException(objError); + )); return scope.Close(Undefined()); } From 66768bd97fc7974083b9eb4b7a5da8163d7c97a8 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 8 May 2013 19:07:55 -0400 Subject: [PATCH 259/511] Check handle allocation succeeded --- src/odbc_connection.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index ba24e316..487869c1 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -890,7 +890,6 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); - Local objError = Object::New(); Parameter* params = new Parameter[0]; Parameter prm; SQLRETURN ret; @@ -973,14 +972,19 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { uv_mutex_lock(&ODBC::g_odbcMutex); //allocate a new statment handle - SQLAllocHandle( SQL_HANDLE_STMT, + ret = SQLAllocHandle( SQL_HANDLE_STMT, conn->m_hDBC, &hSTMT ); uv_mutex_unlock(&ODBC::g_odbcMutex); + DEBUG_PRINTF("ODBCConnection::QuerySync - hSTMT=%p\n", hSTMT); + //check to see if should excute a direct or a parameter bound query - if (!paramCount) { + if (!SQL_SUCCEEDED(ret)) { + //We'll check again later + } + else if (!paramCount) { // execute the query directly ret = SQLExecDirect( hSTMT, From 2973603dd3a78bfc1646e7fe4a33fd4a419a0e14 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 9 May 2013 12:46:46 -0400 Subject: [PATCH 260/511] Replace GetSQLDiagRecError with GetSQLError in all instances What really happened is that I change the name of GetSQLDiagRecError to GetSQLError and then modfied all calls to GetSQLDiagRecError and GetSQLError so that they are calling properly. Also, GetSQLError is now overloaded so that a custom error message can be added to the error object, or not. ' --- src/odbc.cpp | 77 ++++++++++++++++------------------------- src/odbc.h | 8 ++--- src/odbc_connection.cpp | 15 ++++---- src/odbc_result.cpp | 12 +++---- src/odbc_statement.cpp | 27 +++++---------- 5 files changed, 53 insertions(+), 86 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 8f36e1ef..2ba8a75a 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -132,7 +132,7 @@ Handle ODBC::New(const Arguments& args) { if (!SQL_SUCCEEDED(ret)) { DEBUG_PRINTF("ODBC::New - ERROR ALLOCATING ENV HANDLE!!\n"); - Local objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_ENV, dbo->m_hEnv); + Local objError = ODBC::GetSQLError(SQL_HANDLE_ENV, dbo->m_hEnv); ThrowException(objError); } @@ -201,7 +201,7 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { if (!SQL_SUCCEEDED(data->result)) { Local args[1]; - args[0] = ODBC::GetSQLDiagRecError(SQL_HANDLE_ENV, data->dbo->m_hEnv); + args[0] = ODBC::GetSQLError(SQL_HANDLE_ENV, data->dbo->m_hEnv); data->cb->Call(Context::GetCurrent()->Global(), 1, args); } @@ -665,17 +665,28 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { * CallbackSQLError */ -Handle ODBC::CallbackSQLError (HENV hENV, - HDBC hDBC, - HSTMT hSTMT, - Persistent cb) { +Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, + SQLHANDLE handle, + Persistent cb) { + HandleScope scope; + + return scope.Close(CallbackSQLError( + handleType, + handle, + (char *) "[node-odbc] SQL_ERROR", + cb)); +} + +Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, + SQLHANDLE handle, + char* message, + Persistent cb) { HandleScope scope; Local objError = ODBC::GetSQLError( - hENV, - hDBC, - hSTMT, - (char *) "[node-odbc] Error in some module" + handleType, + handle, + message ); Local args[1]; @@ -689,48 +700,19 @@ Handle ODBC::CallbackSQLError (HENV hENV, * GetSQLError */ -Local ODBC::GetSQLError (HENV hENV, - HDBC hDBC, - HSTMT hSTMT, - char* message) { - HandleScope scope; - - SQLSMALLINT handleType; - SQLHANDLE handle; - - if (hSTMT) { - handleType = SQL_HANDLE_STMT; - handle = hSTMT; - } - else if (hDBC) { - handleType = SQL_HANDLE_DBC; - handle = hDBC; - } - else { - handleType = SQL_HANDLE_ENV; - handle = hENV; - } - - return scope.Close(GetSQLDiagRecError(handleType, handle, message)); -} - -/* - * GetSQLDiagRecError - */ - -Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle) { +Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle) { HandleScope scope; - return scope.Close(GetSQLDiagRecError( + return scope.Close(GetSQLError( handleType, handle, (char *) "[node-odbc] SQL_ERROR")); } -Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle, char* message) { +Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message) { HandleScope scope; - DEBUG_PRINTF("ODBC::GetSQLDiagRecError : handleType=%i, handle=%p\n", handleType, handle); + DEBUG_PRINTF("ODBC::GetSQLError : handleType=%i, handle=%p\n", handleType, handle); Local objError = Object::New(); @@ -753,7 +735,7 @@ Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle &len); for (i = 0; i < numfields; i++){ - DEBUG_PRINTF("ODBC::GetSQLDiagRecError : calling SQLGetDiagRec; i=%i, numfields=%i\n", i, numfields); + DEBUG_PRINTF("ODBC::GetSQLError : calling SQLGetDiagRec; i=%i, numfields=%i\n", i, numfields); ret = SQLGetDiagRec( handleType, @@ -765,10 +747,10 @@ Local ODBC::GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle sizeof(errorMessage), &len); - DEBUG_PRINTF("ODBC::GetSQLDiagRecError : after SQLGetDiagRec; i=%i\n", i); + DEBUG_PRINTF("ODBC::GetSQLError : after SQLGetDiagRec; i=%i\n", i); if (SQL_SUCCEEDED(ret)) { - DEBUG_PRINTF("ODBC::GetSQLDiagRecError : errorMessage=%s, errorSQLState=%s\n", errorMessage, errorSQLState); + DEBUG_PRINTF("ODBC::GetSQLError : errorMessage=%s, errorSQLState=%s\n", errorMessage, errorSQLState); objError->Set(String::New("error"), String::New(message)); objError->Set(String::New("message"), String::New(errorMessage)); @@ -814,8 +796,7 @@ Local ODBC::GetAllRecordsSync (HENV hENV, errorCount++; objError = ODBC::GetSQLError( - hENV, - hDBC, + SQL_HANDLE_STMT, hSTMT, (char *) "[node-odbc] Error in ODBC::GetAllRecordsSync" ); diff --git a/src/odbc.h b/src/odbc.h index 02803141..af5efb4b 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -71,10 +71,10 @@ class ODBC : public node::ObjectWrap { static Handle GetColumnValue(SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength); static Local GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); - static Handle CallbackSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, Persistent cb); - static Local GetSQLError (HENV hENV, HDBC hDBC, HSTMT hSTMT, char* message); - static Local GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle); - static Local GetSQLDiagRecError (SQLSMALLINT handleType, SQLHANDLE handle, char* message); + static Handle CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, Persistent cb); + static Handle CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, Persistent cb); + static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle); + static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message); static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); #ifdef dynodbc static Handle LoadODBCLibrary(const Arguments& args); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 487869c1..979f3082 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -230,7 +230,7 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { if (data->result) { err = true; - Local objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, data->conn->self()->m_hDBC); + Local objError = ODBC::GetSQLError(SQL_HANDLE_DBC, data->conn->self()->m_hDBC); argv[0] = objError; } @@ -301,7 +301,7 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { if (!SQL_SUCCEEDED(ret)) { err = true; - objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, conn->self()->m_hDBC); + objError = ODBC::GetSQLError(SQL_HANDLE_DBC, conn->self()->m_hDBC); } else { HSTMT hStmt; @@ -797,8 +797,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { //check to see if there was an error during execution if(data->result == SQL_ERROR) { ODBC::CallbackSQLError( - data->conn->m_hENV, - data->conn->m_hDBC, + SQL_HANDLE_STMT, data->hSTMT, data->cb); } @@ -1045,7 +1044,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { //check to see if there was an error during execution if(ret == SQL_ERROR) { - ThrowException(ODBC::GetSQLDiagRecError( + ThrowException(ODBC::GetSQLError( SQL_HANDLE_STMT, hSTMT, (char *) "[node-odbc] Error in ODBCConnection::QuerySync" @@ -1279,7 +1278,7 @@ Handle ODBCConnection::BeginTransactionSync(const Arguments& args) { SQL_NTS); if (!SQL_SUCCEEDED(ret)) { - Local objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, conn->m_hDBC); + Local objError = ODBC::GetSQLError(SQL_HANDLE_DBC, conn->m_hDBC); ThrowException(objError); @@ -1320,7 +1319,7 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { if (!SQL_SUCCEEDED(ret)) { error = true; - objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, conn->m_hDBC); + objError = ODBC::GetSQLError(SQL_HANDLE_DBC, conn->m_hDBC); } //Reset the connection back to autocommit @@ -1339,7 +1338,7 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { //be restarting the connection or something to deal with this state error = true; - objError = ODBC::GetSQLDiagRecError(SQL_HANDLE_DBC, conn->m_hDBC); + objError = ODBC::GetSQLError(SQL_HANDLE_DBC, conn->m_hDBC); } if (error) { diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 3acba2c9..00d571ba 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -246,8 +246,7 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { error = true; objError = ODBC::GetSQLError( - data->objResult->m_hENV, - data->objResult->m_hDBC, + SQL_HANDLE_STMT, data->objResult->m_hSTMT, (char *) "Error in ODBCResult::UV_AfterFetch"); } @@ -349,8 +348,7 @@ Handle ODBCResult::FetchSync(const Arguments& args) { error = true; objError = ODBC::GetSQLError( - objResult->m_hENV, - objResult->m_hDBC, + SQL_HANDLE_STMT, objResult->m_hSTMT, (char *) "Error in ODBCResult::UV_AfterFetch"); } @@ -487,8 +485,7 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { data->errorCount++; data->objError = Persistent::New(ODBC::GetSQLError( - self->m_hENV, - self->m_hDBC, + SQL_HANDLE_STMT, self->m_hSTMT, (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll" )); @@ -605,8 +602,7 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { errorCount++; objError = ODBC::GetSQLError( - self->m_hENV, - self->m_hDBC, + SQL_HANDLE_STMT, self->m_hSTMT, (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll; probably" " your query did not have a result set." diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 863c311a..f0030d41 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -204,8 +204,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { //First thing, let's check if the execution of the query returned any errors if(data->result == SQL_ERROR) { ODBC::CallbackSQLError( - self->m_hENV, - self->m_hDBC, + SQL_HANDLE_STMT, self->m_hSTMT, data->cb); } @@ -259,8 +258,7 @@ Handle ODBCStatement::ExecuteSync(const Arguments& args) { if(ret == SQL_ERROR) { ThrowException(ODBC::GetSQLError( - stmt->m_hENV, - stmt->m_hDBC, + SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); @@ -342,8 +340,7 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { //First thing, let's check if the execution of the query returned any errors if(data->result == SQL_ERROR) { ODBC::CallbackSQLError( - self->m_hENV, - self->m_hDBC, + SQL_HANDLE_STMT, self->m_hSTMT, data->cb); } @@ -400,8 +397,7 @@ Handle ODBCStatement::ExecuteNonQuerySync(const Arguments& args) { if(ret == SQL_ERROR) { ThrowException(ODBC::GetSQLError( - stmt->m_hENV, - stmt->m_hDBC, + SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); @@ -492,8 +488,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { //First thing, let's check if the execution of the query returned any errors if(data->result == SQL_ERROR) { ODBC::CallbackSQLError( - self->m_hENV, - self->m_hDBC, + SQL_HANDLE_STMT, self->m_hSTMT, data->cb); } @@ -553,8 +548,7 @@ Handle ODBCStatement::ExecuteDirectSync(const Arguments& args) { if(ret == SQL_ERROR) { ThrowException(ODBC::GetSQLError( - stmt->m_hENV, - stmt->m_hDBC, + SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteDirectSync" )); @@ -688,8 +682,7 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { //First thing, let's check if the execution of the query returned any errors if(data->result == SQL_ERROR) { ODBC::CallbackSQLError( - data->stmt->m_hENV, - data->stmt->m_hDBC, + SQL_HANDLE_STMT, data->stmt->m_hSTMT, data->cb); } @@ -806,8 +799,7 @@ Handle ODBCStatement::BindSync(const Arguments& args) { } else { ThrowException(ODBC::GetSQLError( - stmt->m_hENV, - stmt->m_hDBC, + SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::BindSync" )); @@ -951,8 +943,7 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { //Check if there were errors if(data->result == SQL_ERROR) { ODBC::CallbackSQLError( - self->m_hENV, - self->m_hDBC, + SQL_HANDLE_STMT, self->m_hSTMT, data->cb); } From e7b8708d55a8d9013c8dd9ac20157542301c3999 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 9 May 2013 12:48:30 -0400 Subject: [PATCH 261/511] 0.5.13 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8b487405..7fb661be 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.12", + "version": "0.5.13", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From f82529565913a75d1902775c73323a73f147f979 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 9 May 2013 14:17:26 -0400 Subject: [PATCH 262/511] Modify dynodbc to not load functions we don't use --- src/dynodbc.cpp | 58 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/src/dynodbc.cpp b/src/dynodbc.cpp index abaad03c..924e5cf4 100644 --- a/src/dynodbc.cpp +++ b/src/dynodbc.cpp @@ -127,55 +127,53 @@ BOOL DynLoadODBC( char* odbcModuleName ) //#if (ODBCVER < 0x0300) if (LOAD_ENTRY( hMod, SQLGetData ) ) if (LOAD_ENTRY( hMod, SQLGetFunctions ) ) - if (LOAD_ENTRY( hMod, SQLAllocConnect ) ) - if (LOAD_ENTRY( hMod, SQLAllocEnv ) ) - if (LOAD_ENTRY( hMod, SQLAllocStmt ) ) - if (LOAD_ENTRY( hMod, SQLColAttributes ) ) - if (LOAD_ENTRY( hMod, SQLError ) ) - if (LOAD_ENTRY( hMod, SQLFreeConnect ) ) - if (LOAD_ENTRY( hMod, SQLFreeEnv ) ) - if (LOAD_ENTRY( hMod, SQLTransact ) ) - if (LOAD_ENTRY( hMod, SQLSetConnectOption ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLAllocConnect ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLAllocEnv ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLAllocStmt ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLColAttributes ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLError ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLFreeConnect ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLFreeEnv ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLTransact ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLSetConnectOption ) ) /* * NOTE: This is commented out because it wouldn't be used * in a direct-to-driver situation and we currently never * call SQLDrivers. But if we ever do we may need to have - * some type of flag do determine if we should try to load + * some type of flag to determine if we should try to load * this function if the user is not doing a direct-to-driver * and is specifying a specific libodbc library. */ -// if (LOAD_ENTRY( hMod, SQLDrivers ) ) +//Unused-> if (LOAD_ENTRY( hMod, SQLDrivers ) ) - if (LOAD_ENTRY( hMod, SQLDataSources ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLDataSources ) ) //#endif - if (LOAD_ENTRY( hMod, SQLBindCol ) ) - if (LOAD_ENTRY( hMod, SQLCancel ) ) - if (LOAD_ENTRY( hMod, SQLConnect ) ) - if (LOAD_ENTRY( hMod, SQLDescribeCol ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLBindCol ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLCancel ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLConnect ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLDescribeCol ) ) if (LOAD_ENTRY( hMod, SQLDisconnect ) ) if (LOAD_ENTRY( hMod, SQLExecDirect ) ) if (LOAD_ENTRY( hMod, SQLExecute ) ) if (LOAD_ENTRY( hMod, SQLFetch ) ) if (LOAD_ENTRY( hMod, SQLGetDiagRec ) ) if (LOAD_ENTRY( hMod, SQLFreeHandle ) ) - if (LOAD_ENTRY( hMod, SQLFetchScroll ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLFetchScroll ) ) if (LOAD_ENTRY( hMod, SQLColAttribute ) ) if (LOAD_ENTRY( hMod, SQLSetConnectAttr ) ) - if (LOAD_ENTRY( hMod, SQLDriverConnect ) ) - if (LOAD_ENTRY( hMod, SQLAllocHandle ) ) - if (LOAD_ENTRY( hMod, SQLRowCount ) ) - if (LOAD_ENTRY( hMod, SQLNumResultCols ) ) - if (LOAD_ENTRY( hMod, SQLEndTran ) ) - if (LOAD_ENTRY( hMod, SQLTables) ) - if (LOAD_ENTRY( hMod, SQLColumns) ) - if (LOAD_ENTRY( hMod, SQLBindParameter) ) - if (LOAD_ENTRY( hMod, SQLPrimaryKeys) ) - if (LOAD_ENTRY( hMod, SQLSetEnvAttr) ) - if (LOAD_ENTRY( hMod, SQLFreeStmt ) ) + if (LOAD_ENTRY( hMod, SQLDriverConnect ) ) + if (LOAD_ENTRY( hMod, SQLAllocHandle ) ) + if (LOAD_ENTRY( hMod, SQLRowCount ) ) if (LOAD_ENTRY( hMod, SQLNumResultCols ) ) + if (LOAD_ENTRY( hMod, SQLEndTran ) ) + if (LOAD_ENTRY( hMod, SQLTables ) ) + if (LOAD_ENTRY( hMod, SQLColumns ) ) + if (LOAD_ENTRY( hMod, SQLBindParameter ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLPrimaryKeys) ) + if (LOAD_ENTRY( hMod, SQLSetEnvAttr ) ) + if (LOAD_ENTRY( hMod, SQLFreeStmt ) ) if (LOAD_ENTRY( hMod, SQLPrepare ) ) - if (LOAD_ENTRY( hMod, SQLRowCount ) ) - if (LOAD_ENTRY( hMod, SQLGetInfo ) ) + //Unused-> if (LOAD_ENTRY( hMod, SQLGetInfo ) ) if (LOAD_ENTRY( hMod, SQLBindParameter ) ) if (LOAD_ENTRY( hMod, SQLMoreResults ) ) { From c7fa30b995932bbb5d15cfb9415b453e4a20b6cd Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 9 May 2013 14:18:22 -0400 Subject: [PATCH 263/511] Replace SQLSetConnectOption with SQLSetConnectAttr; closes #38 --- src/odbc_connection.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 979f3082..d775fb01 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -173,12 +173,18 @@ void ODBCConnection::UV_Open(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); + char connstr[1024]; + //TODO: make this configurable - //NOTE: SQLSetConnectOption requires the thread to be locked - SQLSetConnectOption( self->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); + int timeOut = 5; + + //NOTE: SQLSetConnectAttr requires the thread to be locked + SQLSetConnectAttr( + self->m_hDBC, //ConnectionHandle + SQL_ATTR_LOGIN_TIMEOUT, //Attribute + &timeOut, //ValuePtr + sizeof(timeOut)); //StringLength - char connstr[1024]; - //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked int ret = SQLDriverConnect( @@ -283,8 +289,14 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { uv_mutex_lock(&ODBC::g_odbcMutex); //TODO: make this configurable - //NOTE: SQLSetConnectOption requires the thread to be locked - SQLSetConnectOption( conn->m_hDBC, SQL_LOGIN_TIMEOUT, 5 ); + int timeOut = 5; + + //NOTE: SQLSetConnectAttr requires the thread to be locked + SQLSetConnectAttr( + conn->m_hDBC, //ConnectionHandle + SQL_ATTR_LOGIN_TIMEOUT, //Attribute + &timeOut, //ValuePtr + sizeof(timeOut)); //StringLength //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked From d47c2c8aead44a4f2304b62ea0b5e6a49e60155f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 9 May 2013 14:19:13 -0400 Subject: [PATCH 264/511] Add libsqlncli library to test with --- test/common.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/common.js b/test/common.js index 8755d5cf..88523a3c 100644 --- a/test/common.js +++ b/test/common.js @@ -1,6 +1,7 @@ var odbc = require("../"); //odbc.library = '/usr/lib/odbc/libsqlite3odbc-0.91'; //odbc.library = '/usr/lib/x86_64-linux-gnu/odbc/libtdsodbc'; +//odbc.library = '/opt/sqlncli-11.0.1790.0/lib64/libsqlncli-11.0'; exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; //exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; From 8468548cfdeb7af29a7a60a2e867433aeb0a0272 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 9 May 2013 14:20:11 -0400 Subject: [PATCH 265/511] 0.5.14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fb661be..878c4b46 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.13", + "version": "0.5.14", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From ffea76cf0e1f79fb4db6bd5009dea44fca3d4fd0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 28 Jun 2013 15:05:24 -0400 Subject: [PATCH 266/511] Fix windows time struct declaration --- src/odbc.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 2ba8a75a..52fe8613 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -403,22 +403,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, break; case SQL_DATETIME : case SQL_TIMESTAMP : { - struct tm timeInfo = { - tm_sec : 0 - , tm_min : 0 - , tm_hour : 0 - , tm_mday : 0 - , tm_mon : 0 - , tm_year : 0 - , tm_wday : 0 - , tm_yday : 0 - , tm_isdst : 0 - , tm_gmtoff : 0 - , tm_zone : 0 - }; //I am not sure if this is locale-safe or cross database safe, but it //works for me on MSSQL #ifdef _WIN32 + struct tm timeInfo = {}; + ret = SQLGetData( hStmt, column.index, @@ -444,6 +433,20 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, return Date::New((double(mktime(&timeInfo)) * 1000)); } #else + struct tm timeInfo = { + tm_sec : 0 + , tm_min : 0 + , tm_hour : 0 + , tm_mday : 0 + , tm_mon : 0 + , tm_year : 0 + , tm_wday : 0 + , tm_yday : 0 + , tm_isdst : 0 + , tm_gmtoff : 0 + , tm_zone : 0 + }; + SQL_TIMESTAMP_STRUCT odbcTime; ret = SQLGetData( From 62c70e0e137be6ce830c464eb82b9278f1ab1944 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 28 Jun 2013 15:05:38 -0400 Subject: [PATCH 267/511] 0.5.15 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 878c4b46..ff65c554 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.14", + "version": "0.5.15", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 3795e780182a69155a0312377b7ee52d4d66d9ca Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 28 Jun 2013 21:09:47 -0400 Subject: [PATCH 268/511] Try to fix windows unicode support by using SQL_C_WCHAR Also have to create the return string in a special way with some type casting --- src/odbc.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index 52fe8613..a4b18fe5 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -510,7 +510,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, ret = SQLGetData( hStmt, column.index, +#ifdef _WIN32 + SQL_C_WCHAR, +#else SQL_C_CHAR, +#endif (char *) buffer, bufferLength, &len); @@ -524,7 +528,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } else { //return scope.Close(String::New((char*) buffer)); +#ifdef _WIN32 + return String::New((uint16_t*) buffer, wcslen((wchar_t*) buffer)); +#else return String::New((char*) buffer); +#endif } } } From 6ef87a50283e402a0c11bd98915f11cd999a41fc Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 28 Jun 2013 21:10:03 -0400 Subject: [PATCH 269/511] 0.5.16 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ff65c554..35e61e06 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.15", + "version": "0.5.16", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From de5c4290a5a2bc51bd64734d51f1ac5a1008e333 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sat, 29 Jun 2013 01:41:58 -0400 Subject: [PATCH 270/511] Unicode progress; all tests work now except unicode. DOH. --- src/odbc.cpp | 8 +++---- src/odbc.h | 4 ++++ src/odbc_connection.cpp | 50 ++++++++++++++++++++--------------------- src/odbc_connection.h | 3 ++- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index a4b18fe5..f09a9345 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -599,16 +599,16 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { i, ¶ms[i].length); if (value->IsString()) { - String::Utf8Value string(value); + String::Value string(value); - params[i].c_type = SQL_C_CHAR; + params[i].c_type = SQL_C_WCHAR; params[i].type = SQL_VARCHAR; - params[i].buffer_length = string.length() + 1; + params[i].buffer_length = (string.length() * sizeof(uint16_t)) + sizeof(uint16_t); params[i].buffer = malloc(params[i].buffer_length); params[i].size = params[i].buffer_length; params[i].length = SQL_NTS;//params[i].buffer_length; - strcpy((char*)params[i].buffer, *string); + memcpy(params[i].buffer, *string, params[i].buffer_length); DEBUG_PRINTF("ODBC::GetParametersFromArray - IsString(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i " diff --git a/src/odbc.h b/src/odbc.h index af5efb4b..6d723da6 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -20,6 +20,7 @@ #include #include +#include #include #ifdef dynodbc @@ -28,6 +29,7 @@ #include #include #include +#include #endif using namespace v8; @@ -41,6 +43,8 @@ using namespace node; #define FETCH_ARRAY 3 #define FETCH_OBJECT 4 #define SQL_DESTROY 9999 +#define UNICODE + typedef struct { unsigned char *name; diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index d775fb01..317c7ed0 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -613,7 +613,7 @@ Handle ODBCConnection::Query(const Arguments& args) { Local cb; - String::Utf8Value* sql; + String::Value* sql; ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -641,7 +641,7 @@ Handle ODBCConnection::Query(const Arguments& args) { )); } - sql = new String::Utf8Value(args[0]->ToString()); + sql = new String::Value(args[0]->ToString()); data->params = ODBC::GetParametersFromArray( Local::Cast(args[1]), @@ -663,7 +663,7 @@ Handle ODBCConnection::Query(const Arguments& args) { if (args[0]->IsString()) { //handle Query("sql", function cb () {}) - sql = new String::Utf8Value(args[0]->ToString()); + sql = new String::Value(args[0]->ToString()); data->paramCount = 0; } @@ -676,10 +676,10 @@ Handle ODBCConnection::Query(const Arguments& args) { Local obj = args[0]->ToObject(); if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { - sql = new String::Utf8Value(obj->Get(OPTION_SQL)->ToString()); + sql = new String::Value(obj->Get(OPTION_SQL)->ToString()); } else { - sql = new String::Utf8Value(String::New("")); + sql = new String::Value(String::New("")); } if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { @@ -710,12 +710,12 @@ Handle ODBCConnection::Query(const Arguments& args) { ); } //Done checking arguments - - data->sql = (char *) malloc(sql->length() +1); + data->sqlLen = sql->length(); + data->sql = (uint16_t *) malloc((data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t)); data->cb = Persistent::New(cb); - - strcpy(data->sql, **sql); - + + memcpy(data->sql, **sql, (data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t)); + delete sql; data->conn = conn; @@ -752,17 +752,17 @@ void ODBCConnection::UV_Query(uv_work_t* req) { //check to see if should excute a direct or a parameter bound query if (!data->paramCount) { // execute the query directly - ret = SQLExecDirect( + ret = SQLExecDirectW( data->hSTMT, - (SQLCHAR *) data->sql, - strlen(data->sql)); + (SQLWCHAR *) data->sql, + data->sqlLen); } else { // prepare statement, bind parameters and execute statement - ret = SQLPrepare( + ret = SQLPrepareW( data->hSTMT, - (SQLCHAR *) data->sql, - strlen(data->sql)); + (SQLWCHAR *) data->sql, + data->sqlLen); if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { for (int i = 0; i < data->paramCount; i++) { @@ -864,7 +864,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { for (int i = 0; i < data->paramCount; i++) { if (prm = data->params[i], prm.buffer != NULL) { switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_WCHAR: free(prm.buffer); break; case SQL_C_LONG: delete (int64_t *)prm.buffer; break; case SQL_C_DOUBLE: delete (double *)prm.buffer; break; case SQL_C_BIT: delete (bool *)prm.buffer; break; @@ -897,7 +897,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { HandleScope scope; - String::Utf8Value* sql; + String::Value* sql; ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -923,7 +923,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { )); } - sql = new String::Utf8Value(args[0]->ToString()); + sql = new String::Value(args[0]->ToString()); params = ODBC::GetParametersFromArray( Local::Cast(args[1]), @@ -935,7 +935,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { if (args[0]->IsString()) { //handle Query("sql") - sql = new String::Utf8Value(args[0]->ToString()); + sql = new String::Value(args[0]->ToString()); paramCount = 0; } @@ -948,10 +948,10 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { Local obj = args[0]->ToObject(); if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { - sql = new String::Utf8Value(obj->Get(OPTION_SQL)->ToString()); + sql = new String::Value(obj->Get(OPTION_SQL)->ToString()); } else { - sql = new String::Utf8Value(String::New("")); + sql = new String::Value(String::New("")); } if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { @@ -997,9 +997,9 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { } else if (!paramCount) { // execute the query directly - ret = SQLExecDirect( + ret = SQLExecDirectW( hSTMT, - (SQLCHAR *) **sql, + (SQLWCHAR *) **sql, sql->length()); } else { @@ -1043,7 +1043,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { for (int i = 0; i < paramCount; i++) { if (prm = params[i], prm.buffer != NULL) { switch (prm.c_type) { - case SQL_C_CHAR: free(prm.buffer); break; + case SQL_C_WCHAR: free(prm.buffer); break; case SQL_C_LONG: delete (int64_t *)prm.buffer; break; case SQL_C_DOUBLE: delete (double *)prm.buffer; break; case SQL_C_BIT: delete (bool *)prm.buffer; break; diff --git a/src/odbc_connection.h b/src/odbc_connection.h index b6dc455c..f174412e 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -116,13 +116,14 @@ struct query_work_data { int paramCount; bool noResultObject; - char *sql; + uint16_t *sql; char *catalog; char *schema; char *table; char *type; char *column; + int sqlLen; int result; }; From 09f7f4ea9d575442af77e8c5b65d5b6b11faf91c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sat, 29 Jun 2013 03:43:20 -0400 Subject: [PATCH 271/511] More Unicode updates --- src/odbc_connection.cpp | 10 ++++++---- src/odbc_connection.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 317c7ed0..c3b6b5d3 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -711,10 +711,12 @@ Handle ODBCConnection::Query(const Arguments& args) { } //Done checking arguments data->sqlLen = sql->length(); - data->sql = (uint16_t *) malloc((data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t)); + data->sqlSize = (data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t); + + data->sql = (uint16_t *) malloc(data->sqlSize); data->cb = Persistent::New(cb); - memcpy(data->sql, **sql, (data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t)); + memcpy(data->sql, **sql, data->sqlSize); delete sql; @@ -754,14 +756,14 @@ void ODBCConnection::UV_Query(uv_work_t* req) { // execute the query directly ret = SQLExecDirectW( data->hSTMT, - (SQLWCHAR *) data->sql, + data->sql, data->sqlLen); } else { // prepare statement, bind parameters and execute statement ret = SQLPrepareW( data->hSTMT, - (SQLWCHAR *) data->sql, + data->sql, data->sqlLen); if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { diff --git a/src/odbc_connection.h b/src/odbc_connection.h index f174412e..b8cb259c 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -124,6 +124,8 @@ struct query_work_data { char *column; int sqlLen; + int sqlSize; + int result; }; From 9717e140a672d039d46eb424b20f103581e41a0c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 30 Jun 2013 14:40:40 -0400 Subject: [PATCH 272/511] Param set type to SQL_WVARHCAR, debug info --- src/odbc.cpp | 2 +- src/odbc_connection.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index f09a9345..6d2235ad 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -602,7 +602,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { String::Value string(value); params[i].c_type = SQL_C_WCHAR; - params[i].type = SQL_VARCHAR; + params[i].type = SQL_WVARCHAR; params[i].buffer_length = (string.length() * sizeof(uint16_t)) + sizeof(uint16_t); params[i].buffer = malloc(params[i].buffer_length); params[i].size = params[i].buffer_length; diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index c3b6b5d3..ef4ee1be 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -719,6 +719,8 @@ Handle ODBCConnection::Query(const Arguments& args) { memcpy(data->sql, **sql, data->sqlSize); delete sql; + + DEBUG_PRINTF("ODBCConnection::Query : sqlLen=%i, sqlSize=%i, sql=%s\n", data->sqlLen, data->sqlSize, (char*) data->sql); data->conn = conn; work_req->data = data; From e592c6ed14233c49770a76130eb90cbad5b602df Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 30 Jun 2013 15:04:01 -0400 Subject: [PATCH 273/511] Use Unicode column retrieval for linux also. --- src/odbc.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 6d2235ad..2980a99c 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -510,11 +510,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, ret = SQLGetData( hStmt, column.index, -#ifdef _WIN32 SQL_C_WCHAR, -#else - SQL_C_CHAR, -#endif (char *) buffer, bufferLength, &len); @@ -528,11 +524,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } else { //return scope.Close(String::New((char*) buffer)); -#ifdef _WIN32 - return String::New((uint16_t*) buffer, wcslen((wchar_t*) buffer)); -#else - return String::New((char*) buffer); -#endif + return String::New((uint16_t*) buffer, len / 2); } } } From cbe3f271d9879f13b52751c0bf41fd2c3d73b1e6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 30 Jun 2013 15:25:00 -0400 Subject: [PATCH 274/511] Cast to SQLWCHAR --- src/odbc_connection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index ef4ee1be..26b98548 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -758,14 +758,14 @@ void ODBCConnection::UV_Query(uv_work_t* req) { // execute the query directly ret = SQLExecDirectW( data->hSTMT, - data->sql, + (SQLWCHAR *) data->sql, data->sqlLen); } else { // prepare statement, bind parameters and execute statement ret = SQLPrepareW( data->hSTMT, - data->sql, + (SQLWCHAR *) data->sql, data->sqlLen); if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { From c1e9034bc90e39a5c4cda146eff76c2477bd49af Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 30 Jun 2013 23:17:03 -0400 Subject: [PATCH 275/511] Use Local and Write instead of memcopy --- src/odbc_connection.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 26b98548..9c1d3f9a 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -613,7 +613,7 @@ Handle ODBCConnection::Query(const Arguments& args) { Local cb; - String::Value* sql; + Local sql; ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -641,7 +641,7 @@ Handle ODBCConnection::Query(const Arguments& args) { )); } - sql = new String::Value(args[0]->ToString()); + sql = args[0]->ToString(); data->params = ODBC::GetParametersFromArray( Local::Cast(args[1]), @@ -663,7 +663,7 @@ Handle ODBCConnection::Query(const Arguments& args) { if (args[0]->IsString()) { //handle Query("sql", function cb () {}) - sql = new String::Value(args[0]->ToString()); + sql = args[0]->ToString(); data->paramCount = 0; } @@ -676,10 +676,10 @@ Handle ODBCConnection::Query(const Arguments& args) { Local obj = args[0]->ToObject(); if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { - sql = new String::Value(obj->Get(OPTION_SQL)->ToString()); + sql = obj->Get(OPTION_SQL)->ToString(); } else { - sql = new String::Value(String::New("")); + sql = String::New(""); } if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { @@ -710,17 +710,19 @@ Handle ODBCConnection::Query(const Arguments& args) { ); } //Done checking arguments - data->sqlLen = sql->length(); + data->sqlLen = sql->Length(); data->sqlSize = (data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t); data->sql = (uint16_t *) malloc(data->sqlSize); data->cb = Persistent::New(cb); - memcpy(data->sql, **sql, data->sqlSize); + sql->Write(data->sql); + //memcpy(data->sql, **sql, data->sqlSize); - delete sql; +// delete sql; - DEBUG_PRINTF("ODBCConnection::Query : sqlLen=%i, sqlSize=%i, sql=%s\n", data->sqlLen, data->sqlSize, (char*) data->sql); + DEBUG_PRINTF("ODBCConnection::Query : sqlLen=%i, sqlSize=%i, sql=%s\n", + data->sqlLen, data->sqlSize, (char*) data->sql); data->conn = conn; work_req->data = data; @@ -1008,9 +1010,9 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { } else { // prepare statement, bind parameters and execute statement - ret = SQLPrepare( + ret = SQLPrepareW( hSTMT, - (SQLCHAR *) **sql, + (SQLWCHAR *) **sql, sql->length()); if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { @@ -1058,6 +1060,8 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { free(params); } + delete sql; + //check to see if there was an error during execution if(ret == SQL_ERROR) { ThrowException(ODBC::GetSQLError( From 1c75b7421c452d9bbc7b248f504ffaf969645d7b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 30 Jun 2013 23:17:44 -0400 Subject: [PATCH 276/511] New and updated unicode tests --- test/test-param-select-with-unicode.js | 15 +++++++++++++++ test/test-query-select-unicode.js | 2 +- test/test-querySync-select-unicode.js | 20 ++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 test/test-param-select-with-unicode.js create mode 100644 test/test-querySync-select-unicode.js diff --git a/test/test-param-select-with-unicode.js b/test/test-param-select-with-unicode.js new file mode 100644 index 00000000..c2e5b8d5 --- /dev/null +++ b/test/test-param-select-with-unicode.js @@ -0,0 +1,15 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + db.query("select ? as UNICODETEXT", ['ף צ ץ ק ר ש תכ ך ל מ ם נ ן ס ע פ 電电電買买買開开開東东東車车車'], function (err, data) { + db.close(function () { + console.log(data); + assert.equal(err, null); + assert.deepEqual(data, [{ UNICODETEXT: 'ף צ ץ ק ר ש תכ ך ל מ ם נ ן ס ע פ 電电電買买買開开開東东東車车車' }]); + }); + }); +}); diff --git a/test/test-query-select-unicode.js b/test/test-query-select-unicode.js index 91cb3bd4..cd55774c 100644 --- a/test/test-query-select-unicode.js +++ b/test/test-query-select-unicode.js @@ -5,7 +5,7 @@ var common = require("./common") ; db.open(common.connectionString, function(err) { - db.query("select 'ף צ ץ ק ר ש תכ ך ל מ ם נ ן ס ע פ 電电電買买買開开開東东東車车車' as UNICODETEXT", function (err, data) { + db.query("select '車' as UNICODETEXT", function (err, data) { db.close(function () { console.log(data); assert.equal(err, null); diff --git a/test/test-querySync-select-unicode.js b/test/test-querySync-select-unicode.js new file mode 100644 index 00000000..09a9dad8 --- /dev/null +++ b/test/test-querySync-select-unicode.js @@ -0,0 +1,20 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err) { + var data; + try { + data = db.querySync("select 'ꜨꜢ' as UNICODETEXT"); + } + catch (e) { + console.log(e); + } + + db.close(function () { + console.log(data); + assert.deepEqual(data, [{ UNICODETEXT: 'ꜨꜢ' }]); + }); +}); From 2c9c5adec8baf1b9272badf23b9de89d50b11ae1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 1 Jul 2013 12:23:24 -0400 Subject: [PATCH 277/511] Modify GetParametersFromArray to use string->Write instead of memcpy --- src/odbc.cpp | 6 +++--- src/odbc_connection.cpp | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 2980a99c..272d291b 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -591,16 +591,16 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { i, ¶ms[i].length); if (value->IsString()) { - String::Value string(value); + Local string = value->ToString(); params[i].c_type = SQL_C_WCHAR; params[i].type = SQL_WVARCHAR; - params[i].buffer_length = (string.length() * sizeof(uint16_t)) + sizeof(uint16_t); + params[i].buffer_length = (string->Length() * sizeof(uint16_t)) + sizeof(uint16_t); params[i].buffer = malloc(params[i].buffer_length); params[i].size = params[i].buffer_length; params[i].length = SQL_NTS;//params[i].buffer_length; - memcpy(params[i].buffer, *string, params[i].buffer_length); + string->Write((uint16_t *) params[i].buffer); DEBUG_PRINTF("ODBC::GetParametersFromArray - IsString(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i " diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 9c1d3f9a..1a7b68dd 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -710,6 +710,7 @@ Handle ODBCConnection::Query(const Arguments& args) { ); } //Done checking arguments + data->sqlLen = sql->Length(); data->sqlSize = (data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t); @@ -717,9 +718,6 @@ Handle ODBCConnection::Query(const Arguments& args) { data->cb = Persistent::New(cb); sql->Write(data->sql); - //memcpy(data->sql, **sql, data->sqlSize); - -// delete sql; DEBUG_PRINTF("ODBCConnection::Query : sqlLen=%i, sqlSize=%i, sql=%s\n", data->sqlLen, data->sqlSize, (char*) data->sql); From 631c2eb1e72da7d3b6e23d06d82d3f11543f82ad Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 1 Jul 2013 14:18:11 -0400 Subject: [PATCH 278/511] Use sql as void* and cast where necessary --- src/odbc_connection.cpp | 2 +- src/odbc_connection.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 1a7b68dd..7f5939ab 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -717,7 +717,7 @@ Handle ODBCConnection::Query(const Arguments& args) { data->sql = (uint16_t *) malloc(data->sqlSize); data->cb = Persistent::New(cb); - sql->Write(data->sql); + sql->Write((uint16_t *) data->sql); DEBUG_PRINTF("ODBCConnection::Query : sqlLen=%i, sqlSize=%i, sql=%s\n", data->sqlLen, data->sqlSize, (char*) data->sql); diff --git a/src/odbc_connection.h b/src/odbc_connection.h index b8cb259c..5b104872 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -116,7 +116,7 @@ struct query_work_data { int paramCount; bool noResultObject; - uint16_t *sql; + void *sql; char *catalog; char *schema; char *table; From 1db4356b48550435b93cf582d01098dc7f51c87d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 1 Jul 2013 14:45:24 -0400 Subject: [PATCH 279/511] Update characters included in the unicode test --- test/test-query-select-unicode.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-query-select-unicode.js b/test/test-query-select-unicode.js index cd55774c..afb2522d 100644 --- a/test/test-query-select-unicode.js +++ b/test/test-query-select-unicode.js @@ -5,11 +5,11 @@ var common = require("./common") ; db.open(common.connectionString, function(err) { - db.query("select '車' as UNICODETEXT", function (err, data) { + db.query("select '☯ąčęėįšųūž☎áäàéêèóöòüßÄÖÜ€ шчябы Ⅲ ❤' as UNICODETEXT", function (err, data) { db.close(function () { console.log(data); assert.equal(err, null); - assert.deepEqual(data, [{ UNICODETEXT: 'ף צ ץ ק ר ש תכ ך ל מ ם נ ן ס ע פ 電电電買买買開开開東东東車车車' }]); + assert.deepEqual(data, [{ UNICODETEXT: '☯ąčęėįšųūž☎áäàéêèóöòüßÄÖÜ€ шчябы Ⅲ ❤' }]); }); }); }); From b3ce1fe826f47db4ec87ed447022c04d28a66707 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Jul 2013 10:54:55 -0400 Subject: [PATCH 280/511] 0.5.17 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 35e61e06..ad004f1d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.16", + "version": "0.5.17", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 0d6103613e82b0500be3d1dd4fec2b624beaee40 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Jul 2013 11:57:00 -0400 Subject: [PATCH 281/511] Add more REQ_STR for non-Utf8Value --- src/odbc.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/odbc.h b/src/odbc.h index 6d723da6..97f5902f 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -157,18 +157,48 @@ struct query_request { return ThrowException(Exception::TypeError( \ String::New("Expected " #N "arguments"))); +//Require String Argument; Save String as Utf8 #define REQ_STR_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsString()) \ return ThrowException(Exception::TypeError( \ String::New("Argument " #I " must be a string"))); \ String::Utf8Value VAR(args[I]->ToString()); +//Require String Argument; Save String as Wide String (UCS2) +#define REQ_WSTR_ARG(I, VAR) \ + if (args.Length() <= (I) || !args[I]->IsString()) \ + return ThrowException(Exception::TypeError( \ + String::New("Argument " #I " must be a string"))); \ + String::Value VAR(args[I]->ToString()); + +//Require String Argument; Save String as Object +#define REQ_STRO_ARG(I, VAR) \ + if (args.Length() <= (I) || !args[I]->IsString()) \ + return ThrowException(Exception::TypeError( \ + String::New("Argument " #I " must be a string"))); \ + Local VAR(args[I]->ToString()); + +//Require String or Null Argument; Save String as Utf8 #define REQ_STR_OR_NULL_ARG(I, VAR) \ if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ return ThrowException(Exception::TypeError( \ String::New("Argument " #I " must be a string or null"))); \ String::Utf8Value VAR(args[I]->ToString()); +//Require String or Null Argument; Save String as Wide String (UCS2) +#define REQ_WSTR_OR_NULL_ARG(I, VAR) \ + if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ + return ThrowException(Exception::TypeError( \ + String::New("Argument " #I " must be a string or null"))); \ + String::Value VAR(args[I]->ToString()); + +//Require String or Null Argument; save String as String Object +#define REQ_STRO_OR_NULL_ARG(I, VAR) \ + if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ + return ThrowException(Exception::TypeError( \ + String::New("Argument " #I " must be a string or null"))); \ + Local VAR(args[I]->ToString()); + #define REQ_FUN_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsFunction()) \ return ThrowException(Exception::TypeError( \ From d3aa4b25086b11ffc668e718e21545496e059d7e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Jul 2013 11:57:36 -0400 Subject: [PATCH 282/511] Enable Unicode connection strings --- src/odbc_connection.cpp | 24 +++++++++++++----------- src/odbc_connection.h | 3 ++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 7f5939ab..a9f0ae45 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -134,7 +134,7 @@ Handle ODBCConnection::Open(const Arguments& args) { DEBUG_PRINTF("ODBCConnection::Open\n"); HandleScope scope; - REQ_STR_ARG(0, connection); + REQ_STRO_ARG(0, connection); REQ_FUN_ARG(1, cb); //get reference to the connection object @@ -145,10 +145,12 @@ Handle ODBCConnection::Open(const Arguments& args) { //allocate our worker data open_connection_work_data* data = (open_connection_work_data *) - calloc(1, sizeof(open_connection_work_data) + connection.length()); + calloc(1, sizeof(open_connection_work_data) + (connection->Length() * sizeof(uint16_t))); + + data->connectionLength = connection->Length(); //copy the connection string to the work data - strcpy(data->connection, *connection); + connection->Write((uint16_t*) data->connection); data->cb = Persistent::New(cb); data->conn = conn; @@ -187,12 +189,12 @@ void ODBCConnection::UV_Open(uv_work_t* req) { //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked - int ret = SQLDriverConnect( + int ret = SQLDriverConnectW( self->m_hDBC, NULL, - (SQLCHAR*) data->connection, - strlen(data->connection), - (SQLCHAR*) connstr, + (SQLWCHAR*) data->connection, + data->connectionLength, + (SQLWCHAR*) connstr, 1024, NULL, SQL_DRIVER_NOPROMPT); @@ -276,7 +278,7 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { DEBUG_PRINTF("ODBCConnection::OpenSync\n"); HandleScope scope; - REQ_STR_ARG(0, connection); + REQ_WSTR_ARG(0, connection); //get reference to the connection object ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -300,12 +302,12 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked - ret = SQLDriverConnect( + ret = SQLDriverConnectW( conn->m_hDBC, NULL, - (SQLCHAR*) *connection, + (SQLWCHAR*) *connection, connection.length(), - (SQLCHAR*) connstr, + (SQLWCHAR*) connstr, 1024, NULL, SQL_DRIVER_NOPROMPT); diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 5b104872..37406565 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -133,7 +133,8 @@ struct open_connection_work_data { Persistent cb; ODBCConnection *conn; int result; - char connection[1]; + int connectionLength; + uint16_t connection[1]; }; struct close_connection_work_data { From 0081275f0f26d4c0d5919b6b6e5a6d04ff8a91be Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 2 Jul 2013 22:54:45 -0400 Subject: [PATCH 283/511] unicode: start properly handling Unicode based on defined UNICODE flag --- src/odbc.cpp | 22 +++++++-- src/odbc.h | 1 - src/odbc_connection.cpp | 100 ++++++++++++++++++++++++++++------------ src/odbc_connection.h | 2 +- 4 files changed, 89 insertions(+), 36 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 272d291b..216fbac7 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -510,7 +510,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, ret = SQLGetData( hStmt, column.index, - SQL_C_WCHAR, + SQL_C_TCHAR, (char *) buffer, bufferLength, &len); @@ -524,7 +524,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } else { //return scope.Close(String::New((char*) buffer)); +#ifdef UNICODE return String::New((uint16_t*) buffer, len / 2); +#else + return String::New((char *) buffer, len); +#endif } } } @@ -593,14 +597,22 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { if (value->IsString()) { Local string = value->ToString(); - params[i].c_type = SQL_C_WCHAR; - params[i].type = SQL_WVARCHAR; + params[i].c_type = SQL_C_TCHAR; + params[i].type = SQL_VARCHAR; +#ifdef UNICODE params[i].buffer_length = (string->Length() * sizeof(uint16_t)) + sizeof(uint16_t); +#else + params[i].buffer_length = string->Utf8Length() + 1; +#endif params[i].buffer = malloc(params[i].buffer_length); params[i].size = params[i].buffer_length; params[i].length = SQL_NTS;//params[i].buffer_length; +#ifdef UNICODE string->Write((uint16_t *) params[i].buffer); +#else + string->WriteUtf8((char *) params[i].buffer); +#endif DEBUG_PRINTF("ODBC::GetParametersFromArray - IsString(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i " @@ -744,9 +756,9 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* handleType, handle, i + 1, - (SQLCHAR *) errorSQLState, + (SQLTCHAR *) errorSQLState, &native, - (SQLCHAR *) errorMessage, + (SQLTCHAR *) errorMessage, sizeof(errorMessage), &len); diff --git a/src/odbc.h b/src/odbc.h index 97f5902f..268074ba 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -43,7 +43,6 @@ using namespace node; #define FETCH_ARRAY 3 #define FETCH_OBJECT 4 #define SQL_DESTROY 9999 -#define UNICODE typedef struct { diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index a9f0ae45..b84c6c2e 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -144,13 +144,23 @@ Handle ODBCConnection::Open(const Arguments& args) { uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); //allocate our worker data +#ifdef UNICODE open_connection_work_data* data = (open_connection_work_data *) calloc(1, sizeof(open_connection_work_data) + (connection->Length() * sizeof(uint16_t))); +#else + open_connection_work_data* data = (open_connection_work_data *) + calloc(1, sizeof(open_connection_work_data) + connection->Length()); +#endif data->connectionLength = connection->Length(); //copy the connection string to the work data +#ifdef UNICODE connection->Write((uint16_t*) data->connection); +#else + connection->WriteUtf8((char*) data->connection); +#endif + data->cb = Persistent::New(cb); data->conn = conn; @@ -189,16 +199,17 @@ void ODBCConnection::UV_Open(uv_work_t* req) { //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked - int ret = SQLDriverConnectW( + + int ret = SQLDriverConnect( self->m_hDBC, NULL, - (SQLWCHAR*) data->connection, + (SQLTCHAR*) data->connection, data->connectionLength, - (SQLWCHAR*) connstr, + (SQLTCHAR*) connstr, 1024, NULL, SQL_DRIVER_NOPROMPT); - + if (SQL_SUCCEEDED(ret)) { HSTMT hStmt; @@ -278,7 +289,11 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { DEBUG_PRINTF("ODBCConnection::OpenSync\n"); HandleScope scope; +#ifdef UNICODE REQ_WSTR_ARG(0, connection); +#else + REQ_STR_ARG(0, connection); +#endif //get reference to the connection object ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -302,12 +317,12 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked - ret = SQLDriverConnectW( + ret = SQLDriverConnect( conn->m_hDBC, NULL, - (SQLWCHAR*) *connection, + (SQLTCHAR*) *connection, connection.length(), - (SQLWCHAR*) connstr, + (SQLTCHAR*) connstr, 1024, NULL, SQL_DRIVER_NOPROMPT); @@ -713,13 +728,18 @@ Handle ODBCConnection::Query(const Arguments& args) { } //Done checking arguments + data->cb = Persistent::New(cb); data->sqlLen = sql->Length(); - data->sqlSize = (data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t); +#ifdef UNICODE + data->sqlSize = (data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t); data->sql = (uint16_t *) malloc(data->sqlSize); - data->cb = Persistent::New(cb); - sql->Write((uint16_t *) data->sql); +#else + data->sqlSize = sql->Utf8Length() + 1; + data->sql = (char *) malloc(data->sqlSize); + sql->WriteUtf8((char *) data->sql); +#endif DEBUG_PRINTF("ODBCConnection::Query : sqlLen=%i, sqlSize=%i, sql=%s\n", data->sqlLen, data->sqlSize, (char*) data->sql); @@ -758,16 +778,16 @@ void ODBCConnection::UV_Query(uv_work_t* req) { //check to see if should excute a direct or a parameter bound query if (!data->paramCount) { // execute the query directly - ret = SQLExecDirectW( + ret = SQLExecDirect( data->hSTMT, - (SQLWCHAR *) data->sql, + (SQLTCHAR *) data->sql, data->sqlLen); } else { // prepare statement, bind parameters and execute statement - ret = SQLPrepareW( + ret = SQLPrepare( data->hSTMT, - (SQLWCHAR *) data->sql, + (SQLTCHAR *) data->sql, data->sqlLen); if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { @@ -871,6 +891,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { if (prm = data->params[i], prm.buffer != NULL) { switch (prm.c_type) { case SQL_C_WCHAR: free(prm.buffer); break; + case SQL_C_CHAR: free(prm.buffer); break; case SQL_C_LONG: delete (int64_t *)prm.buffer; break; case SQL_C_DOUBLE: delete (double *)prm.buffer; break; case SQL_C_BIT: delete (bool *)prm.buffer; break; @@ -903,7 +924,11 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { HandleScope scope; +#ifdef UNICODE String::Value* sql; +#else + String::Utf8Value* sql; +#endif ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -929,7 +954,11 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { )); } +#ifdef UNICODE sql = new String::Value(args[0]->ToString()); +#else + sql = new String::Utf8Value(args[0]->ToString()); +#endif params = ODBC::GetParametersFromArray( Local::Cast(args[1]), @@ -941,8 +970,12 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { if (args[0]->IsString()) { //handle Query("sql") +#ifdef UNICODE sql = new String::Value(args[0]->ToString()); - +#else + sql = new String::Utf8Value(args[0]->ToString()); +#endif + paramCount = 0; } else if (args[0]->IsObject()) { @@ -954,10 +987,18 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { Local obj = args[0]->ToObject(); if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { +#ifdef UNICODE sql = new String::Value(obj->Get(OPTION_SQL)->ToString()); +#else + sql = new String::Utf8Value(obj->Get(OPTION_SQL)->ToString()); +#endif } else { +#ifdef UNICODE sql = new String::Value(String::New("")); +#else + sql = new String::Utf8Value(String::New("")); +#endif } if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { @@ -1003,16 +1044,16 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { } else if (!paramCount) { // execute the query directly - ret = SQLExecDirectW( + ret = SQLExecDirect( hSTMT, - (SQLWCHAR *) **sql, + (SQLTCHAR *) **sql, sql->length()); } else { - // prepare statement, bind parameters and execute statement - ret = SQLPrepareW( + // prepare statement, bind parameters and execute statement + ret = SQLPrepare( hSTMT, - (SQLWCHAR *) **sql, + (SQLTCHAR *) **sql, sql->length()); if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { @@ -1049,7 +1090,8 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { for (int i = 0; i < paramCount; i++) { if (prm = params[i], prm.buffer != NULL) { switch (prm.c_type) { - case SQL_C_WCHAR: free(prm.buffer); break; + case SQL_C_WCHAR: free(prm.buffer); break; + case SQL_C_CHAR: free(prm.buffer); break; case SQL_C_LONG: delete (int64_t *)prm.buffer; break; case SQL_C_DOUBLE: delete (double *)prm.buffer; break; case SQL_C_BIT: delete (bool *)prm.buffer; break; @@ -1177,10 +1219,10 @@ void ODBCConnection::UV_Tables(uv_work_t* req) { SQLRETURN ret = SQLTables( data->hSTMT, - (SQLCHAR *) data->catalog, SQL_NTS, - (SQLCHAR *) data->schema, SQL_NTS, - (SQLCHAR *) data->table, SQL_NTS, - (SQLCHAR *) data->type, SQL_NTS + (SQLTCHAR *) data->catalog, SQL_NTS, + (SQLTCHAR *) data->schema, SQL_NTS, + (SQLTCHAR *) data->table, SQL_NTS, + (SQLTCHAR *) data->type, SQL_NTS ); // this will be checked later in UV_AfterQuery @@ -1267,10 +1309,10 @@ void ODBCConnection::UV_Columns(uv_work_t* req) { SQLRETURN ret = SQLColumns( data->hSTMT, - (SQLCHAR *) data->catalog, SQL_NTS, - (SQLCHAR *) data->schema, SQL_NTS, - (SQLCHAR *) data->table, SQL_NTS, - (SQLCHAR *) data->column, SQL_NTS + (SQLTCHAR *) data->catalog, SQL_NTS, + (SQLTCHAR *) data->schema, SQL_NTS, + (SQLTCHAR *) data->table, SQL_NTS, + (SQLTCHAR *) data->column, SQL_NTS ); // this will be checked later in UV_AfterQuery diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 37406565..d01256bd 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -134,7 +134,7 @@ struct open_connection_work_data { ODBCConnection *conn; int result; int connectionLength; - uint16_t connection[1]; + void* connection[1]; }; struct close_connection_work_data { From f6a5bbee221aa609244bde1f6a9da7b3be11424a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Jul 2013 11:05:51 -0400 Subject: [PATCH 284/511] unicode: finish up proper handling of Unicode in all classes --- src/odbc.cpp | 14 ++- src/odbc_connection.cpp | 192 ++++++++++++++++++++++++++-------------- src/odbc_connection.h | 12 +-- src/odbc_statement.cpp | 66 +++++++++----- src/odbc_statement.h | 6 +- test/test-openSync.js | 29 ++++++ 6 files changed, 221 insertions(+), 98 deletions(-) create mode 100644 test/test-openSync.js diff --git a/src/odbc.cpp b/src/odbc.cpp index 216fbac7..c5b501ef 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -545,8 +545,13 @@ Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, Local tuple = Object::New(); for(int i = 0; i < *colCount; i++) { +#ifdef UNICODE + tuple->Set( String::New((uint16_t *) columns[i].name), + GetColumnValue( hStmt, columns[i], buffer, bufferLength)); +#else tuple->Set( String::New((const char *) columns[i].name), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); +#endif } //return tuple; @@ -737,8 +742,8 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* SQLSMALLINT len; SQLINTEGER numfields; SQLRETURN ret; - char errorSQLState[7]; - char errorMessage[256]; + char errorSQLState[14]; + char errorMessage[512]; SQLGetDiagField( handleType, @@ -768,8 +773,13 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* DEBUG_PRINTF("ODBC::GetSQLError : errorMessage=%s, errorSQLState=%s\n", errorMessage, errorSQLState); objError->Set(String::New("error"), String::New(message)); +#ifdef UNICODE + objError->Set(String::New("message"), String::New((uint16_t *) errorMessage)); + objError->Set(String::New("state"), String::New((uint16_t *) errorSQLState)); +#else objError->Set(String::New("message"), String::New(errorMessage)); objError->Set(String::New("state"), String::New(errorSQLState)); +#endif } } diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index b84c6c2e..691813c8 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -144,20 +144,17 @@ Handle ODBCConnection::Open(const Arguments& args) { uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); //allocate our worker data -#ifdef UNICODE - open_connection_work_data* data = (open_connection_work_data *) - calloc(1, sizeof(open_connection_work_data) + (connection->Length() * sizeof(uint16_t))); -#else open_connection_work_data* data = (open_connection_work_data *) - calloc(1, sizeof(open_connection_work_data) + connection->Length()); -#endif + calloc(1, sizeof(open_connection_work_data)); data->connectionLength = connection->Length(); - //copy the connection string to the work data + //copy the connection string to the work data #ifdef UNICODE + data->connection = (uint16_t *) malloc(sizeof(uint16_t) * data->connectionLength); connection->Write((uint16_t*) data->connection); #else + data->connection = (char *) malloc(sizeof(char) * data->connectionLength); connection->WriteUtf8((char*) data->connection); #endif @@ -185,7 +182,13 @@ void ODBCConnection::UV_Open(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); - char connstr[1024]; +#ifdef UNICODE + int connectionStringOutSize = 1024 * sizeof(uint16_t); + uint16_t* connectionStringOut = (uint16_t *) malloc(connectionStringOutSize); +#else + int connectionStringOutSize = 1024; + char* connectionStringOut = (char *) malloc(connectionStringOutSize); +#endif //TODO: make this configurable int timeOut = 5; @@ -199,17 +202,17 @@ void ODBCConnection::UV_Open(uv_work_t* req) { //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked - - int ret = SQLDriverConnect( - self->m_hDBC, - NULL, - (SQLTCHAR*) data->connection, - data->connectionLength, - (SQLTCHAR*) connstr, - 1024, - NULL, - SQL_DRIVER_NOPROMPT); - + int ret = SQLDriverConnect( + self->m_hDBC, //ConnectionHandle + NULL, //WindowHandle + (SQLTCHAR*) data->connection, //InConnectionString + data->connectionLength, //StringLength1 + (SQLTCHAR*) connectionStringOut,//OutConnectionString + 1024, //BufferLength - in characters + NULL, //StringLength2Ptr + SQL_DRIVER_NOPROMPT); //DriverCompletion + + if (SQL_SUCCEEDED(ret)) { HSTMT hStmt; @@ -276,6 +279,7 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { data->cb.Dispose(); + free(data->connection); free(data); free(req); scope.Close(Undefined()); @@ -289,11 +293,7 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { DEBUG_PRINTF("ODBCConnection::OpenSync\n"); HandleScope scope; -#ifdef UNICODE - REQ_WSTR_ARG(0, connection); -#else - REQ_STR_ARG(0, connection); -#endif + REQ_STRO_ARG(0, connection); //get reference to the connection object ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -301,7 +301,20 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { Local objError; SQLRETURN ret; bool err = false; - char connstr[1024]; + + int connectionLength = connection->Length() + 1; + +#ifdef UNICODE + int connectionStringOutSize = 1024 * sizeof(uint16_t); + uint16_t* connectionStringOut = (uint16_t *) malloc(connectionStringOutSize); + uint16_t* connectionString = (uint16_t *) malloc(connectionLength * sizeof(uint16_t)); + connection->Write(connectionString); +#else + int connectionStringOutSize = 1024; + char* connectionStringOut = (char *) malloc(connectionStringOutSize); + char* connectionString = (char *) malloc(connectionLength); + connection->WriteUtf8(connectionString); +#endif uv_mutex_lock(&ODBC::g_odbcMutex); @@ -317,15 +330,15 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked - ret = SQLDriverConnect( - conn->m_hDBC, - NULL, - (SQLTCHAR*) *connection, - connection.length(), - (SQLTCHAR*) connstr, - 1024, - NULL, - SQL_DRIVER_NOPROMPT); + ret = SQLDriverConnect( + conn->m_hDBC, //ConnectionHandle + NULL, //WindowHandle + (SQLTCHAR*) connectionString, //InConnectionString + connectionLength, //StringLength1 + (SQLTCHAR*) connectionStringOut,//OutConnectionString + 1024, //BufferLength - in characters + NULL, //StringLength2Ptr + SQL_DRIVER_NOPROMPT); //DriverCompletion if (!SQL_SUCCEEDED(ret)) { err = true; @@ -364,6 +377,9 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { uv_mutex_unlock(&ODBC::g_odbcMutex); + free(connectionString); + free(connectionStringOut); + if (err) { ThrowException(objError); return scope.Close(False()); @@ -1148,10 +1164,10 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { Handle ODBCConnection::Tables(const Arguments& args) { HandleScope scope; - REQ_STR_OR_NULL_ARG(0, catalog); - REQ_STR_OR_NULL_ARG(1, schema); - REQ_STR_OR_NULL_ARG(2, table); - REQ_STR_OR_NULL_ARG(3, type); + REQ_STRO_OR_NULL_ARG(0, catalog); + REQ_STRO_OR_NULL_ARG(1, schema); + REQ_STRO_OR_NULL_ARG(2, table); + REQ_STRO_OR_NULL_ARG(3, type); Local cb = Local::Cast(args[4]); ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -1174,24 +1190,44 @@ Handle ODBCConnection::Tables(const Arguments& args) { data->column = NULL; data->cb = Persistent::New(cb); - if (!String::New(*catalog)->Equals(String::New("null"))) { - data->catalog = (char *) malloc(catalog.length() +1); - strcpy(data->catalog, *catalog); + if (!catalog->Equals(String::New("null"))) { +#ifdef UNICODE + data->catalog = (uint16_t *) malloc((catalog->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); + catalog->Write((uint16_t *) data->catalog); +#else + data->catalog = (char *) malloc(catalog->Length() + 1); + catalog->WriteUtf8((char *) data->catalog); +#endif } - - if (!String::New(*schema)->Equals(String::New("null"))) { - data->schema = (char *) malloc(schema.length() +1); - strcpy(data->schema, *schema); + + if (!schema->Equals(String::New("null"))) { +#ifdef UNICODE + data->schema = (uint16_t *) malloc((schema->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); + schema->Write((uint16_t *) data->schema); +#else + data->schema = (char *) malloc(schema->Length() + 1); + schema->WriteUtf8((char *) data->schema); +#endif } - if (!String::New(*table)->Equals(String::New("null"))) { - data->table = (char *) malloc(table.length() +1); - strcpy(data->table, *table); + if (!table->Equals(String::New("null"))) { +#ifdef UNICODE + data->table = (uint16_t *) malloc((table->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); + table->Write((uint16_t *) data->table); +#else + data->table = (char *) malloc(table->Length() + 1); + table->WriteUtf8((char *) data->table); +#endif } - if (!String::New(*type)->Equals(String::New("null"))) { - data->type = (char *) malloc(type.length() +1); - strcpy(data->type, *type); + if (!type->Equals(String::New("null"))) { +#ifdef UNICODE + data->type = (uint16_t *) malloc((type->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); + type->Write((uint16_t *) data->type); +#else + data->type = (char *) malloc(type->Length() + 1); + type->WriteUtf8((char *) data->type); +#endif } data->conn = conn; @@ -1238,10 +1274,10 @@ void ODBCConnection::UV_Tables(uv_work_t* req) { Handle ODBCConnection::Columns(const Arguments& args) { HandleScope scope; - REQ_STR_OR_NULL_ARG(0, catalog); - REQ_STR_OR_NULL_ARG(1, schema); - REQ_STR_OR_NULL_ARG(2, table); - REQ_STR_OR_NULL_ARG(3, column); + REQ_STRO_OR_NULL_ARG(0, catalog); + REQ_STRO_OR_NULL_ARG(1, schema); + REQ_STRO_OR_NULL_ARG(2, table); + REQ_STRO_OR_NULL_ARG(3, column); Local cb = Local::Cast(args[4]); @@ -1264,24 +1300,44 @@ Handle ODBCConnection::Columns(const Arguments& args) { data->column = NULL; data->cb = Persistent::New(cb); - if (!String::New(*catalog)->Equals(String::New("null"))) { - data->catalog = (char *) malloc(catalog.length() +1); - strcpy(data->catalog, *catalog); + if (!catalog->Equals(String::New("null"))) { +#ifdef UNICODE + data->catalog = (uint16_t *) malloc((catalog->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); + catalog->Write((uint16_t *) data->catalog); +#else + data->catalog = (char *) malloc(catalog->Length() + 1); + catalog->WriteUtf8((char *) data->catalog); +#endif } - - if (!String::New(*schema)->Equals(String::New("null"))) { - data->schema = (char *) malloc(schema.length() +1); - strcpy(data->schema, *schema); + + if (!schema->Equals(String::New("null"))) { +#ifdef UNICODE + data->schema = (uint16_t *) malloc((schema->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); + schema->Write((uint16_t *) data->schema); +#else + data->schema = (char *) malloc(schema->Length() + 1); + schema->WriteUtf8((char *) data->schema); +#endif } - if (!String::New(*table)->Equals(String::New("null"))) { - data->table = (char *) malloc(table.length() +1); - strcpy(data->table, *table); + if (!table->Equals(String::New("null"))) { +#ifdef UNICODE + data->table = (uint16_t *) malloc((table->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); + table->Write((uint16_t *) data->table); +#else + data->table = (char *) malloc(table->Length() + 1); + table->WriteUtf8((char *) data->table); +#endif } - if (!String::New(*column)->Equals(String::New("null"))) { - data->column = (char *) malloc(column.length() +1); - strcpy(data->column, *column); + if (!column->Equals(String::New("null"))) { +#ifdef UNICODE + data->column = (uint16_t *) malloc((column->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); + column->Write((uint16_t *) data->column); +#else + data->column = (char *) malloc(column->Length() + 1); + column->WriteUtf8((char *) data->column); +#endif } data->conn = conn; diff --git a/src/odbc_connection.h b/src/odbc_connection.h index d01256bd..28cbf3fc 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -117,11 +117,11 @@ struct query_work_data { bool noResultObject; void *sql; - char *catalog; - char *schema; - char *table; - char *type; - char *column; + void *catalog; + void *schema; + void *table; + void *type; + void *column; int sqlLen; int sqlSize; @@ -134,7 +134,7 @@ struct open_connection_work_data { ODBCConnection *conn; int result; int connectionLength; - void* connection[1]; + void* connection; }; struct close_connection_work_data { diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index f0030d41..de400298 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -431,7 +431,7 @@ Handle ODBCStatement::ExecuteDirect(const Arguments& args) { HandleScope scope; - REQ_STR_ARG(0, sql); + REQ_STRO_ARG(0, sql); REQ_FUN_ARG(1, cb); ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); @@ -441,11 +441,18 @@ Handle ODBCStatement::ExecuteDirect(const Arguments& args) { execute_direct_work_data* data = (execute_direct_work_data *) calloc(1, sizeof(execute_direct_work_data)); - data->sql = (char *) malloc(sql.length() +1); data->cb = Persistent::New(cb); - - strcpy(data->sql, *sql); - + + data->sqlLen = sql->Length(); + +#ifdef UNICODE + data->sql = (uint16_t *) malloc((data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t)); + sql->Write((uint16_t *) data->sql); +#else + data->sql = (char *) malloc(data->sqlLen +1); + sql->WriteUtf8((char *) data->sql); +#endif + data->stmt = stmt; work_req->data = data; @@ -469,8 +476,8 @@ void ODBCStatement::UV_ExecuteDirect(uv_work_t* req) { ret = SQLExecDirect( data->stmt->m_hSTMT, - (SQLCHAR *) data->sql, - strlen(data->sql)); + (SQLTCHAR *) data->sql, + data->sqlLen); data->result = ret; } @@ -537,13 +544,17 @@ Handle ODBCStatement::ExecuteDirectSync(const Arguments& args) { HandleScope scope; +#ifdef UNICODE + REQ_WSTR_ARG(0, sql); +#else REQ_STR_ARG(0, sql); +#endif ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); SQLRETURN ret = SQLExecDirect( stmt->m_hSTMT, - (SQLCHAR *) *sql, + (SQLTCHAR *) *sql, sql.length()); if(ret == SQL_ERROR) { @@ -581,20 +592,28 @@ Handle ODBCStatement::PrepareSync(const Arguments& args) { HandleScope scope; - REQ_STR_ARG(0, sql); + REQ_STRO_ARG(0, sql); ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); SQLRETURN ret; + + int sqlLen = sql->Length() + 1; + +#ifdef UNICODE + uint16_t *sql2; + sql2 = (uint16_t *) malloc(sqlLen * sizeof(uint16_t)); + sql->Write(sql2); +#else char *sql2; - - sql2 = (char *) malloc(sql.length() +1); - strcpy(sql2, *sql); + sql2 = (char *) malloc(sqlLen); + sql->WriteUtf8(sql2); +#endif ret = SQLPrepare( stmt->m_hSTMT, - (SQLCHAR *) sql2, - strlen(sql2)); + (SQLTCHAR *) sql2, + sqlLen); if (SQL_SUCCEEDED(ret)) { return scope.Close(True()); @@ -615,7 +634,7 @@ Handle ODBCStatement::Prepare(const Arguments& args) { HandleScope scope; - REQ_STR_ARG(0, sql); + REQ_STRO_ARG(0, sql); REQ_FUN_ARG(1, cb); ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); @@ -625,10 +644,17 @@ Handle ODBCStatement::Prepare(const Arguments& args) { prepare_work_data* data = (prepare_work_data *) calloc(1, sizeof(prepare_work_data)); - data->sql = (char *) malloc(sql.length() +1); data->cb = Persistent::New(cb); - - strcpy(data->sql, *sql); + + data->sqlLen = sql->Length(); + +#ifdef UNICODE + data->sql = (uint16_t *) malloc((data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t)); + sql->Write((uint16_t *) data->sql); +#else + data->sql = (char *) malloc(data->sqlLen +1); + sql->WriteUtf8((char *) data->sql); +#endif data->stmt = stmt; @@ -660,8 +686,8 @@ void ODBCStatement::UV_Prepare(uv_work_t* req) { ret = SQLPrepare( data->stmt->m_hSTMT, - (SQLCHAR *) data->sql, - strlen(data->sql)); + (SQLTCHAR *) data->sql, + data->sqlLen); data->result = ret; } diff --git a/src/odbc_statement.h b/src/odbc_statement.h index c0269179..2900cb3a 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -93,7 +93,8 @@ struct execute_direct_work_data { Persistent cb; ODBCStatement *stmt; int result; - char *sql; + void *sql; + int sqlLen; }; struct execute_work_data { @@ -106,7 +107,8 @@ struct prepare_work_data { Persistent cb; ODBCStatement *stmt; int result; - char *sql; + void *sql; + int sqlLen; }; struct bind_work_data { diff --git a/test/test-openSync.js b/test/test-openSync.js new file mode 100644 index 00000000..4fabf4f8 --- /dev/null +++ b/test/test-openSync.js @@ -0,0 +1,29 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert"); + +assert.equal(db.connected, false); + +db.query("select * from " + common.tableName, function (err, rs, moreResultSets) { + assert.deepEqual(err, { message: 'Connection not open.' }); + assert.deepEqual(rs, []); + assert.equal(moreResultSets, false); + assert.equal(db.connected, false); +}); + +try { + db.openSync(common.connectionString); +} +catch(e) { + console.log(e); + assert.deepEqual(e, null); +} + +try { + db.closeSync(); +} +catch(e) { + console.log(e); + assert.deepEqual(e, null); +} From 778b2b8de7c0019618b1f0dfeef6d3b55b60a04a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Jul 2013 11:34:31 -0400 Subject: [PATCH 285/511] fix: use SQL_DESC_TYPE to get the column type --- src/odbc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index c5b501ef..cae16419 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -305,7 +305,7 @@ Column* ODBC::GetColumns(SQLHSTMT hStmt, short* colCount) { //get the column type and store it directly in column[i].type ret = SQLColAttribute( hStmt, columns[i].index, - SQL_COLUMN_TYPE, + SQL_DESC_TYPE, NULL, 0, NULL, From f8d9b8a5004c9a334acaaf5eb438e7b57a23cd54 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Jul 2013 11:40:01 -0400 Subject: [PATCH 286/511] fix: re-introduce connectionString objects --- lib/odbc.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/odbc.js b/lib/odbc.js index d841303b..f5260fd6 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -70,6 +70,15 @@ Object.keys(odbc.ODBC).forEach(function (key) { Database.prototype.open = function (connectionString, cb) { var self = this; + if (typeof(connectionString) == "object") { + var obj = connectionString; + connectionString = ""; + + Object.keys(obj).forEach(function (key) { + connectionString += key + "=" + obj[key] + ";"; + }); + } + self.odbc.createConnection(function (err, conn) { if (err) return cb(err); @@ -90,6 +99,15 @@ Database.prototype.openSync = function (connectionString) { self.conn = self.odbc.createConnectionSync(); + if (typeof(connectionString) == "object") { + var obj = connectionString; + connectionString = ""; + + Object.keys(obj).forEach(function (key) { + connectionString += key + "=" + obj[key] + ";"; + }); + } + var result = self.conn.openSync(connectionString); if (result) { From b1b3b99d1334d980c7dd0a91314e68d2e266b84e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Jul 2013 13:37:09 -0400 Subject: [PATCH 287/511] test: update test runner to test multiple drivers --- test/common.js | 39 +++++++++++---- test/config.benchConnectionStrings.json | 4 ++ test/config.testConnectionStrings.json | 5 ++ test/run-tests.js | 64 +++++++++++++++++++------ test/test-multi-open-close.js | 2 +- test/test-multi-openSync-closeSync.js | 14 +++++- 6 files changed, 102 insertions(+), 26 deletions(-) create mode 100644 test/config.benchConnectionStrings.json create mode 100644 test/config.testConnectionStrings.json diff --git a/test/common.js b/test/common.js index 88523a3c..18a60337 100644 --- a/test/common.js +++ b/test/common.js @@ -4,22 +4,43 @@ var odbc = require("../"); //odbc.library = '/opt/sqlncli-11.0.1790.0/lib64/libsqlncli-11.0'; exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; -//exports.connectionString = "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;"; -//exports.connectionString = process.env.ODBC_CONNETION_STRING; + +if (process.argv.length === 3) { + exports.connectionString = process.argv[2]; +} exports.connectionObject = { DRIVER : "{SQLITE3}", DATABASE : "data/sqlite-test.db" }; -exports.connections = [ - { - DRIVER : "{SQLITE3}", - DATABASE : "data/sqlite-test.db" - } -]; +try { + exports.testConnectionStrings = require('./config.testConnectionStrings.json'); +} +catch (e) { + exports.testConnectionStrings = [{ title : "Sqlite3", connectionString : exports.connectionString }]; +} + +try { + exports.benchConnectionStrings = require('./config.benchConnectionStrings.json'); +} +catch (e) { + exports.benchConnectionStrings = [{ title : "Sqlite3", connectionString : exports.connectionString }]; +} + +if (process.argv.length === 3) { + //look through the testConnectionStrings to see if there is a title that matches + //what was requested. + var lookup = process.argv[2]; + + exports.testConnectionStrings.forEach(function (connectionString) { + if (connectionString && connectionString.title && connectionString.title == lookup) { + exports.connectionString = connectionString.connectionString + } + }); +} -exports.databaseName = "MAIN"; +exports.databaseName = "test"; exports.tableName = "NODE_ODBC_TEST_TABLE"; exports.dropTables = function (db, cb) { diff --git a/test/config.benchConnectionStrings.json b/test/config.benchConnectionStrings.json new file mode 100644 index 00000000..9399cc17 --- /dev/null +++ b/test/config.benchConnectionStrings.json @@ -0,0 +1,4 @@ +[ + { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db" } + , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;" } +] diff --git a/test/config.testConnectionStrings.json b/test/config.testConnectionStrings.json new file mode 100644 index 00000000..96daacab --- /dev/null +++ b/test/config.testConnectionStrings.json @@ -0,0 +1,5 @@ +[ + { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db" } + , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;" } + , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test" } +] diff --git a/test/run-tests.js b/test/run-tests.js index 743f2a7a..9a596325 100644 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -1,8 +1,11 @@ var fs = require("fs") + , common = require('./common.js') , spawn = require("child_process").spawn , errorCount = 0 , testCount = 0 , testTimeout = 5000 + , requestedTest = null + , files ; var filesDisabled = fs.readdirSync("./disabled"); @@ -11,23 +14,32 @@ if (filesDisabled.length) { console.log("\n\033[01;31mWarning\033[01;0m : there are %s disabled tests\n", filesDisabled.length); } -var files = fs.readdirSync("./"); +if (process.argv.length === 3) { + requestedTest = process.argv[2]; +} + +var connectionStrings = common.testConnectionStrings; -files = files.filter(function (file) { - return (/^test-/.test(file)) ? true : false; -}); +//check to see if the requested test is actually a driver to test +if (requestedTest) { + connectionStrings.forEach(function (connectionString) { + if (requestedTest == connectionString.title) { + connectionStrings = [connectionString]; + requestedTest = null; + } + }); +} -files.sort(); +doNextConnectionString(); -doNextTest(); -function doTest(file) { - var test = spawn("node", ['--expose_gc',file]) +function doTest(file, connectionString) { + var test = spawn("node", ['--expose_gc',file, connectionString.connectionString]) , timer = null , timedOut = false; ; - process.stdout.write("Running test : " + file.replace(/\.js$/, "")); + process.stdout.write("Running test for [\033[01;29m" + connectionString.title + "\033[01;0m] : " + file.replace(/\.js$/, "")); process.stdout.write(" ... "); testCount += 1; @@ -50,7 +62,7 @@ function doTest(file) { process.stdout.write("\n"); - doNextTest(); + doNextTest(connectionString); }); var timer = setTimeout(function () { @@ -59,15 +71,39 @@ function doTest(file) { },testTimeout); } -function doNextTest() { +function doNextTest(connectionString) { if (files.length) { var testFile = files.shift(); - doTest(testFile); + doTest(testFile, connectionString); } else { - //we're done, display results and exit accordingly + //we're done with this connection string, display results and exit accordingly + doNextConnectionString(); + } +} + +function doNextConnectionString() { + if (connectionStrings.length) { + var connectionString = connectionStrings.shift(); + if (requestedTest) { + files = [requestedTest]; + } + else { + //re-read files + files = fs.readdirSync("./"); + + files = files.filter(function (file) { + return (/^test-/.test(file)) ? true : false; + }); + + files.sort(); + } + + doNextTest(connectionString); + } + else { if (errorCount) { console.log("\nResults : %s of %s tests failed.\n", errorCount, testCount); process.exit(errorCount); @@ -76,4 +112,4 @@ function doNextTest() { console.log("Results : All tests were successful."); } } -} +} \ No newline at end of file diff --git a/test/test-multi-open-close.js b/test/test-multi-open-close.js index 87a0f917..9d6097e9 100644 --- a/test/test-multi-open-close.js +++ b/test/test-multi-open-close.js @@ -2,7 +2,7 @@ var common = require("./common") , odbc = require("../") , openCallback = 0 , closeCallback = 0 - , openCount = 1000 + , openCount = 100 , connections = [] ; diff --git a/test/test-multi-openSync-closeSync.js b/test/test-multi-openSync-closeSync.js index 381c7573..3e89d5f0 100644 --- a/test/test-multi-openSync-closeSync.js +++ b/test/test-multi-openSync-closeSync.js @@ -2,15 +2,24 @@ var common = require("./common") , odbc = require("../") , openCallback = 0 , closeCallback = 0 - , openCount = 1000 + , openCount = 100 , connections = [] + , errorCount = 0; ; for (var x = 0; x < openCount; x++ ) { var db = new odbc.Database(); connections.push(db); - db.openSync(common.connectionString); + try { + db.openSync(common.connectionString); + } + catch (e) { + console.log(common.connectionString); + console.log(e); + errorCount += 1; + break; + } } connections.forEach(function (db) { @@ -18,3 +27,4 @@ connections.forEach(function (db) { }); console.log('Done'); +process.exit(errorCount); \ No newline at end of file From a7cf5cd94de67e80e203c1c626bcd2ebb71aa756 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Jul 2013 13:58:18 -0400 Subject: [PATCH 288/511] fix: specify SQL_WVARCHAR or SQL_VARCHAR for params depending on UNICODE --- src/odbc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index cae16419..19b8437a 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -603,10 +603,11 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { Local string = value->ToString(); params[i].c_type = SQL_C_TCHAR; - params[i].type = SQL_VARCHAR; #ifdef UNICODE + params[i].type = SQL_WVARCHAR; params[i].buffer_length = (string->Length() * sizeof(uint16_t)) + sizeof(uint16_t); #else + params[i].type = SQL_VARCHAR; params[i].buffer_length = string->Utf8Length() + 1; #endif params[i].buffer = malloc(params[i].buffer_length); From 3b2f476f6bd49368bd53820fa7489ab10876f0c0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Jul 2013 14:30:00 -0400 Subject: [PATCH 289/511] test: updated tests and new default for MSSQL Native Client connectionString --- binding.gyp | 2 +- test/config.testConnectionStrings.json | 3 ++- test/test-multi-open-query-close.js | 2 +- test/test-openSync.js | 2 ++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/binding.gyp b/binding.gyp index ad9fad26..4181ecdf 100644 --- a/binding.gyp +++ b/binding.gyp @@ -10,7 +10,7 @@ 'src/dynodbc.cpp' ], 'defines' : [ - + 'UNICODE' ], 'conditions' : [ [ 'OS == "linux"', { diff --git a/test/config.testConnectionStrings.json b/test/config.testConnectionStrings.json index 96daacab..437710d6 100644 --- a/test/config.testConnectionStrings.json +++ b/test/config.testConnectionStrings.json @@ -1,5 +1,6 @@ [ { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db" } , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;" } - , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test" } + , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test;AutoTranslate=yes" } + , { "title" : "MSSQL-NativeCLI-Remote", "connectionString" : "DRIVER={SQL Server Native Client 11.0};SERVER=sql2;DATABASE=test;UID=test;PWD=test;" } ] diff --git a/test/test-multi-open-query-close.js b/test/test-multi-open-query-close.js index 835bf505..766e8c3e 100644 --- a/test/test-multi-open-query-close.js +++ b/test/test-multi-open-query-close.js @@ -3,7 +3,7 @@ var common = require("./common") , openCallback = 0 , closeCallback = 0 , queryCallback = 0 -, openCount = 10 +, openCount = 3 , connections = [] ; diff --git a/test/test-openSync.js b/test/test-openSync.js index 4fabf4f8..9e03d593 100644 --- a/test/test-openSync.js +++ b/test/test-openSync.js @@ -12,6 +12,8 @@ db.query("select * from " + common.tableName, function (err, rs, moreResultSets) assert.equal(db.connected, false); }); +console.log("Attempting to connect to: %s", common.connectionString); + try { db.openSync(common.connectionString); } From 67517dd26af23dff66277c2bab31acb11d44d5a2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Jul 2013 16:20:05 -0400 Subject: [PATCH 290/511] bench: move insert benchmark to disabled --- test/{ => disabled}/bench-insert.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{ => disabled}/bench-insert.js (100%) diff --git a/test/bench-insert.js b/test/disabled/bench-insert.js similarity index 100% rename from test/bench-insert.js rename to test/disabled/bench-insert.js From 2d019ef030f167d33789b0e158e08257a6c7d892 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Jul 2013 16:31:43 -0400 Subject: [PATCH 291/511] bench: update benchmarks to run with run-bench Eventually we will have a table which displays results for each driver --- test/bench-prepare-bind-execute-closeSync.js | 6 +- test/bench-prepare-bind-executeNonQuery.js | 6 +- ...ench-prepare-bindSync-execute-closeSync.js | 6 +- .../bench-prepare-bindSync-executeNonQuery.js | 6 +- test/bench-prepare-execute-closeSync.js | 6 +- test/bench-prepare-executeNonQuery.js | 6 +- test/bench-prepare-not.js | 6 +- test/bench-query-fetch.js | 4 +- test/bench-query-fetchAll.js | 4 +- test/bench-query-fetchAllSync.js | 4 +- test/bench-query.js | 4 +- test/bench-querySync-fetchArray.js | 4 +- test/bench-querySync.js | 4 +- test/config.benchConnectionStrings.json | 2 + test/run-bench.js | 75 +++++++++++++++++++ 15 files changed, 97 insertions(+), 46 deletions(-) create mode 100644 test/run-bench.js diff --git a/test/bench-prepare-bind-execute-closeSync.js b/test/bench-prepare-bind-execute-closeSync.js index 19aad5a0..d858fa09 100644 --- a/test/bench-prepare-bind-execute-closeSync.js +++ b/test/bench-prepare-bind-execute-closeSync.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 100000 + , iterations = 10000 ; db.open(common.connectionString, function(err){ @@ -56,7 +56,5 @@ function issueQuery3(done) { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } diff --git a/test/bench-prepare-bind-executeNonQuery.js b/test/bench-prepare-bind-executeNonQuery.js index 1be5e9f7..f6c5d2a3 100644 --- a/test/bench-prepare-bind-executeNonQuery.js +++ b/test/bench-prepare-bind-executeNonQuery.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 100000 + , iterations = 10000 ; db.open(common.connectionString, function(err){ @@ -50,7 +50,5 @@ function issueQuery2(done) { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } diff --git a/test/bench-prepare-bindSync-execute-closeSync.js b/test/bench-prepare-bindSync-execute-closeSync.js index 1fdb2e1f..cbf0f670 100644 --- a/test/bench-prepare-bindSync-execute-closeSync.js +++ b/test/bench-prepare-bindSync-execute-closeSync.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 100000 + , iterations = 10000 ; db.open(common.connectionString, function(err){ @@ -46,7 +46,5 @@ function issueQuery3(done) { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } diff --git a/test/bench-prepare-bindSync-executeNonQuery.js b/test/bench-prepare-bindSync-executeNonQuery.js index 40aad267..f26da824 100644 --- a/test/bench-prepare-bindSync-executeNonQuery.js +++ b/test/bench-prepare-bindSync-executeNonQuery.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 100000 + , iterations = 10000 ; db.open(common.connectionString, function(err){ @@ -44,7 +44,5 @@ function issueQuery2(done) { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } diff --git a/test/bench-prepare-execute-closeSync.js b/test/bench-prepare-execute-closeSync.js index 8bcf4327..c4d1e53c 100644 --- a/test/bench-prepare-execute-closeSync.js +++ b/test/bench-prepare-execute-closeSync.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 100000 + , iterations = 10000 ; db.open(common.connectionString, function(err){ @@ -47,7 +47,5 @@ function issueQuery3(done) { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } diff --git a/test/bench-prepare-executeNonQuery.js b/test/bench-prepare-executeNonQuery.js index 309a33a4..14b9320a 100644 --- a/test/bench-prepare-executeNonQuery.js +++ b/test/bench-prepare-executeNonQuery.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 100000 + , iterations = 10000 ; db.open(common.connectionString, function(err){ @@ -43,7 +43,5 @@ function issueQuery2(done) { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } diff --git a/test/bench-prepare-not.js b/test/bench-prepare-not.js index a880e802..8596da49 100644 --- a/test/bench-prepare-not.js +++ b/test/bench-prepare-not.js @@ -1,7 +1,7 @@ var common = require("./common") , odbc = require("../") , db = new odbc.Database() - , iterations = 100000 + , iterations = 10000 ; db.open(common.connectionString, function(err){ @@ -39,7 +39,5 @@ function issueQuery1(done) { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } diff --git a/test/bench-query-fetch.js b/test/bench-query-fetch.js index 81c522a8..ea4d91b4 100644 --- a/test/bench-query-fetch.js +++ b/test/bench-query-fetch.js @@ -54,8 +54,6 @@ function issueQuery() { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } } \ No newline at end of file diff --git a/test/bench-query-fetchAll.js b/test/bench-query-fetchAll.js index a5b4b63d..04ba202d 100644 --- a/test/bench-query-fetchAll.js +++ b/test/bench-query-fetchAll.js @@ -44,8 +44,6 @@ function issueQuery() { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } } \ No newline at end of file diff --git a/test/bench-query-fetchAllSync.js b/test/bench-query-fetchAllSync.js index 31059f1e..e5390777 100644 --- a/test/bench-query-fetchAllSync.js +++ b/test/bench-query-fetchAllSync.js @@ -38,8 +38,6 @@ function issueQuery() { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } } \ No newline at end of file diff --git a/test/bench-query.js b/test/bench-query.js index c56f3c69..c4e4c90a 100644 --- a/test/bench-query.js +++ b/test/bench-query.js @@ -36,7 +36,5 @@ function issueQuery() { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } diff --git a/test/bench-querySync-fetchArray.js b/test/bench-querySync-fetchArray.js index fc52ce8e..2674dd18 100644 --- a/test/bench-querySync-fetchArray.js +++ b/test/bench-querySync-fetchArray.js @@ -25,7 +25,5 @@ function issueQuery() { console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); - db.close(function () { - console.log("connection closed"); - }); + db.close(function () {}); } \ No newline at end of file diff --git a/test/bench-querySync.js b/test/bench-querySync.js index 92edb18b..8a8a8948 100644 --- a/test/bench-querySync.js +++ b/test/bench-querySync.js @@ -25,7 +25,5 @@ function issueQuery() { console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); - db.close(function () { - console.log("connection closed"); - }); + db.close(function () { }); } \ No newline at end of file diff --git a/test/config.benchConnectionStrings.json b/test/config.benchConnectionStrings.json index 9399cc17..437710d6 100644 --- a/test/config.benchConnectionStrings.json +++ b/test/config.benchConnectionStrings.json @@ -1,4 +1,6 @@ [ { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db" } , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;" } + , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test;AutoTranslate=yes" } + , { "title" : "MSSQL-NativeCLI-Remote", "connectionString" : "DRIVER={SQL Server Native Client 11.0};SERVER=sql2;DATABASE=test;UID=test;PWD=test;" } ] diff --git a/test/run-bench.js b/test/run-bench.js new file mode 100644 index 00000000..c19ab424 --- /dev/null +++ b/test/run-bench.js @@ -0,0 +1,75 @@ +var fs = require("fs") + , common = require('./common.js') + , spawn = require("child_process").spawn + , requestedBench = null + , files + ; + +if (process.argv.length === 3) { + requestedBench = process.argv[2]; +} + +var connectionStrings = common.benchConnectionStrings; + +//check to see if the requested test is actually a driver to benchmark +if (requestedBench) { + connectionStrings.forEach(function (connectionString) { + if (requestedBench == connectionString.title) { + connectionStrings = [connectionString]; + requestedBench = null; + } + }); +} + +doNextConnectionString(); + +function doBench(file, connectionString) { + var bench = spawn("node", ['--expose_gc',file, connectionString.connectionString]); + + process.stdout.write("Running \033[01;33m" + file.replace(/\.js$/, "") + "\033[01;0m with [\033[01;29m" + connectionString.title + "\033[01;0m] : "); + + bench.on("exit", function (code, signal) { + doNextBench(connectionString); + }); + + bench.stdout.on("data", function (data) { + process.stdout.write(data); + }); +} + +function doNextBench(connectionString) { + if (files.length) { + var benchFile = files.shift(); + + doBench(benchFile, connectionString); + } + else { + //we're done with this connection string, display results and exit accordingly + doNextConnectionString(); + } +} + +function doNextConnectionString() { + if (connectionStrings.length) { + var connectionString = connectionStrings.shift(); + + if (requestedBench) { + files = [requestedBench]; + } + else { + //re-read files + files = fs.readdirSync("./"); + + files = files.filter(function (file) { + return (/^bench-/.test(file)) ? true : false; + }); + + files.sort(); + } + + doNextBench(connectionString); + } + else { + console.log("Done"); + } +} \ No newline at end of file From 19796895f30c629c36595b00341ddacd96f74853 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 Jul 2013 16:49:19 -0400 Subject: [PATCH 292/511] 0.5.18 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ad004f1d..8330409f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.17", + "version": "0.5.18", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 7fb2eb388674b395e93e75d58384c4d893973fa5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Jul 2013 12:19:05 -0400 Subject: [PATCH 293/511] fix: malloc error --- src/odbc_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 691813c8..4ae81fa3 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -147,7 +147,7 @@ Handle ODBCConnection::Open(const Arguments& args) { open_connection_work_data* data = (open_connection_work_data *) calloc(1, sizeof(open_connection_work_data)); - data->connectionLength = connection->Length(); + data->connectionLength = connection->Length() + 1; //copy the connection string to the work data #ifdef UNICODE From 593188a47761bde8c2e3b42a02598d0609577bb4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Jul 2013 12:19:51 -0400 Subject: [PATCH 294/511] debug: update debug messages --- src/odbc.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 19b8437a..441f54c6 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -388,8 +388,8 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, sizeof(value), &len); - DEBUG_PRINTF("ODBC::GetColumnValue - Number: index=%i name=%s type=%i len=%i ret=%i\n", - column.index, column.name, column.type, len, ret); + DEBUG_PRINTF("ODBC::GetColumnValue - Number: index=%i name=%s type=%i len=%i ret=%i val=%f\n", + column.index, column.name, column.type, len, ret, value); if(ret == SQL_NULL_DATA || len < 0) { //return scope.Close(Null()); @@ -661,9 +661,11 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].size = sizeof(double); DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + "c_type=%i type=%i buffer_length=%i size=%i length=%i " + "value=%f\n", i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length); + params[i].buffer_length, params[i].size, params[i].length, + *number); } else if (value->IsBoolean()) { bool *boolean = new bool(value->BooleanValue()); From 8d7be8ae142c1bf74d5254d4857db01954e119c4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Jul 2013 12:52:00 -0400 Subject: [PATCH 295/511] Specify DecimalDigits when binding param" --- src/odbc_connection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 4ae81fa3..4b431262 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -822,7 +822,7 @@ void ODBCConnection::UV_Query(uv_work_t* req) { prm.c_type, //ValueType prm.type, //ParameterType prm.size, //ColumnSize - 0, //DecimalDigits + prm.decimals, //DecimalDigits prm.buffer, //ParameterValuePtr prm.buffer_length, //BufferLength //using &prm.length did not work here... @@ -1088,7 +1088,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { prm.c_type, //ValueType prm.type, //ParameterType prm.size, //ColumnSize - 0, //DecimalDigits + prm.decimals, //DecimalDigits prm.buffer, //ParameterValuePtr prm.buffer_length, //BufferLength //using &prm.length did not work here... From e711f6b32b507f58e468d6e61044fcea7636f74d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Jul 2013 14:17:54 -0400 Subject: [PATCH 296/511] fix-attempt: in the case of type double binding params, set decimals to 7. This is probably a terrible idea, but so far MSSQL drivers don't work any other way. While MySQL and Sqlite seem to ignore this value because it does not effect the precision of the input. --- src/odbc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 441f54c6..297bc0e5 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -657,7 +657,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].buffer = number; params[i].buffer_length = sizeof(double); params[i].length = params[i].buffer_length; - params[i].decimals = 0; + params[i].decimals = 7; params[i].size = sizeof(double); DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] " From 69f4f06b392d6103da5ff6da944061ff0f5fd610 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 4 Jul 2013 15:23:00 -0400 Subject: [PATCH 297/511] 0.5.19 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8330409f..540e091d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.18", + "version": "0.5.19", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 3c1ac190fbcbd29e55423ed7612ac5e95f74c1f6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Jul 2013 11:08:16 -0400 Subject: [PATCH 298/511] Add STRICT_COLUMN_NAMES compile option and documentation. --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ src/odbc.cpp | 4 ++++ 2 files changed, 43 insertions(+) diff --git a/README.md b/README.md index 317f9f40..c5493104 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,8 @@ driver. On Ubuntu: `sudo apt-get install libsqliteodbc` build options ------------- +### Debug + If you would like to enable debugging messages to be displayed you can add the flag `DEBUG` to the defines section of the `binding.gyp` file and then execute `node-gyp rebuild`. @@ -353,6 +355,8 @@ flag `DEBUG` to the defines section of the `binding.gyp` file and then execute ``` +### Dynodbc + You may also enable the ability to load a specific ODBC driver and bypass the ODBC driver management layer. A performance increase of ~5Kqps was seen using this method with the libsqlite3odbc driver. To do this, specify the `dynodbc` @@ -373,6 +377,41 @@ rebuild`. ``` +### Unicode + +By default, UNICODE suppport is enabled. This should provide the most accurate +way to get Unicode strings submitted to your database. For best results, you +may want to put your Unicode string into bound parameters. + +However, if you experience issues or you think that submitting UTF8 strings will +work better or faster, you can remove the `UNICODE` define in `binding.gyp` + +```javascript + +'defines' : [ + "UNICODE" +], + +``` + +### Strict Column Naming + +When column names are retrieved from ODBC, you can request by SQL_DESC_NAME or +SQL_DESC_LABEL. SQL_DESC_NAME is the exact column name or none if there is none +defined. SQL_DESC_LABEL is the heading or column name or calculation. +SQL_DESC_LABEL is used by default and seems to work well in most cases. + +If you want to use the exact column name via SQL_DESC_NAME, enable the `STRICT_COLUMN_NAMES` +define in `binding.gyp` + +```javascript + +'defines' : [ + "STRICT_COLUMN_NAMES" +], + +``` + tips ---- ### Using node < v0.10 on Linux diff --git a/src/odbc.cpp b/src/odbc.cpp index 297bc0e5..fde923c2 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -293,7 +293,11 @@ Column* ODBC::GetColumns(SQLHSTMT hStmt, short* colCount) { //get the column name ret = SQLColAttribute( hStmt, columns[i].index, +#ifdef STRICT_COLUMN_NAMES + SQL_DESC_NAME, +#else SQL_DESC_LABEL, +#endif columns[i].name, (SQLSMALLINT) MAX_FIELD_SIZE, (SQLSMALLINT *) &buflen, From ca9a91f3227aae6dc13bc3c51c55d8ab03ff9dff Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Jul 2013 11:08:43 -0400 Subject: [PATCH 299/511] 0.5.20 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 540e091d..94921d42 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.19", + "version": "0.5.20", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From d0a726841cb089e3445dbe4a54c7118bd55c6921 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Jul 2013 17:12:58 -0400 Subject: [PATCH 300/511] Disable OutConnectionString in SQLDriverConnect We weren't using it anyway and it was an extra malloc --- src/odbc_connection.cpp | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 4b431262..f0ed745f 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -182,14 +182,6 @@ void ODBCConnection::UV_Open(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); -#ifdef UNICODE - int connectionStringOutSize = 1024 * sizeof(uint16_t); - uint16_t* connectionStringOut = (uint16_t *) malloc(connectionStringOutSize); -#else - int connectionStringOutSize = 1024; - char* connectionStringOut = (char *) malloc(connectionStringOutSize); -#endif - //TODO: make this configurable int timeOut = 5; @@ -207,8 +199,8 @@ void ODBCConnection::UV_Open(uv_work_t* req) { NULL, //WindowHandle (SQLTCHAR*) data->connection, //InConnectionString data->connectionLength, //StringLength1 - (SQLTCHAR*) connectionStringOut,//OutConnectionString - 1024, //BufferLength - in characters + NULL, //OutConnectionString + 0, //BufferLength - in characters NULL, //StringLength2Ptr SQL_DRIVER_NOPROMPT); //DriverCompletion @@ -305,13 +297,9 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { int connectionLength = connection->Length() + 1; #ifdef UNICODE - int connectionStringOutSize = 1024 * sizeof(uint16_t); - uint16_t* connectionStringOut = (uint16_t *) malloc(connectionStringOutSize); uint16_t* connectionString = (uint16_t *) malloc(connectionLength * sizeof(uint16_t)); connection->Write(connectionString); #else - int connectionStringOutSize = 1024; - char* connectionStringOut = (char *) malloc(connectionStringOutSize); char* connectionString = (char *) malloc(connectionLength); connection->WriteUtf8(connectionString); #endif @@ -335,8 +323,8 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { NULL, //WindowHandle (SQLTCHAR*) connectionString, //InConnectionString connectionLength, //StringLength1 - (SQLTCHAR*) connectionStringOut,//OutConnectionString - 1024, //BufferLength - in characters + NULL, //OutConnectionString + 0, //BufferLength - in characters NULL, //StringLength2Ptr SQL_DRIVER_NOPROMPT); //DriverCompletion @@ -378,7 +366,6 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { uv_mutex_unlock(&ODBC::g_odbcMutex); free(connectionString); - free(connectionStringOut); if (err) { ThrowException(objError); From cba5ac7e85a582e0ebcfbc5d112589a90793c02f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Jul 2013 17:13:54 -0400 Subject: [PATCH 301/511] Use Local instead of Persisten for ODBC* instances --- src/odbc.cpp | 4 ++-- src/odbc_connection.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index fde923c2..1fde96d4 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -210,7 +210,7 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { args[0] = External::New(data->dbo->m_hEnv); args[1] = External::New(data->hDBC); - Persistent js_result(ODBCConnection::constructor_template-> + Local js_result(ODBCConnection::constructor_template-> GetFunction()->NewInstance(2, args)); args[0] = Local::New(Null()); @@ -255,7 +255,7 @@ Handle ODBC::CreateConnectionSync(const Arguments& args) { params[0] = External::New(dbo->m_hEnv); params[1] = External::New(hDBC); - Persistent js_result(ODBCConnection::constructor_template-> + Local js_result(ODBCConnection::constructor_template-> GetFunction()->NewInstance(2, params)); return scope.Close(js_result); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index f0ed745f..cf893fd4 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -518,7 +518,7 @@ Handle ODBCConnection::CreateStatementSync(const Arguments& args) { params[1] = External::New(conn->m_hDBC); params[2] = External::New(hSTMT); - Persistent js_result(ODBCStatement::constructor_template-> + Local js_result(ODBCStatement::constructor_template-> GetFunction()->NewInstance(3, params)); return scope.Close(js_result); @@ -605,7 +605,7 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { args[1] = External::New(data->conn->m_hDBC); args[2] = External::New(data->hSTMT); - Persistent js_result(ODBCStatement::constructor_template-> + Local js_result(ODBCStatement::constructor_template-> GetFunction()->NewInstance(3, args)); args[0] = Local::New(Null()); @@ -868,7 +868,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { args[2] = External::New(data->hSTMT); args[3] = External::New(canFreeHandle); - Persistent js_result(ODBCResult::constructor_template-> + Local js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); args[0] = Local::New(Null()); @@ -1137,7 +1137,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { args[2] = External::New(hSTMT); args[3] = External::New(canFreeHandle); - Persistent js_result(ODBCResult::constructor_template-> + Local js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); return scope.Close(js_result); From cf18f5c1a399959f990a083588c1a76ee15e4c5d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Jul 2013 17:14:30 -0400 Subject: [PATCH 302/511] Use proper HDBC type when getting js_hdbc->Value() --- src/odbc_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index cf893fd4..27a66a65 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -108,7 +108,7 @@ Handle ODBCConnection::New(const Arguments& args) { REQ_EXT_ARG(1, js_hdbc); HENV hENV = static_cast(js_henv->Value()); - HDBC hDBC = static_cast(js_hdbc->Value()); + HDBC hDBC = static_cast(js_hdbc->Value()); ODBCConnection* conn = new ODBCConnection(hENV, hDBC); From 11a37f5d0e15d885c5ff3b42e9a59d3426fa7b50 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 5 Jul 2013 17:15:36 -0400 Subject: [PATCH 303/511] Set connected status after closeSync() --- lib/odbc.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/odbc.js b/lib/odbc.js index f5260fd6..cb38908e 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -123,6 +123,7 @@ Database.prototype.close = function (cb) { self.queue.push(function (next) { self.conn.close(function (err) { self.connected = false; + delete self.conn; cb(err); return next(); @@ -133,7 +134,12 @@ Database.prototype.close = function (cb) { Database.prototype.closeSync = function (cb) { var self = this; - return self.conn.closeSync(); + var result = self.conn.closeSync(); + + self.connected = false; + delete self.conn; + + return result } Database.prototype.query = function (sql, params, cb) { From 20102ebaedc8b09790de323a8ee8c124ef5b78ed Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 17 Jul 2013 13:27:59 -0400 Subject: [PATCH 304/511] statement: free SQL_C_WCHAR param types --- src/odbc_statement.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index de400298..a18492db 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -87,6 +87,7 @@ void ODBCStatement::Free() { for (int i = 0; i < count; i++) { if (prm = params[i], prm.buffer != NULL) { switch (prm.c_type) { + case SQL_C_WCHAR: free(prm.buffer); break; case SQL_C_CHAR: free(prm.buffer); break; case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; case SQL_C_DOUBLE: delete (double *)prm.buffer; break; @@ -774,6 +775,7 @@ Handle ODBCStatement::BindSync(const Arguments& args) { for (int i = 0; i < count; i++) { if (prm = stmt->params[i], prm.buffer != NULL) { switch (prm.c_type) { + case SQL_C_WCHAR: free(prm.buffer); break; case SQL_C_CHAR: free(prm.buffer); break; case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; case SQL_C_DOUBLE: delete (double *)prm.buffer; break; @@ -873,6 +875,7 @@ Handle ODBCStatement::Bind(const Arguments& args) { for (int i = 0; i < count; i++) { if (prm = stmt->params[i], prm.buffer != NULL) { switch (prm.c_type) { + case SQL_C_WCHAR: free(prm.buffer); break; case SQL_C_CHAR: free(prm.buffer); break; case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; case SQL_C_DOUBLE: delete (double *)prm.buffer; break; From 4d5e700d0042eb256477a6bf7b69cdb32134d88d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 17 Jul 2013 13:28:37 -0400 Subject: [PATCH 305/511] statement: throw errors on prepareSync --- src/odbc_statement.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index a18492db..04c4ea58 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -620,7 +620,12 @@ Handle ODBCStatement::PrepareSync(const Arguments& args) { return scope.Close(True()); } else { - //TODO: throw an error object + ThrowException(ODBC::GetSQLError( + SQL_HANDLE_STMT, + stmt->m_hSTMT, + (char *) "[node-odbc] Error in ODBCStatement::BindSync" + )); + return scope.Close(False()); } } From ca77ad6c7a38211f701c7ae42b07660210492e5a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 17 Jul 2013 13:42:38 -0400 Subject: [PATCH 306/511] test: add test to try to invoke errors during prepare/bind/execute --- test/test-prepare-bind-execute-error.js | 53 +++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 test/test-prepare-bind-execute-error.js diff --git a/test/test-prepare-bind-execute-error.js b/test/test-prepare-bind-execute-error.js new file mode 100644 index 00000000..0d36649b --- /dev/null +++ b/test/test-prepare-bind-execute-error.js @@ -0,0 +1,53 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , time = new Date().getTime(); + + try { + var stmt = db.prepareSync('select cast(? as datetime) as test'); + } + catch (e) { + console.log(e); + return finish(1); + } + + stmt.bind([], function (err) { + if (err) { + console.log(err); + return finish(1); + } + + stmt.execute(function (err, result) { + if (err) { + console.log(err); + return finish(1); + } + + console.log(result.fetchAllSync()); + + finish(0); + }); + }); +} + +function finish(exitCode) { + db.close(function () { + console.log("connection closed"); + + process.exit(exitCode || 0); + }); +} From 7ad460ae635beb22f3fd9aac713f84f0790936ec Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 09:48:55 -0400 Subject: [PATCH 307/511] Set the prototype of our error objects to Exception::Error This allows node's default error handling to display stack traces and such. So much nicer than just [Object object] --- src/odbc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index 1fde96d4..d4888335 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -743,6 +743,11 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* Local objError = Object::New(); + //Setting the prototype to an Error Exception will actually + //allow the node interface to show proper errors rather than + //just [Object object]. + objError->SetPrototype(Exception::Error(String::New(""))); + SQLINTEGER i = 0; SQLINTEGER native; From 23d364c99209fbf8d29e6695de9660417d882227 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 10:15:33 -0400 Subject: [PATCH 308/511] Don't set message manually on error object, use Exception::Error constructor for that --- src/odbc.cpp | 9 ++------- src/odbc_statement.cpp | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index d4888335..79fdba4c 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -743,11 +743,6 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* Local objError = Object::New(); - //Setting the prototype to an Error Exception will actually - //allow the node interface to show proper errors rather than - //just [Object object]. - objError->SetPrototype(Exception::Error(String::New(""))); - SQLINTEGER i = 0; SQLINTEGER native; @@ -786,10 +781,10 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* objError->Set(String::New("error"), String::New(message)); #ifdef UNICODE - objError->Set(String::New("message"), String::New((uint16_t *) errorMessage)); + objError->SetPrototype(Exception::Error(String::New((uint16_t *) errorMessage))); objError->Set(String::New("state"), String::New((uint16_t *) errorSQLState)); #else - objError->Set(String::New("message"), String::New(errorMessage)); + objError->SetPrototype(Exception::Error(String::New(errorMessage))); objError->Set(String::New("state"), String::New(errorSQLState)); #endif } diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 04c4ea58..f4be6266 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -623,9 +623,9 @@ Handle ODBCStatement::PrepareSync(const Arguments& args) { ThrowException(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, - (char *) "[node-odbc] Error in ODBCStatement::BindSync" + (char *) "[node-odbc] Error in ODBCStatement::PrepareSync" )); - + return scope.Close(False()); } } From 7a9674dee398b03f872d4cc3c52dd342d69cfe11 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 10:19:53 -0400 Subject: [PATCH 309/511] test: update test to assert throw/doesNotThrow --- test/test-prepare-bind-execute-error.js | 50 +++++++++++++------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/test/test-prepare-bind-execute-error.js b/test/test-prepare-bind-execute-error.js index 0d36649b..e6637738 100644 --- a/test/test-prepare-bind-execute-error.js +++ b/test/test-prepare-bind-execute-error.js @@ -15,33 +15,35 @@ db.open(common.connectionString, function(err){ function issueQuery() { var count = 0 - , time = new Date().getTime(); + , time = new Date().getTime() + , stmt + , result + , data + ; - try { - var stmt = db.prepareSync('select cast(? as datetime) as test'); - } - catch (e) { - console.log(e); - return finish(1); - } + assert.doesNotThrow(function () { + stmt = db.prepareSync('select cast(? as datetime) as test'); + }); + + assert.throws(function () { + result = stmt.executeSync(); + }); - stmt.bind([], function (err) { - if (err) { - console.log(err); - return finish(1); - } - - stmt.execute(function (err, result) { - if (err) { - console.log(err); - return finish(1); - } - - console.log(result.fetchAllSync()); - - finish(0); - }); + assert.doesNotThrow(function () { + stmt.bindSync([0]); }); + + assert.doesNotThrow(function () { + result = stmt.executeSync(); + }); + + assert.doesNotThrow(function () { + data = result.fetchAllSync(); + }); + + assert.ok(data); + + finish(0); } function finish(exitCode) { From 9bd0a43729f319552f40e75e926c3cc04186c680 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 10:20:04 -0400 Subject: [PATCH 310/511] 0.5.21 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 94921d42..43b6a42f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.20", + "version": "0.5.21", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From a7888a085e2622caa79809e660f66065108f7920 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 11:49:06 -0400 Subject: [PATCH 311/511] Use SQL_LONGVARCHAR for string parameters --- src/odbc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 79fdba4c..6930ac50 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -608,10 +608,10 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].c_type = SQL_C_TCHAR; #ifdef UNICODE - params[i].type = SQL_WVARCHAR; + params[i].type = SQL_WLONGVARCHAR; params[i].buffer_length = (string->Length() * sizeof(uint16_t)) + sizeof(uint16_t); #else - params[i].type = SQL_VARCHAR; + params[i].type = SQL_LONGVARCHAR; params[i].buffer_length = string->Utf8Length() + 1; #endif params[i].buffer = malloc(params[i].buffer_length); From 48c0bbf601b0ba2be8fc2bd114cec814e6c9983a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 11:49:30 -0400 Subject: [PATCH 312/511] Add test for binding a long string --- test/test-prepare-bind-execute-long-string.js | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 test/test-prepare-bind-execute-long-string.js diff --git a/test/test-prepare-bind-execute-long-string.js b/test/test-prepare-bind-execute-long-string.js new file mode 100644 index 00000000..eda36708 --- /dev/null +++ b/test/test-prepare-bind-execute-long-string.js @@ -0,0 +1,66 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , time = new Date().getTime() + , stmt + , result + , data + , str = '' + ; + + for (var x = 0; x < 10000; x++) { + str += 't'; + } + + assert.doesNotThrow(function () { + stmt = db.prepareSync('select ? as longString'); + }); + + assert.doesNotThrow(function () { + stmt.bindSync([str]); + }); + + assert.doesNotThrow(function () { + result = stmt.executeSync(); + }); + + assert.doesNotThrow(function () { + data = result.fetchAllSync(); + }); + +// console.log(data); + console.log(str.length); + console.log(data[0].longString.length); + +// for (var x = 0; x < str.length; x++) { +// if (str[x] != data[0].longString[x]) { +// //console.log(x, str[x], data[0].longString[x]); +// } +// } + + assert.equal(data[0].longString, str); + + finish(0); +} + +function finish(exitCode) { + db.close(function () { + console.log("connection closed"); + + process.exit(exitCode || 0); + }); +} From a978948a3cb81fe5237c9cd21e32dd45e8dc3a78 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 11:50:00 -0400 Subject: [PATCH 313/511] 0.5.22 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 43b6a42f..cb2ca2cb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.21", + "version": "0.5.22", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 961b03835b8cf7d8b0a9db55def8498f40f2686d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 16:17:39 -0400 Subject: [PATCH 314/511] GetColumnValue: check if len == SQL_NULL_DATA instead of ret --- src/odbc.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 6930ac50..4ffec49e 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -366,7 +366,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Integer: index=%i name=%s type=%i len=%i ret=%i\n", column.index, column.name, column.type, len, ret); - if (ret == SQL_NULL_DATA || len < 0) { + if (len == SQL_NULL_DATA || len < 0) { //return scope.Close(Null()); return Null(); } @@ -395,7 +395,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Number: index=%i name=%s type=%i len=%i ret=%i val=%f\n", column.index, column.name, column.type, len, ret, value); - if(ret == SQL_NULL_DATA || len < 0) { + if(len == SQL_NULL_DATA || len < 0) { //return scope.Close(Null()); return Null(); } @@ -423,7 +423,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - W32 Timestamp: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); - if(ret == SQL_NULL_DATA || len < 0) { + if(len == SQL_NULL_DATA || len < 0) { return Null(); } else { @@ -464,7 +464,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Unix Timestamp: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); - if(ret == SQL_NULL_DATA || len < 0) { + if(len == SQL_NULL_DATA || len < 0) { //return scope.Close(Null()); return Null(); } @@ -502,7 +502,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Bit: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); - if(ret == SQL_NULL_DATA || len < 0) { + if(len == SQL_NULL_DATA || len < 0) { //return scope.Close(Null()); return Null(); } From 5dc58a1b330003b439585ee65cd394b4be49b7ac Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 16:19:12 -0400 Subject: [PATCH 315/511] GetColumnValue: add support for returning really long string column values --- src/odbc.cpp | 62 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 4ffec49e..5e37f8b1 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -511,29 +511,55 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, return Boolean::New(( *buffer == '0') ? false : true ); } default : - ret = SQLGetData( - hStmt, - column.index, - SQL_C_TCHAR, - (char *) buffer, - bufferLength, - &len); + Local str; + int count = 0; + + do { + ret = SQLGetData( + hStmt, + column.index, + SQL_C_TCHAR, + (char *) buffer, + bufferLength, + &len); - DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", - column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); + DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", + column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); - if(ret == SQL_NULL_DATA || len < 0) { - //return scope.Close(Null()); - return Null(); - } - else { - //return scope.Close(String::New((char*) buffer)); + if(len == SQL_NULL_DATA || len < 0) { + //return scope.Close(Null()); + return Null(); + } + + if (ret != SQL_NO_DATA) { + //we have not captured all of the data yet + + if (count == 0) { + //no concatenation required, this is our first pass #ifdef UNICODE - return String::New((uint16_t*) buffer, len / 2); + str = String::New((uint16_t*) buffer); #else - return String::New((char *) buffer, len); + str = String::New((char *) buffer); #endif - } + } + else { + //we need to concatenate +#ifdef UNICODE + str = String::Concat(str, String::New((uint16_t*) buffer)); +#else + str = String::Concat(str, String::New((char *) buffer)); +#endif + } + + count += 1; + } + else { + //we have captured all of the data + break; + } + } while (true); + + return str; } } From 6f2622fb12a23a059163f935534f302db4a1138e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 16:20:04 -0400 Subject: [PATCH 316/511] GetSQLError: re-introduce the explicit message property --- src/odbc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index 5e37f8b1..32e67fb9 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -808,9 +808,11 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* objError->Set(String::New("error"), String::New(message)); #ifdef UNICODE objError->SetPrototype(Exception::Error(String::New((uint16_t *) errorMessage))); + objError->Set(String::New("message"), String::New((uint16_t *) errorMessage)); objError->Set(String::New("state"), String::New((uint16_t *) errorSQLState)); #else objError->SetPrototype(Exception::Error(String::New(errorMessage))); + objError->Set(String::New("message"), String::New(errorMessage)); objError->Set(String::New("state"), String::New(errorSQLState)); #endif } From e200576f17b8d571c7fe4eb31bc30059e1ad5510 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 16:20:27 -0400 Subject: [PATCH 317/511] test: udpate some tests --- test/test-bad-connection-string.js | 7 +++++++ test/test-prepare-bind-execute-long-string.js | 18 +++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/test/test-bad-connection-string.js b/test/test-bad-connection-string.js index 9f32ffe1..e742a402 100644 --- a/test/test-bad-connection-string.js +++ b/test/test-bad-connection-string.js @@ -4,8 +4,15 @@ var common = require("./common") , assert = require("assert") ; +assert.throws(function () { + db.openSync("this is wrong"); +}); +assert.equal(db.connected, false); + db.open("this is wrong", function(err) { + console.log(err); + assert.deepEqual(err, { error: '[node-odbc] SQL_ERROR', message: '[unixODBC][Driver Manager]Data source name not found, and no default driver specified', diff --git a/test/test-prepare-bind-execute-long-string.js b/test/test-prepare-bind-execute-long-string.js index eda36708..6a321512 100644 --- a/test/test-prepare-bind-execute-long-string.js +++ b/test/test-prepare-bind-execute-long-string.js @@ -22,8 +22,10 @@ function issueQuery() { , str = '' ; - for (var x = 0; x < 10000; x++) { - str += 't'; + var set = 'abcdefghijklmnopqrstuvwxyz'; + + for (var x = 0; x < 1000001; x++) { + str += set[x % set.length]; } assert.doesNotThrow(function () { @@ -46,11 +48,13 @@ function issueQuery() { console.log(str.length); console.log(data[0].longString.length); -// for (var x = 0; x < str.length; x++) { -// if (str[x] != data[0].longString[x]) { -// //console.log(x, str[x], data[0].longString[x]); -// } -// } + for (var x = 0; x < str.length; x++) { + if (str[x] != data[0].longString[x]) { + console.log(x, str[x], data[0].longString[x]); + + assert.equal(str[x], data[0].longString[x]); + } + } assert.equal(data[0].longString, str); From 5bbce7df7344e4acee3cdad03d12ae6ae8badf00 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 16:31:15 -0400 Subject: [PATCH 318/511] test: reduce number of iterations; causing false failure due to timeout --- test/test-prepare-bindSync-execute-closeSync.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-prepare-bindSync-execute-closeSync.js b/test/test-prepare-bindSync-execute-closeSync.js index 7025a058..2bd0cc72 100644 --- a/test/test-prepare-bindSync-execute-closeSync.js +++ b/test/test-prepare-bindSync-execute-closeSync.js @@ -2,7 +2,7 @@ var common = require("./common") , odbc = require("../") , assert = require("assert") , db = new odbc.Database() - , iterations = 100000 + , iterations = 100 ; db.open(common.connectionString, function(err){ From f5f6cb5280c11d8bea31ce6bd27b5ea1c803a907 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 16:32:07 -0400 Subject: [PATCH 319/511] GetColumnValue: remove comparison of len < 0 That was just an alternate way of checking len == SQL_NULL_DATA --- src/odbc.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 32e67fb9..f0fafa9f 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -366,7 +366,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Integer: index=%i name=%s type=%i len=%i ret=%i\n", column.index, column.name, column.type, len, ret); - if (len == SQL_NULL_DATA || len < 0) { + if (len == SQL_NULL_DATA) { //return scope.Close(Null()); return Null(); } @@ -395,7 +395,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Number: index=%i name=%s type=%i len=%i ret=%i val=%f\n", column.index, column.name, column.type, len, ret, value); - if(len == SQL_NULL_DATA || len < 0) { + if(len == SQL_NULL_DATA) { //return scope.Close(Null()); return Null(); } @@ -423,7 +423,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - W32 Timestamp: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); - if(len == SQL_NULL_DATA || len < 0) { + if(len == SQL_NULL_DATA) { return Null(); } else { @@ -464,7 +464,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Unix Timestamp: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); - if(len == SQL_NULL_DATA || len < 0) { + if(len == SQL_NULL_DATA) { //return scope.Close(Null()); return Null(); } @@ -502,7 +502,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Bit: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); - if(len == SQL_NULL_DATA || len < 0) { + if(len == SQL_NULL_DATA) { //return scope.Close(Null()); return Null(); } @@ -526,7 +526,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); - if(len == SQL_NULL_DATA || len < 0) { + if(len == SQL_NULL_DATA) { //return scope.Close(Null()); return Null(); } From d370361b8b63090825fe084d7696e839a2956bdd Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 16:46:36 -0400 Subject: [PATCH 320/511] test: add note to test --- test/test-binding-statement-rebinding.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test-binding-statement-rebinding.js b/test/test-binding-statement-rebinding.js index b87769fc..6f14a5ea 100644 --- a/test/test-binding-statement-rebinding.js +++ b/test/test-binding-statement-rebinding.js @@ -5,6 +5,12 @@ var common = require("./common") , exitCode = 0 ; +/* + * The purpose of this test is to test binding an array and then + * changing the values of the array before an execute[Sync] + * call and have the new array values be used. + */ + db.createConnection(function (err, conn) { conn.open(common.connectionString, function (err) { conn.createStatement(function (err, stmt) { From 2ca5fc247236344fe5653a2778ad1d2828fac5a4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 17:55:37 -0400 Subject: [PATCH 321/511] GetColumnValue: use handle scope --- src/odbc.cpp | 51 +++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index f0fafa9f..fb0c7476 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -339,7 +339,7 @@ void ODBC::FreeColumns(Column* columns, short* colCount) { Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength) { - //HandleScope scope; + HandleScope scope; SQLLEN len = 0; //reset the buffer @@ -367,12 +367,12 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret); if (len == SQL_NULL_DATA) { - //return scope.Close(Null()); - return Null(); + return scope.Close(Null()); + //return Null(); } else { - //return scope.Close(Integer::New(value)); - return Integer::New(value); + return scope.Close(Integer::New(value)); + //return Integer::New(value); } } break; @@ -396,12 +396,12 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret, value); if(len == SQL_NULL_DATA) { - //return scope.Close(Null()); - return Null(); + return scope.Close(Null()); + //return Null(); } else { - //return scope.Close(Number::New(value)); - return Number::New(value); + return scope.Close(Number::New(value)); + //return Number::New(value); } } break; @@ -424,7 +424,8 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if(len == SQL_NULL_DATA) { - return Null(); + return scope.Close(Null()); + //return Null(); } else { strptime((char *) buffer, "%Y-%m-%d %H:%M:%S", &timeInfo); @@ -434,7 +435,8 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //at the specified time. timeInfo.tm_isdst = -1; - return Date::New((double(mktime(&timeInfo)) * 1000)); + //return Date::New((double(mktime(&timeInfo)) * 1000)); + return scope.Close(Date::New((double(mktime(&timeInfo)) * 1000))); } #else struct tm timeInfo = { @@ -465,8 +467,8 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if(len == SQL_NULL_DATA) { - //return scope.Close(Null()); - return Null(); + return scope.Close(Null()); + //return Null(); } else { timeInfo.tm_year = odbcTime.year - 1900; @@ -481,10 +483,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //at the specified time. timeInfo.tm_isdst = -1; - //return scope.Close(Date::New((double(timegm(&timeInfo)) * 1000) - // + (odbcTime.fraction / 1000000))); - return Date::New((double(timegm(&timeInfo)) * 1000) - + (odbcTime.fraction / 1000000)); + return scope.Close(Date::New((double(timegm(&timeInfo)) * 1000) + + (odbcTime.fraction / 1000000))); + //return Date::New((double(timegm(&timeInfo)) * 1000) + // + (odbcTime.fraction / 1000000)); } #endif } break; @@ -503,12 +505,12 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if(len == SQL_NULL_DATA) { - //return scope.Close(Null()); - return Null(); + return scope.Close(Null()); + //return Null(); } else { - //return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); - return Boolean::New(( *buffer == '0') ? false : true ); + return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); + //return Boolean::New(( *buffer == '0') ? false : true ); } default : Local str; @@ -527,8 +529,8 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); if(len == SQL_NULL_DATA) { - //return scope.Close(Null()); - return Null(); + return scope.Close(Null()); + //return Null(); } if (ret != SQL_NO_DATA) { @@ -559,7 +561,8 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } } while (true); - return str; + return scope.Close(str); + //return str; } } From b318cf6db95f89efe87a806944e072ea1ce679c5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 18 Jul 2013 18:04:22 -0400 Subject: [PATCH 322/511] 0.5.23 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cb2ca2cb..3ae52877 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.22", + "version": "0.5.23", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 74c87245bd890a8d413b7620a9ffa78a6fcb5aa3 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 22 Jul 2013 17:46:07 -0400 Subject: [PATCH 323/511] test: add test for domains --- test/test-domains-open.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/test-domains-open.js diff --git a/test/test-domains-open.js b/test/test-domains-open.js new file mode 100644 index 00000000..042e7afc --- /dev/null +++ b/test/test-domains-open.js @@ -0,0 +1,19 @@ +var domain = require("domain"); + +var d = domain.create(); + +d.on("error", function (error) { + console.log("Error caught!", error); +}); + +d.run(function() { + var db = require("../")(); + + console.trace(); + + db.open("wrongConnectionString", function (error) { + console.trace(); + + throw new Error(); + }); +}); From 7c9e081444d27999a4f952874264bb1ced325f79 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 22 Jul 2013 23:54:19 -0400 Subject: [PATCH 324/511] test: enclose column names in double quotes so they are case sensitive in postgres --- test/test-param-select-with-booleans-only.js | 2 +- test/test-param-select-with-decimals-only.js | 2 +- test/test-param-select-with-null.js | 2 +- test/test-param-select-with-nulls-mixed.js | 2 +- test/test-query-select.js | 2 +- test/test-querySync-select.js | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/test-param-select-with-booleans-only.js b/test/test-param-select-with-booleans-only.js index 94b27bf4..487322cd 100644 --- a/test/test-param-select-with-booleans-only.js +++ b/test/test-param-select-with-booleans-only.js @@ -7,7 +7,7 @@ var common = require("./common") db.open(common.connectionString, function (err) { assert.equal(err, null); - db.query("select ? as TRUECOL, ? as FALSECOL " + db.query("select ? as \"TRUECOL\", ? as \"FALSECOL\" " , [true, false] , function (err, data, more) { db.close(function () { diff --git a/test/test-param-select-with-decimals-only.js b/test/test-param-select-with-decimals-only.js index 35df7d99..2b186db6 100644 --- a/test/test-param-select-with-decimals-only.js +++ b/test/test-param-select-with-decimals-only.js @@ -7,7 +7,7 @@ var common = require("./common") db.open(common.connectionString, function (err) { assert.equal(err, null); - db.query("select ? as DECCOL1 " + db.query("select ? as \"DECCOL1\" " , [5.5] , function (err, data, more) { db.close(function () { diff --git a/test/test-param-select-with-null.js b/test/test-param-select-with-null.js index d1e68584..5c26a026 100644 --- a/test/test-param-select-with-null.js +++ b/test/test-param-select-with-null.js @@ -7,7 +7,7 @@ var common = require("./common") db.open(common.connectionString, function (err) { assert.equal(err, null); - db.query("select ? as NULLCOL1 " + db.query("select ? as \"NULLCOL1\" " , [null] , function (err, data, more) { if (err) { console.error(err) } diff --git a/test/test-param-select-with-nulls-mixed.js b/test/test-param-select-with-nulls-mixed.js index efc04220..4f1026e0 100644 --- a/test/test-param-select-with-nulls-mixed.js +++ b/test/test-param-select-with-nulls-mixed.js @@ -7,7 +7,7 @@ var common = require("./common") db.open(common.connectionString, function (err) { assert.equal(err, null); - db.query("select ? as TEXTCOL1, ? as TEXTCOL2, ? as NULLCOL1 " + db.query("select ? as \"TEXTCOL1\", ? as \"TEXTCOL2\", ? as \"NULLCOL1\" " , ["something", "something", null] , function (err, data, more) { if (err) { console.error(err) } diff --git a/test/test-query-select.js b/test/test-query-select.js index 8109c40b..078dbf51 100644 --- a/test/test-query-select.js +++ b/test/test-query-select.js @@ -5,7 +5,7 @@ var common = require("./common") ; db.open(common.connectionString, function(err) { - db.query("select 1 as COLINT, 'some test' as COLTEXT", function (err, data) { + db.query("select 1 as \"COLINT\", 'some test' as \"COLTEXT\"", function (err, data) { db.close(function () { assert.equal(err, null); assert.deepEqual(data, [{ COLINT: '1', COLTEXT: 'some test' }]); diff --git a/test/test-querySync-select.js b/test/test-querySync-select.js index 75fd8b7b..16dc00c1 100644 --- a/test/test-querySync-select.js +++ b/test/test-querySync-select.js @@ -8,9 +8,9 @@ db.open(common.connectionString, function(err) { assert.equal(err, null); assert.equal(db.connected, true); - var data = db.querySync("select 1 as COLINT, 'some test' as COLTEXT "); + var data = db.querySync("select 1 as \"COLINT\", 'some test' as \"COLTEXT\""); db.close(function () { - assert.deepEqual(data, [{ COLINT: '1', COLTEXT: 'some test' }]); + assert.deepEqual(data, [{ COLINT: 1, COLTEXT: 'some test' }]); }); }); From 5ec560691f0d1401e35671d126c33c083fcc0316 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Jul 2013 12:21:18 -0400 Subject: [PATCH 325/511] Add support for setting connectTimeout. Closes #50. Set the connectTimeout at runtime by passing and options object to the Database constructor function: var db = require('odbc')({ connectTimeout : 60 }); or var odbc = require('odbc'); var db = new odbc.Database({ connectTimeout : 42 }); --- lib/odbc.js | 9 ++++ src/odbc.cpp | 7 +++ src/odbc_connection.cpp | 58 +++++++++++++++++-------- src/odbc_connection.h | 5 ++- test/test-binding-connection-timeOut.js | 31 +++++++++++++ test/test-open-connectTimeout.js | 24 ++++++++++ 6 files changed, 116 insertions(+), 18 deletions(-) create mode 100644 test/test-binding-connection-timeOut.js create mode 100644 test/test-open-connectTimeout.js diff --git a/lib/odbc.js b/lib/odbc.js index cb38908e..b3503ea6 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -54,6 +54,7 @@ function Database(options) { self.queue = new SimpleQueue(); self.fetchMode = options.fetchMode || null; self.connected = false; + self.connectTimeout = options.connectTimeout || null; } //Expose constants @@ -84,6 +85,10 @@ Database.prototype.open = function (connectionString, cb) { self.conn = conn; + if (self.connectTimeout) { + self.conn.connectTimeout = self.connectTimeout; + } + self.conn.open(connectionString, function (err, result) { if (err) return cb(err); @@ -99,6 +104,10 @@ Database.prototype.openSync = function (connectionString) { self.conn = self.odbc.createConnectionSync(); + if (self.connectTimeout) { + self.conn.connectTimeout = self.connectTimeout; + } + if (typeof(connectionString) == "object") { var obj = connectionString; connectionString = ""; diff --git a/src/odbc.cpp b/src/odbc.cpp index fb0c7476..ce913169 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -198,6 +198,8 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { create_connection_work_data* data = (create_connection_work_data *)(req->data); + TryCatch try_catch; + if (!SQL_SUCCEEDED(data->result)) { Local args[1]; @@ -219,6 +221,11 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { data->cb->Call(Context::GetCurrent()->Global(), 2, args); } + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->dbo->Unref(); data->cb.Dispose(); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 27a66a65..f24c9b39 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -52,6 +52,7 @@ void ODBCConnection::Init(v8::Handle target) { // Properties //instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); instance_template->SetAccessor(String::New("connected"), ConnectedGetter); + instance_template->SetAccessor(String::New("connectTimeout"), ConnectTimeoutGetter, ConnectTimeoutSetter); // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open); @@ -114,6 +115,9 @@ Handle ODBCConnection::New(const Arguments& args) { conn->Wrap(args.Holder()); + //set default connectTimeout to 5 seconds + conn->connectTimeout = 5; + return scope.Close(args.Holder()); } @@ -125,6 +129,24 @@ Handle ODBCConnection::ConnectedGetter(Local property, const Acce return scope.Close(obj->connected ? True() : False()); } +Handle ODBCConnection::ConnectTimeoutGetter(Local property, const AccessorInfo &info) { + HandleScope scope; + + ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + + return scope.Close(Number::New(obj->connectTimeout)); +} + +void ODBCConnection::ConnectTimeoutSetter(Local property, Local value, const AccessorInfo &info) { + HandleScope scope; + + ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + + if (value->IsNumber()) { + obj->connectTimeout = value->Int32Value(); + } +} + /* * Open * @@ -182,15 +204,16 @@ void ODBCConnection::UV_Open(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); - //TODO: make this configurable - int timeOut = 5; + int timeOut = self->connectTimeout; - //NOTE: SQLSetConnectAttr requires the thread to be locked - SQLSetConnectAttr( - self->m_hDBC, //ConnectionHandle - SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &timeOut, //ValuePtr - sizeof(timeOut)); //StringLength + if (timeOut > 0) { + //NOTE: SQLSetConnectAttr requires the thread to be locked + SQLSetConnectAttr( + self->m_hDBC, //ConnectionHandle + SQL_ATTR_LOGIN_TIMEOUT, //Attribute + &timeOut, //ValuePtr + sizeof(timeOut)); //StringLength + } //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked @@ -306,16 +329,17 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { uv_mutex_lock(&ODBC::g_odbcMutex); - //TODO: make this configurable - int timeOut = 5; - - //NOTE: SQLSetConnectAttr requires the thread to be locked - SQLSetConnectAttr( - conn->m_hDBC, //ConnectionHandle - SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &timeOut, //ValuePtr - sizeof(timeOut)); //StringLength + int timeOut = conn->connectTimeout; + if (timeOut > 0) { + //NOTE: SQLSetConnectAttr requires the thread to be locked + SQLSetConnectAttr( + conn->m_hDBC, //ConnectionHandle + SQL_ATTR_LOGIN_TIMEOUT, //Attribute + &timeOut, //ValuePtr + sizeof(timeOut)); //StringLength + } + //Attempt to connect //NOTE: SQLDriverConnect requires the thread to be locked ret = SQLDriverConnect( diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 28cbf3fc..d5af05ee 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -44,7 +44,9 @@ class ODBCConnection : public node::ObjectWrap { //Property Getter/Setters static Handle ConnectedGetter(Local property, const AccessorInfo &info); - + static Handle ConnectTimeoutGetter(Local property, const AccessorInfo &info); + static void ConnectTimeoutSetter(Local property, Local value, const AccessorInfo &info); + //async methods // static Handle BeginTransaction(const Arguments& args); // static void UV_BeginTransaction(uv_work_t* work_req); @@ -98,6 +100,7 @@ class ODBCConnection : public node::ObjectWrap { SQLUSMALLINT canHaveMoreResults; bool connected; int statements; + int connectTimeout; }; struct create_statement_work_data { diff --git a/test/test-binding-connection-timeOut.js b/test/test-binding-connection-timeOut.js new file mode 100644 index 00000000..fe70086d --- /dev/null +++ b/test/test-binding-connection-timeOut.js @@ -0,0 +1,31 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.ODBC() + , assert = require("assert") + , exitCode = 0 + ; + +db.createConnection(function (err, conn) { + //connectionTimeout should be 5 by default as set in C++ + assert.equal(conn.connectTimeout, 5); + + //test the setter and getter + conn.connectTimeout = 1234; + assert.equal(conn.connectTimeout, 1234); + + //set the time out to something small + conn.connectTimeout = 1; + assert.equal(conn.connectTimeout, 1); + + conn.open(common.connectionString, function (err) { + //TODO: it would be nice if we could somehow + //force a timeout to occurr, but most testing is + //done locally and it's hard to get a local server + //to not accept a connection within one second... + + console.log(err); + conn.close(function () { + + }); + }); +}); diff --git a/test/test-open-connectTimeout.js b/test/test-open-connectTimeout.js new file mode 100644 index 00000000..75857aa9 --- /dev/null +++ b/test/test-open-connectTimeout.js @@ -0,0 +1,24 @@ +var common = require("./common") + , odbc = require("../") + , assert = require("assert"); + +//test setting connectTimeout via the constructor works +var db = new odbc.Database({ connectTimeout : 1 }) + +db.open(common.connectionString, function(err) { + assert.equal(db.conn.connectTimeout, 1); + + assert.equal(err, null); + assert.equal(db.connected, true); + + db.close(function () { + assert.equal(db.connected, false); + + db.query("select * from " + common.tableName, function (err, rs, moreResultSets) { + assert.deepEqual(err, { message: 'Connection not open.' }); + assert.deepEqual(rs, []); + assert.equal(moreResultSets, false); + assert.equal(db.connected, false); + }); + }); +}); From 5dd584191998b9967e61d91aaf38712992774529 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Jul 2013 12:22:17 -0400 Subject: [PATCH 326/511] 0.5.24 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3ae52877..a9071345 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.23", + "version": "0.5.24", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 77ed2e8c370e5a53c392b9465eb69a89513dc042 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 23 Jul 2013 12:28:16 -0400 Subject: [PATCH 327/511] Allow connectTimeout of zero --- lib/odbc.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index b3503ea6..4d6c7324 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -85,7 +85,7 @@ Database.prototype.open = function (connectionString, cb) { self.conn = conn; - if (self.connectTimeout) { + if (self.connectTimeout || self.connectTimeout === 0) { self.conn.connectTimeout = self.connectTimeout; } @@ -104,7 +104,7 @@ Database.prototype.openSync = function (connectionString) { self.conn = self.odbc.createConnectionSync(); - if (self.connectTimeout) { + if (self.connectTimeout || self.connectTimeout === 0) { self.conn.connectTimeout = self.connectTimeout; } From 3bc0625635befb3f94fd611e24f176da7131b8f6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Jul 2013 11:29:25 -0400 Subject: [PATCH 328/511] Conditionally use SQL_[W]LONGVARCHAR or SQL_[W]VARCHAR based on length of parameter --- src/odbc.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index ce913169..bd8438dd 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -641,13 +641,14 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { if (value->IsString()) { Local string = value->ToString(); - + int length = string->Length(); + params[i].c_type = SQL_C_TCHAR; #ifdef UNICODE - params[i].type = SQL_WLONGVARCHAR; - params[i].buffer_length = (string->Length() * sizeof(uint16_t)) + sizeof(uint16_t); + params[i].type = (length >= 8000) ? SQL_WLONGVARCHAR : SQL_WVARCHAR; + params[i].buffer_length = (length * sizeof(uint16_t)) + sizeof(uint16_t); #else - params[i].type = SQL_LONGVARCHAR; + params[i].type = (length >= 8000) ? SQL_LONGVARCHAR : SQL_VARCHAR; params[i].buffer_length = string->Utf8Length() + 1; #endif params[i].buffer = malloc(params[i].buffer_length); From 963121b0ab65acb24558d889f683d001a08dac84 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Jul 2013 11:34:06 -0400 Subject: [PATCH 329/511] Make sure async callbacks are wrapped in TryCatch --- src/odbc_connection.cpp | 13 +++++-- src/odbc_result.cpp | 26 +++++++++++-- src/odbc_statement.cpp | 83 +++++++++++++++++++---------------------- 3 files changed, 71 insertions(+), 51 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index f24c9b39..e5119ee5 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -282,7 +282,7 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { uv_ref(uv_default_loop()); #endif } - + TryCatch try_catch; data->conn->Unref(); @@ -635,7 +635,14 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { args[0] = Local::New(Null()); args[1] = Local::New(js_result); + + TryCatch try_catch; + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } data->conn->Unref(); data->cb.Dispose(); @@ -859,6 +866,8 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { query_work_data* data = (query_work_data *)(req->data); + TryCatch try_catch; + //check to see if there was an error during execution if(data->result == SQL_ERROR) { ODBC::CallbackSQLError( @@ -901,8 +910,6 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { data->cb->Call(Context::GetCurrent()->Global(), 2, args); } - TryCatch try_catch; - data->conn->Unref(); if (try_catch.HasCaught()) { diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 00d571ba..d6ebb65c 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -275,9 +275,15 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { data->objResult->buffer, data->objResult->bufferLength); } - + + TryCatch try_catch; + data->cb->Call(Context::GetCurrent()->Global(), 2, args); data->cb.Dispose(); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } } else { ODBC::FreeColumns(data->objResult->columns, &data->objResult->colCount); @@ -293,9 +299,15 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { } args[1] = Null(); - + + TryCatch try_catch; + data->cb->Call(Context::GetCurrent()->Global(), 2, args); data->cb.Dispose(); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } } free(data); @@ -543,12 +555,18 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { } args[1] = Local::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); diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index f4be6266..6c475e5d 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -223,18 +223,17 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { args[0] = Local::New(Null()); args[1] = Local::New(js_result); - + + TryCatch try_catch; + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } } - - TryCatch try_catch; - + self->Unref(); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - data->cb.Dispose(); free(data); @@ -362,18 +361,17 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { args[0] = Local::New(Null()); args[1] = Local::New(Number::New(rowCount)); - + + TryCatch try_catch; + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } } - - TryCatch try_catch; - + self->Unref(); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - data->cb.Dispose(); free(data); @@ -514,18 +512,17 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { args[0] = Local::New(Null()); args[1] = Local::New(js_result); - + + TryCatch try_catch; + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } } - - TryCatch try_catch; - + self->Unref(); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - data->cb.Dispose(); free(data->sql); @@ -723,18 +720,17 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { args[0] = Local::New(Null()); args[1] = Local::New(True()); - + + TryCatch try_catch; + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } } - TryCatch try_catch; - data->stmt->Unref(); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - data->cb.Dispose(); free(data->sql); @@ -986,18 +982,17 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { args[0] = Local::New(Null()); args[1] = Local::New(True()); - + + TryCatch try_catch; + data->cb->Call(Context::GetCurrent()->Global(), 2, args); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } } - - TryCatch try_catch; - + self->Unref(); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - data->cb.Dispose(); free(data); From bcf3d4b71cbeee3b1e12fead1c797aab0420b317 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Jul 2013 12:22:57 -0400 Subject: [PATCH 330/511] test: replace open() and close() calls with openSync() and closeSync() Only made the replacement in places where we are not explicitly testing the open() or close() functionality. The reason behind this is that MySQL has a bug which causes most of the tests to time out because a thread is not released if a connection is opened and closed from two different threads. This tends to occur when we are using the async calls because the ODBC open/close call occurs on the thread pool. And we can't guarantee that it will be on the same thread. The only way to guarantee ODBC open/close is called from the same thread is if we call it from the main thread. In order to do that, we must use the Sync versions of open() or close() --- test/test-binding-statement-executeSync.js | 132 +++++++++--------- test/test-binding-statement-rebinding.js | 82 +++++------ test/test-binding-transaction-commit.js | 81 ++++++----- test/test-closed.js | 16 --- test/test-describe-column.js | 42 +++--- test/test-describe-database.js | 22 ++- test/test-describe-table.js | 26 ++-- test/test-prepare-bind-execute-closeSync.js | 17 +-- test/test-prepare-bind-execute-error.js | 18 +-- test/test-prepare-bind-execute-long-string.js | 19 +-- ...test-prepare-bindSync-execute-closeSync.js | 16 +-- test/test-prepare.js | 33 +++-- test/test-prepareSync-multiple-execution.js | 37 ++--- test/test-prepareSync.js | 36 +++-- test/test-query-create-table-fetchSync.js | 34 +++-- test/test-query-create-table.js | 12 +- test/test-query-drop-table.js | 13 +- test/test-query-select-fetch.js | 20 ++- test/test-query-select-fetchAll.js | 27 ++-- test/test-query-select-fetchAllSync.js | 29 ++-- test/test-query-select-fetchMode-array.js | 17 ++- test/test-query-select-unicode.js | 16 +-- test/test-query-select.js | 14 +- test/test-querySync-select-unicode.js | 28 ++-- test/test-querySync-select-with-execption.js | 32 ++--- test/test-querySync-select.js | 19 ++- test/test-transaction-commit-sync.js | 82 +++++------ 27 files changed, 420 insertions(+), 500 deletions(-) diff --git a/test/test-binding-statement-executeSync.js b/test/test-binding-statement-executeSync.js index 20500a37..e546b587 100644 --- a/test/test-binding-statement-executeSync.js +++ b/test/test-binding-statement-executeSync.js @@ -6,76 +6,76 @@ var common = require("./common") ; db.createConnection(function (err, conn) { - conn.open(common.connectionString, function (err) { - conn.createStatement(function (err, stmt) { - var r, result, caughtError; + conn.openSync(common.connectionString); + + conn.createStatement(function (err, stmt) { + var r, result, caughtError; + + //try excuting without preparing or binding. + try { + result = stmt.executeSync(); + } + catch (e) { + caughtError = e; + } + + try { + assert.ok(caughtError); + } + catch (e) { + console.log(e.message); + exitCode = 1; + } + + //try incorrectly binding a string and then executeSync + try { + r = stmt.bind("select 1 + 1 as col1"); + } + catch (e) { + caughtError = e; + } + + try { + assert.equal(caughtError.message, "Argument 1 must be an Array"); - //try excuting without preparing or binding. - try { - result = stmt.executeSync(); - } - catch (e) { - caughtError = e; - } + r = stmt.prepareSync("select 1 + ? as col1"); + assert.equal(r, true, "prepareSync did not return true"); - try { - assert.ok(caughtError); - } - catch (e) { - console.log(e.message); - exitCode = 1; - } + r = stmt.bindSync([2]); + assert.equal(r, true, "bindSync did not return true"); - //try incorrectly binding a string and then executeSync - try { - r = stmt.bind("select 1 + 1 as col1"); - } - catch (e) { - caughtError = e; - } + result = stmt.executeSync(); + assert.equal(result.constructor.name, "ODBCResult"); - try { - assert.equal(caughtError.message, "Argument 1 must be an Array"); - - r = stmt.prepareSync("select 1 + ? as col1"); - assert.equal(r, true, "prepareSync did not return true"); - - r = stmt.bindSync([2]); - assert.equal(r, true, "bindSync did not return true"); - - result = stmt.executeSync(); - assert.equal(result.constructor.name, "ODBCResult"); - - r = result.fetchAllSync(); - assert.deepEqual(r, [ { col1: 3 } ]); - - r = result.closeSync(); - assert.equal(r, true, "closeSync did not return true"); - - result = stmt.executeSync(); - assert.equal(result.constructor.name, "ODBCResult"); - - r = result.fetchAllSync(); - assert.deepEqual(r, [ { col1: 3 } ]); - - console.log(r); - } - catch (e) { - console.log(e); - - exitCode = 1; - } + r = result.fetchAllSync(); + assert.deepEqual(r, [ { col1: 3 } ]); - conn.close(function () { - if (exitCode) { - console.log("failed"); - } - else { - console.log("success"); - } - - process.exit(exitCode); - }); - }); + r = result.closeSync(); + assert.equal(r, true, "closeSync did not return true"); + + result = stmt.executeSync(); + assert.equal(result.constructor.name, "ODBCResult"); + + r = result.fetchAllSync(); + assert.deepEqual(r, [ { col1: 3 } ]); + + console.log(r); + } + catch (e) { + console.log(e); + + exitCode = 1; + } + + conn.closeSync(); + + if (exitCode) { + console.log("failed"); + } + else { + console.log("success"); + } + + process.exit(exitCode); }); }); diff --git a/test/test-binding-statement-rebinding.js b/test/test-binding-statement-rebinding.js index 6f14a5ea..73789f3b 100644 --- a/test/test-binding-statement-rebinding.js +++ b/test/test-binding-statement-rebinding.js @@ -12,46 +12,46 @@ var common = require("./common") */ db.createConnection(function (err, conn) { - conn.open(common.connectionString, function (err) { - conn.createStatement(function (err, stmt) { - var r, result, caughtError; - - var a = ['hello', 'world']; - - stmt.prepareSync('select ? as col1, ? as col2'); - - stmt.bindSync(a); - - result = stmt.executeSync(); - - console.log(result.fetchAllSync()); - result.closeSync(); - - a[0] = 'goodbye'; - a[1] = 'steven'; - - result = stmt.executeSync(); - - r = result.fetchAllSync(); - - try { - assert.deepEqual(r, [ { col1: 'goodbye', col2: 'steven' } ]); - } - catch (e) { - console.log(e); - exitCode = 1; - } - - conn.close(function () { - if (exitCode) { - console.log("failed"); - } - else { - console.log("success"); - } - - process.exit(exitCode); - }); - }); + conn.openSync(common.connectionString); + + conn.createStatement(function (err, stmt) { + var r, result, caughtError; + + var a = ['hello', 'world']; + + stmt.prepareSync('select ? as col1, ? as col2'); + + stmt.bindSync(a); + + result = stmt.executeSync(); + + console.log(result.fetchAllSync()); + result.closeSync(); + + a[0] = 'goodbye'; + a[1] = 'steven'; + + result = stmt.executeSync(); + + r = result.fetchAllSync(); + + try { + assert.deepEqual(r, [ { col1: 'goodbye', col2: 'steven' } ]); + } + catch (e) { + console.log(e); + exitCode = 1; + } + + conn.closeSync(); + + if (exitCode) { + console.log("failed"); + } + else { + console.log("success"); + } + + process.exit(exitCode); }); }); diff --git a/test/test-binding-transaction-commit.js b/test/test-binding-transaction-commit.js index fa06991a..dd7409b3 100644 --- a/test/test-binding-transaction-commit.js +++ b/test/test-binding-transaction-commit.js @@ -6,49 +6,48 @@ var common = require("./common") ; db.createConnection(function (err, conn) { - conn.open(common.connectionString, function (err) { - common.createTables(conn, function (err, data) { - try { - conn.beginTransactionSync(); - - var result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + conn.openSync(common.connectionString); + + common.createTables(conn, function (err, data) { + try { + conn.beginTransactionSync(); + + var result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); - conn.endTransactionSync(true); //rollback - - result = conn.querySync("select * from " + common.tableName); - - assert.deepEqual(result.fetchAllSync(), []); - } - catch (e) { - console.log("Failed when rolling back"); - console.log(e); - exitCode = 1 - } - - try { - //Start a new transaction - conn.beginTransactionSync(); - - result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + conn.endTransactionSync(true); //rollback + + result = conn.querySync("select * from " + common.tableName); + + assert.deepEqual(result.fetchAllSync(), []); + } + catch (e) { + console.log("Failed when rolling back"); + console.log(e); + exitCode = 1 + } + + try { + //Start a new transaction + conn.beginTransactionSync(); + + result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); - conn.endTransactionSync(false); //commit - - result = conn.querySync("select * from " + common.tableName); - - assert.deepEqual(result.fetchAllSync(), [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); - } - catch (e) { - console.log("Failed when committing"); - console.log(e); - - exitCode = 2; - } - - common.dropTables(conn, function (err) { - conn.close(function () { - process.exit(exitCode); - }); - }); + conn.endTransactionSync(false); //commit + + result = conn.querySync("select * from " + common.tableName); + + assert.deepEqual(result.fetchAllSync(), [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); + } + catch (e) { + console.log("Failed when committing"); + console.log(e); + + exitCode = 2; + } + + common.dropTables(conn, function (err) { + conn.closeSync(); + process.exit(exitCode); }); }); }); diff --git a/test/test-closed.js b/test/test-closed.js index 2a7ff5d4..155685e1 100644 --- a/test/test-closed.js +++ b/test/test-closed.js @@ -12,19 +12,3 @@ db.query("select * from test", function (err, rs, moreResultSets) { assert.equal(moreResultSets, false); assert.equal(db.connected, false); }); - -db.open(common.connectionString, function(err) { - assert.equal(err, null); - assert.equal(db.connected, true); - - db.close(function () { - assert.equal(db.connected, false); - - db.query("select * from test", function (err, rs, moreResultSets) { - assert.deepEqual(err, { message: 'Connection not open.' }); - assert.deepEqual(rs, []); - assert.equal(moreResultSets, false); - assert.equal(db.connected, false); - }); - }); -}); diff --git a/test/test-describe-column.js b/test/test-describe-column.js index 091b5a1d..1cbe6759 100644 --- a/test/test-describe-column.js +++ b/test/test-describe-column.js @@ -4,37 +4,31 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - if (err) console.log(err); - - assert.equal(err, null); +db.openSync(common.connectionString); + +console.log("connected"); + +common.dropTables(db, function (err) { + if (err) console.log(err.message); - console.log("connected"); + console.log("tables dropped"); - common.dropTables(db, function (err) { + common.createTables(db, function (err) { if (err) console.log(err.message); - - console.log("tables dropped"); - common.createTables(db, function (err) { + console.log("tables created"); + + db.describe({ + database : common.databaseName, + table : common.tableName, + column : 'COLDATETIME' + }, function (err, data) { if (err) console.log(err.message); - console.log("tables created"); + console.log(data); - db.describe({ - database : common.databaseName, - table : common.tableName, - column : 'COLDATETIME' - }, function (err, data) { - if (err) console.log(err.message); - - console.log(data); - - db.close(function () { - assert.equal(err, null); - assert.ok(data.length, "No records returned when attempting to describe the column COLDATETIME"); - }); - }); + db.closeSync(); + assert.ok(data.length, "No records returned when attempting to describe the column COLDATETIME"); }); }); }); diff --git a/test/test-describe-database.js b/test/test-describe-database.js index a5201905..a715864f 100644 --- a/test/test-describe-database.js +++ b/test/test-describe-database.js @@ -4,19 +4,15 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - assert.equal(err, null); +db.openSync(common.connectionString); - common.dropTables(db, function () { - common.createTables(db, function () { - db.describe({ - database : common.databaseName - }, function (err, data) { - db.close(function () { - assert.equal(err, null); - assert.ok(data.length, "No records returned when attempting to describe the database"); - }); - }); +common.dropTables(db, function () { + common.createTables(db, function () { + db.describe({ + database : common.databaseName + }, function (err, data) { + db.closeSync(); + assert.ok(data.length, "No records returned when attempting to describe the database"); }); }); -}); \ No newline at end of file +}); diff --git a/test/test-describe-table.js b/test/test-describe-table.js index 00fbc220..23b8495b 100644 --- a/test/test-describe-table.js +++ b/test/test-describe-table.js @@ -4,21 +4,17 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - assert.equal(err, null); +db.openSync(common.connectionString); - common.dropTables(db, function () { - common.createTables(db, function () { - - db.describe({ - database : common.databaseName - , table : common.tableName - }, function (err, data) { - db.close(function () { - assert.equal(err, null); - assert.ok(data.length, "No records returned when attempting to describe the tabe " + common.tableName); - }); - }); +common.dropTables(db, function () { + common.createTables(db, function () { + + db.describe({ + database : common.databaseName + , table : common.tableName + }, function (err, data) { + db.closeSync(); + assert.ok(data.length, "No records returned when attempting to describe the tabe " + common.tableName); }); }); -}); \ No newline at end of file +}); diff --git a/test/test-prepare-bind-execute-closeSync.js b/test/test-prepare-bind-execute-closeSync.js index abaa95fa..f7c74d7c 100644 --- a/test/test-prepare-bind-execute-closeSync.js +++ b/test/test-prepare-bind-execute-closeSync.js @@ -5,17 +5,13 @@ var common = require("./common") , iterations = 1000 ; -db.open(common.connectionString, function(err){ - if (err) { - console.error(err); - process.exit(1); - } +db.openSync(common.connectionString); - issueQuery3(function () { - finish(); - }); +issueQuery3(function () { + finish(); }); + function issueQuery3(done) { var count = 0 , time = new Date().getTime(); @@ -61,7 +57,6 @@ function issueQuery3(done) { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.closeSync(); + console.log("connection closed"); } diff --git a/test/test-prepare-bind-execute-error.js b/test/test-prepare-bind-execute-error.js index e6637738..e76af75b 100644 --- a/test/test-prepare-bind-execute-error.js +++ b/test/test-prepare-bind-execute-error.js @@ -4,14 +4,8 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err){ - if (err) { - console.error(err); - process.exit(1); - } - - issueQuery(); -}); +db.openSync(common.connectionString); +issueQuery(); function issueQuery() { var count = 0 @@ -47,9 +41,9 @@ function issueQuery() { } function finish(exitCode) { - db.close(function () { - console.log("connection closed"); + db.closeSync(); + + console.log("connection closed"); - process.exit(exitCode || 0); - }); + process.exit(exitCode || 0); } diff --git a/test/test-prepare-bind-execute-long-string.js b/test/test-prepare-bind-execute-long-string.js index 6a321512..56bd90f0 100644 --- a/test/test-prepare-bind-execute-long-string.js +++ b/test/test-prepare-bind-execute-long-string.js @@ -4,14 +4,8 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err){ - if (err) { - console.error(err); - process.exit(1); - } - - issueQuery(); -}); +db.openSync(common.connectionString); +issueQuery(); function issueQuery() { var count = 0 @@ -62,9 +56,8 @@ function issueQuery() { } function finish(exitCode) { - db.close(function () { - console.log("connection closed"); - - process.exit(exitCode || 0); - }); + db.closeSync(); + + console.log("connection closed"); + process.exit(exitCode || 0); } diff --git a/test/test-prepare-bindSync-execute-closeSync.js b/test/test-prepare-bindSync-execute-closeSync.js index 2bd0cc72..d2c74ca3 100644 --- a/test/test-prepare-bindSync-execute-closeSync.js +++ b/test/test-prepare-bindSync-execute-closeSync.js @@ -5,15 +5,10 @@ var common = require("./common") , iterations = 100 ; -db.open(common.connectionString, function(err){ - if (err) { - console.error(err); - process.exit(1); - } +db.openSync(common.connectionString); - issueQuery3(function () { - finish(); - }); +issueQuery3(function () { + finish(); }); function issueQuery3(done) { @@ -46,7 +41,6 @@ function issueQuery3(done) { } function finish() { - db.close(function () { - console.log("connection closed"); - }); + db.closeSync(); + console.log("connection closed"); } diff --git a/test/test-prepare.js b/test/test-prepare.js index 0ea0294d..5b11dd45 100644 --- a/test/test-prepare.js +++ b/test/test-prepare.js @@ -4,32 +4,31 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { +db.openSync(common.connectionString); + +assert.equal(db.connected, true); + +db.prepare("select ? as col1", function (err, stmt) { assert.equal(err, null); - assert.equal(db.connected, true); + assert.equal(stmt.constructor.name, "ODBCStatement"); - db.prepare("select ? as col1", function (err, stmt) { + stmt.bind(["hello world"], function (err) { assert.equal(err, null); - assert.equal(stmt.constructor.name, "ODBCStatement"); - stmt.bind(["hello world"], function (err) { + stmt.execute(function (err, result) { assert.equal(err, null); + assert.equal(result.constructor.name, "ODBCResult"); - stmt.execute(function (err, result) { + result.fetchAll(function (err, data) { assert.equal(err, null); - assert.equal(result.constructor.name, "ODBCResult"); + console.log(data); - result.fetchAll(function (err, data) { - assert.equal(err, null); - console.log(data); - - result.closeSync(); - - db.close(function () { - assert.deepEqual(data, [{ col1: "hello world" }]); - }); - }); + result.closeSync(); + + db.closeSync(); + assert.deepEqual(data, [{ col1: "hello world" }]); }); }); }); }); + diff --git a/test/test-prepareSync-multiple-execution.js b/test/test-prepareSync-multiple-execution.js index 8f2f5dce..144c894c 100644 --- a/test/test-prepareSync-multiple-execution.js +++ b/test/test-prepareSync-multiple-execution.js @@ -7,37 +7,28 @@ var common = require("./common") var count = 0; var iterations = 10; -db.open(common.connectionString, function(err) { - if(err) { - console.log(err) - - return finish(1); - } +db.openSync(common.connectionString); - common.dropTables(db, function () { - common.createTables(db, function (err, data) { - if (err) { - console.log(err); - - return finish(2); - } - - var stmt = db.prepareSync("insert into " + common.tableName + " (colint, coltext) VALUES (?, ?)"); - assert.equal(stmt.constructor.name, "ODBCStatement"); +common.dropTables(db, function () { + common.createTables(db, function (err, data) { + if (err) { + console.log(err); - recursive(stmt); - }); + return finish(2); + } + + var stmt = db.prepareSync("insert into " + common.tableName + " (colint, coltext) VALUES (?, ?)"); + assert.equal(stmt.constructor.name, "ODBCStatement"); + + recursive(stmt); }); }); function finish(retValue) { console.log("finish exit value: %s", retValue); - db.close(function (err) { - if (err) console.log (err); - - process.exit(retValue || 0); - }); + db.closeSync(); + process.exit(retValue || 0); } function recursive (stmt) { diff --git a/test/test-prepareSync.js b/test/test-prepareSync.js index 0fd6210a..8e83d54a 100644 --- a/test/test-prepareSync.js +++ b/test/test-prepareSync.js @@ -4,28 +4,26 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - assert.equal(err, null); - assert.equal(db.connected, true); +db.openSync(common.connectionString); +assert.equal(db.connected, true); + +var stmt = db.prepareSync("select ? as col1, ? as col2, ? as col3"); +assert.equal(stmt.constructor.name, "ODBCStatement"); - var stmt = db.prepareSync("select ? as col1, ? as col2, ? as col3"); - assert.equal(stmt.constructor.name, "ODBCStatement"); +stmt.bindSync(["hello world", 1, null]); - stmt.bindSync(["hello world", 1, null]); - - stmt.execute(function (err, result) { +stmt.execute(function (err, result) { + assert.equal(err, null); + assert.equal(result.constructor.name, "ODBCResult"); + + result.fetchAll(function (err, data) { assert.equal(err, null); - assert.equal(result.constructor.name, "ODBCResult"); + console.log(data); + + result.closeSync(); - result.fetchAll(function (err, data) { - assert.equal(err, null); - console.log(data); - - result.closeSync(); - - db.close(function () { - assert.deepEqual(data, [{ col1: "hello world", col2 : 1, col3 : null }]); - }); - }); + db.closeSync(); + assert.deepEqual(data, [{ col1: "hello world", col2 : 1, col3 : null }]); }); }); + diff --git a/test/test-query-create-table-fetchSync.js b/test/test-query-create-table-fetchSync.js index 8cfa3a9c..61d0d226 100644 --- a/test/test-query-create-table-fetchSync.js +++ b/test/test-query-create-table-fetchSync.js @@ -4,22 +4,20 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - db.queryResult("create table " + common.tableName + " (COLINT INTEGER, COLDATETIME DATETIME, COLTEXT TEXT)", function (err, result) { - console.log(arguments); - - try { - //this should throw because there was no result to be had? - var data = result.fetchAllSync(); - console.log(data); - } - catch (e) { - console.log(e); - } - - - db.close(function () { - - }); - }); +db.openSync(common.connectionString); + +db.queryResult("create table " + common.tableName + " (COLINT INTEGER, COLDATETIME DATETIME, COLTEXT TEXT)", function (err, result) { + console.log(arguments); + + try { + //this should throw because there was no result to be had? + var data = result.fetchAllSync(); + console.log(data); + } + catch (e) { + console.log(e); + } + + db.closeSync(); }); + diff --git a/test/test-query-create-table.js b/test/test-query-create-table.js index b5711134..bc3f7a26 100644 --- a/test/test-query-create-table.js +++ b/test/test-query-create-table.js @@ -4,11 +4,9 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - common.createTables(db, function (err, data, morefollowing) { - console.log(arguments); - db.close(function () { - - }); - }); +db.openSync(common.connectionString); +common.createTables(db, function (err, data, morefollowing) { + console.log(arguments); + db.closeSync(); }); + diff --git a/test/test-query-drop-table.js b/test/test-query-drop-table.js index f4612f27..376ea1d9 100644 --- a/test/test-query-drop-table.js +++ b/test/test-query-drop-table.js @@ -4,11 +4,10 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - common.dropTables(db, function (err, data) { - db.close(function () { - assert.equal(err, null); - assert.deepEqual(data, []); - }); - }); +db.openSync(common.connectionString); +common.dropTables(db, function (err, data) { + db.closeSync(); + assert.equal(err, null); + assert.deepEqual(data, []); }); + diff --git a/test/test-query-select-fetch.js b/test/test-query-select-fetch.js index 40628b9e..0fc356f5 100644 --- a/test/test-query-select-fetch.js +++ b/test/test-query-select-fetch.js @@ -4,18 +4,16 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { +db.openSync(common.connectionString); +assert.equal(db.connected, true); + +db.queryResult("select 1 as COLINT, 'some test' as COLTEXT ", function (err, result) { assert.equal(err, null); - assert.equal(db.connected, true); + assert.equal(result.constructor.name, "ODBCResult"); - db.queryResult("select 1 as COLINT, 'some test' as COLTEXT ", function (err, result) { - assert.equal(err, null); - assert.equal(result.constructor.name, "ODBCResult"); - - result.fetch(function (err, data) { - db.close(function () { - assert.deepEqual(data, { COLINT: '1', COLTEXT: 'some test' }); - }); - }); + result.fetch(function (err, data) { + db.closeSync(); + assert.deepEqual(data, { COLINT: '1', COLTEXT: 'some test' }); }); }); + diff --git a/test/test-query-select-fetchAll.js b/test/test-query-select-fetchAll.js index 100b7273..12450968 100644 --- a/test/test-query-select-fetchAll.js +++ b/test/test-query-select-fetchAll.js @@ -4,21 +4,20 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { +db.openSync(common.connectionString); + +assert.equal(db.connected, true); + +db.queryResult("select 1 as COLINT, 'some test' as COLTEXT union select 2, 'something else' ", function (err, result) { assert.equal(err, null); - assert.equal(db.connected, true); + assert.equal(result.constructor.name, "ODBCResult"); - db.queryResult("select 1 as COLINT, 'some test' as COLTEXT union select 2, 'something else' ", function (err, result) { - assert.equal(err, null); - assert.equal(result.constructor.name, "ODBCResult"); - - result.fetchAll(function (err, data) { - db.close(function () { - assert.deepEqual(data, [ - {"COLINT":1,"COLTEXT":"some test"} - ,{"COLINT":2,"COLTEXT":"something else"} - ]); - }); - }); + result.fetchAll(function (err, data) { + db.closeSync(); + assert.deepEqual(data, [ + {"COLINT":1,"COLTEXT":"some test"} + ,{"COLINT":2,"COLTEXT":"something else"} + ]); }); }); + diff --git a/test/test-query-select-fetchAllSync.js b/test/test-query-select-fetchAllSync.js index 405e6217..68eaa57e 100644 --- a/test/test-query-select-fetchAllSync.js +++ b/test/test-query-select-fetchAllSync.js @@ -4,21 +4,20 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { +db.openSync(common.connectionString); + +assert.equal(db.connected, true); + +db.queryResult("select 1 as COLINT, 'some test' as COLTEXT union select 2, 'something else' ", function (err, result) { assert.equal(err, null); - assert.equal(db.connected, true); + assert.equal(result.constructor.name, "ODBCResult"); - db.queryResult("select 1 as COLINT, 'some test' as COLTEXT union select 2, 'something else' ", function (err, result) { - assert.equal(err, null); - assert.equal(result.constructor.name, "ODBCResult"); - - var data = result.fetchAllSync(); - - db.close(function () { - assert.deepEqual(data, [ - {"COLINT":1,"COLTEXT":"some test"} - ,{"COLINT":2,"COLTEXT":"something else"} - ]); - }); - }); + var data = result.fetchAllSync(); + + db.closeSync(); + assert.deepEqual(data, [ + {"COLINT":1,"COLTEXT":"some test"} + ,{"COLINT":2,"COLTEXT":"something else"} + ]); }); + diff --git a/test/test-query-select-fetchMode-array.js b/test/test-query-select-fetchMode-array.js index f42f9897..33cb72b3 100644 --- a/test/test-query-select-fetchMode-array.js +++ b/test/test-query-select-fetchMode-array.js @@ -4,15 +4,14 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { +db.openSync(common.connectionString); + +assert.equal(db.connected, true); + +db.query("select 1 as COLINT, 'some test' as COLTEXT ", function (err, data) { assert.equal(err, null); - assert.equal(db.connected, true); - db.query("select 1 as COLINT, 'some test' as COLTEXT ", function (err, data) { - assert.equal(err, null); - - db.close(function () { - assert.deepEqual(data, [[1,"some test"]]); - }); - }); + db.closeSync(); + assert.deepEqual(data, [[1,"some test"]]); }); + diff --git a/test/test-query-select-unicode.js b/test/test-query-select-unicode.js index afb2522d..751b4644 100644 --- a/test/test-query-select-unicode.js +++ b/test/test-query-select-unicode.js @@ -4,12 +4,12 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - db.query("select '☯ąčęėįšųūž☎áäàéêèóöòüßÄÖÜ€ шчябы Ⅲ ❤' as UNICODETEXT", function (err, data) { - db.close(function () { - console.log(data); - assert.equal(err, null); - assert.deepEqual(data, [{ UNICODETEXT: '☯ąčęėįšųūž☎áäàéêèóöòüßÄÖÜ€ шчябы Ⅲ ❤' }]); - }); - }); +db.openSync(common.connectionString); + +db.query("select '☯ąčęėįšųūž☎áäàéêèóöòüßÄÖÜ€ шчябы Ⅲ ❤' as UNICODETEXT", function (err, data) { + db.closeSync(); + console.log(data); + assert.equal(err, null); + assert.deepEqual(data, [{ UNICODETEXT: '☯ąčęėįšųūž☎áäàéêèóöòüßÄÖÜ€ шчябы Ⅲ ❤' }]); }); + diff --git a/test/test-query-select.js b/test/test-query-select.js index 078dbf51..39916d1b 100644 --- a/test/test-query-select.js +++ b/test/test-query-select.js @@ -4,11 +4,11 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - db.query("select 1 as \"COLINT\", 'some test' as \"COLTEXT\"", function (err, data) { - db.close(function () { - assert.equal(err, null); - assert.deepEqual(data, [{ COLINT: '1', COLTEXT: 'some test' }]); - }); - }); +db.openSync(common.connectionString); + +db.query("select 1 as \"COLINT\", 'some test' as \"COLTEXT\"", function (err, data) { + db.closeSync(); + assert.equal(err, null); + assert.deepEqual(data, [{ COLINT: '1', COLTEXT: 'some test' }]); }); + diff --git a/test/test-querySync-select-unicode.js b/test/test-querySync-select-unicode.js index 09a9dad8..bcd4166f 100644 --- a/test/test-querySync-select-unicode.js +++ b/test/test-querySync-select-unicode.js @@ -4,17 +4,17 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - var data; - try { - data = db.querySync("select 'ꜨꜢ' as UNICODETEXT"); - } - catch (e) { - console.log(e); - } - - db.close(function () { - console.log(data); - assert.deepEqual(data, [{ UNICODETEXT: 'ꜨꜢ' }]); - }); -}); +db.openSync(common.connectionString); +var data; + +try { + data = db.querySync("select 'ꜨꜢ' as UNICODETEXT"); +} +catch (e) { + console.log(e); +} + +db.closeSync(); +console.log(data); +assert.deepEqual(data, [{ UNICODETEXT: 'ꜨꜢ' }]); + diff --git a/test/test-querySync-select-with-execption.js b/test/test-querySync-select-with-execption.js index 6690771f..2bd2015b 100644 --- a/test/test-querySync-select-with-execption.js +++ b/test/test-querySync-select-with-execption.js @@ -4,21 +4,19 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - assert.equal(err, null); - assert.equal(db.connected, true); - var err = null; - - try { - var data = db.querySync("select invalid query"); - } - catch (e) { - console.log(e); - - err = e; - } +db.openSync(common.connectionString); +assert.equal(db.connected, true); +var err = null; + +try { + var data = db.querySync("select invalid query"); +} +catch (e) { + console.log(e); - db.close(function () { - assert.equal(err.error, "[node-odbc] Error in ODBCConnection::QuerySync"); - }); -}); + err = e; +} + +db.closeSync(); +assert.equal(err.error, "[node-odbc] Error in ODBCConnection::QuerySync"); + diff --git a/test/test-querySync-select.js b/test/test-querySync-select.js index 16dc00c1..7f14fa3c 100644 --- a/test/test-querySync-select.js +++ b/test/test-querySync-select.js @@ -4,13 +4,12 @@ var common = require("./common") , assert = require("assert") ; -db.open(common.connectionString, function(err) { - assert.equal(err, null); - assert.equal(db.connected, true); - - var data = db.querySync("select 1 as \"COLINT\", 'some test' as \"COLTEXT\""); - - db.close(function () { - assert.deepEqual(data, [{ COLINT: 1, COLTEXT: 'some test' }]); - }); -}); +db.openSync(common.connectionString); +assert.equal(db.connected, true); + +var data = db.querySync("select 1 as \"COLINT\", 'some test' as \"COLTEXT\""); + +db.closeSync(); +assert.deepEqual(data, [{ COLINT: 1, COLTEXT: 'some test' }]); + + diff --git a/test/test-transaction-commit-sync.js b/test/test-transaction-commit-sync.js index fe91069f..3d992382 100644 --- a/test/test-transaction-commit-sync.js +++ b/test/test-transaction-commit-sync.js @@ -6,49 +6,49 @@ var common = require("./common") ; -db.open(common.connectionString, function (err) { - common.createTables(db, function (err, data) { - try { - db.beginTransactionSync(); - - var results = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); +db.openSync(common.connectionString); - db.rollbackTransactionSync(); - - results = db.querySync("select * from " + common.tableName); - - assert.deepEqual(results, []); - } - catch (e) { - console.log("Failed when rolling back"); - console.log(e); - exitCode = 1 - } - - try { - //Start a new transaction - db.beginTransactionSync(); - - result = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); +common.createTables(db, function (err, data) { + try { + db.beginTransactionSync(); + + var results = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + + db.rollbackTransactionSync(); + + results = db.querySync("select * from " + common.tableName); + + assert.deepEqual(results, []); + } + catch (e) { + console.log("Failed when rolling back"); + console.log(e); + exitCode = 1 + } + + try { + //Start a new transaction + db.beginTransactionSync(); + + result = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); - db.commitTransactionSync(); //commit - - result = db.querySync("select * from " + common.tableName); - - assert.deepEqual(result, [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); - } - catch (e) { - console.log("Failed when committing"); - console.log(e); - - exitCode = 2; - } - - common.dropTables(db, function (err) { - db.close(function () { - process.exit(exitCode); - }); - }); + db.commitTransactionSync(); //commit + + result = db.querySync("select * from " + common.tableName); + + assert.deepEqual(result, [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); + } + catch (e) { + console.log("Failed when committing"); + console.log(e); + + exitCode = 2; + } + + common.dropTables(db, function (err) { + db.closeSync(); + process.exit(exitCode); }); }); + From df643669c623988f21f9dc34c2166c7cc1fe2a42 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 24 Jul 2013 12:31:15 -0400 Subject: [PATCH 331/511] 0.5.25 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9071345..16d9032b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.24", + "version": "0.5.25", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 87846d34b8e81019588cf9d69f75a6f768b405e6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 25 Jul 2013 10:53:08 -0400 Subject: [PATCH 332/511] Check to see if 'cb' exists in 'close()' before calling it --- lib/odbc.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 4d6c7324..8f721d23 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -134,13 +134,13 @@ Database.prototype.close = function (cb) { self.connected = false; delete self.conn; - cb(err); + if (cb) cb(err); return next(); }); }); }; -Database.prototype.closeSync = function (cb) { +Database.prototype.closeSync = function () { var self = this; var result = self.conn.closeSync(); From c7ca27ef95f0365d25c6c3b9f461cdd31758ff9a Mon Sep 17 00:00:00 2001 From: Roger Hoover Date: Sun, 1 Sep 2013 21:00:37 -0700 Subject: [PATCH 333/511] Attempt to handle INVALID_HANDLE errors --- src/odbc.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index bd8438dd..f956e95c 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -540,7 +540,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //return Null(); } - if (ret != SQL_NO_DATA) { + if (SQL_NO_DATA == ret) { + //we have captured all of the data + break; + } + else if (SQL_SUCCEEDED(ret)) { //we have not captured all of the data yet if (count == 0) { @@ -563,7 +567,22 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, count += 1; } else { - //we have captured all of the data + //an error has occured + char *errorMessage = "ODBC::GetColumnValue - String: - ERROR"; + DEBUG_PRINTF("%s\n", errorMessage); + + Local objError = Object::New(); + objError->SetPrototype(Exception::Error(String::New(errorMessage))); + objError->Set(String::New("message"), String::New(errorMessage)); + objError->Set(String::New("state"), String::New("FUCKED UP")); + + return ThrowException(scope.Close(objError)); + // ThrowException(ODBC::GetSQLError( + // SQL_HANDLE_STMT, + // hStmt, + // (char *) "[node-odbc] Error in ODBC::GetColumnValue" + // )); + break; } } while (true); From f03997ca24bdd71731def2134bbe062a069c21b6 Mon Sep 17 00:00:00 2001 From: Roger Hoover Date: Tue, 3 Sep 2013 10:23:17 -0700 Subject: [PATCH 334/511] Added some comments --- src/odbc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index f956e95c..c24f0260 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -568,9 +568,12 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } else { //an error has occured + //possible values for ret are SQL_ERROR (-1) and SQL_INVALID_HANDLE (-2) char *errorMessage = "ODBC::GetColumnValue - String: - ERROR"; DEBUG_PRINTF("%s\n", errorMessage); + + //What's the right way to handle errors here???? Local objError = Object::New(); objError->SetPrototype(Exception::Error(String::New(errorMessage))); objError->Set(String::New("message"), String::New(errorMessage)); From ccceb527b83a3dce3610af87a202b34b89804169 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 3 Sep 2013 15:07:05 -0400 Subject: [PATCH 335/511] Add test for pull request issue --- test/test-issue-get-column-value-2.js | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/test-issue-get-column-value-2.js diff --git a/test/test-issue-get-column-value-2.js b/test/test-issue-get-column-value-2.js new file mode 100644 index 00000000..90de0bba --- /dev/null +++ b/test/test-issue-get-column-value-2.js @@ -0,0 +1,41 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + , util = require('util') + , count = 0 + ; + +var getSchema = function () { + var db = new odbc.Database(); + + console.log(util.format('Count %s, time %s', count, new Date())); + console.log(db); + + db.open(common.connectionString, function(err) { + if (err) { + console.error("connection error: ", err.message); + db.close(function(){}); + return; + } + + db.describe({database: 'main', schema: 'RETAIL', table: 'PURCHASES'}, function (err, rows) { + if (err) { + console.error("describe error: ", err.message); + db.close(function(){}); + return; + } + // console.log(rows); + db.close(function() { + console.log("Connection Closed"); + db = null; + count += 1; + if (count < 100) { + setImmediate(getSchema); + } + }); + }); + }); +}; + +getSchema(); \ No newline at end of file From 0e3fbe00919fa58bb61e0be720cf6345c7049c00 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 3 Sep 2013 15:48:53 -0400 Subject: [PATCH 336/511] Close result objects in columns and tables proxy functions --- lib/odbc.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/odbc.js b/lib/odbc.js index 8f721d23..ea023d5a 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -336,6 +336,8 @@ Database.prototype.columns = function(catalog, schema, table, column, callback) if (err) return callback(err, [], false); result.fetchAll(function (err, data) { + result.closeSync(); + callback(err, data); return next(); @@ -355,6 +357,8 @@ Database.prototype.tables = function(catalog, schema, table, type, callback) { if (err) return callback(err, [], false); result.fetchAll(function (err, data) { + result.closeSync(); + callback(err, data); return next(); From 49d9016606b8f06c3f2cc93d0ff8673fe7b69626 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 3 Sep 2013 15:49:17 -0400 Subject: [PATCH 337/511] Add a debug message --- src/odbc_connection.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index e5119ee5..1de13859 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -868,6 +868,8 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { TryCatch try_catch; + DEBUG_PRINTF("ODBCConnection::UV_AfterQuery : data->result=%i, data->noResultObject=%i\n", data->result, data->noResultObject); + //check to see if there was an error during execution if(data->result == SQL_ERROR) { ODBC::CallbackSQLError( From 0ccc315c61f2d658026867481f866f97ca994f72 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 3 Sep 2013 15:49:51 -0400 Subject: [PATCH 338/511] Add an assertion that return value from SQLGetData must not be -2 --- src/odbc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index bd8438dd..9d1dbb56 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -532,6 +532,8 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, bufferLength, &len); + assert(ret != -2); + DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); From 750f5983fbbc72853afba2bf8887d4481fe6b2c5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 3 Sep 2013 15:52:13 -0400 Subject: [PATCH 339/511] Updated most recent issue test --- test/test-issue-get-column-value-2.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/test-issue-get-column-value-2.js b/test/test-issue-get-column-value-2.js index 90de0bba..6fb36aa3 100644 --- a/test/test-issue-get-column-value-2.js +++ b/test/test-issue-get-column-value-2.js @@ -10,7 +10,7 @@ var getSchema = function () { var db = new odbc.Database(); console.log(util.format('Count %s, time %s', count, new Date())); - console.log(db); + //console.log(db); db.open(common.connectionString, function(err) { if (err) { @@ -19,13 +19,14 @@ var getSchema = function () { return; } - db.describe({database: 'main', schema: 'RETAIL', table: 'PURCHASES'}, function (err, rows) { + db.describe({database: 'main', schema: 'RETAIL', table: common.tableName }, function (err, rows) { +// db.query("select * from " + common.tableName, function (err, rows) { if (err) { console.error("describe error: ", err.message); db.close(function(){}); return; } - // console.log(rows); + db.close(function() { console.log("Connection Closed"); db = null; @@ -33,6 +34,9 @@ var getSchema = function () { if (count < 100) { setImmediate(getSchema); } + else { + process.exit(0); + } }); }); }); From 05bdbf3a73ec20c811fa2d202be9093892f0397d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 3 Sep 2013 16:25:25 -0400 Subject: [PATCH 340/511] Move assertion to inside error handling and attempt to throw an error object This could be bad because are in some cases expecting to pass the error object back as an argument to the callback function. Other times it might be ok to actually throw. At any case, this should be a super rare error to hit. --- src/odbc.cpp | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 0a39944e..61d19497 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -532,8 +532,6 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, bufferLength, &len); - assert(ret != -2); - DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); @@ -571,22 +569,20 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, else { //an error has occured //possible values for ret are SQL_ERROR (-1) and SQL_INVALID_HANDLE (-2) - char *errorMessage = "ODBC::GetColumnValue - String: - ERROR"; - DEBUG_PRINTF("%s\n", errorMessage); - - - //What's the right way to handle errors here???? - Local objError = Object::New(); - objError->SetPrototype(Exception::Error(String::New(errorMessage))); - objError->Set(String::New("message"), String::New(errorMessage)); - objError->Set(String::New("state"), String::New("FUCKED UP")); - - return ThrowException(scope.Close(objError)); - // ThrowException(ODBC::GetSQLError( - // SQL_HANDLE_STMT, - // hStmt, - // (char *) "[node-odbc] Error in ODBC::GetColumnValue" - // )); + + //If we have an invalid handle, then stuff is way bad and we should abort + //immediately. Memory errors are bound to follow as we must be in an + //inconsisant state. + assert(ret != SQL_INVALID_HANDLE); + + //Not sure if throwing here will work out well for us but we can try + //since we should have a valid handle and the error is something we + //can look into + return ThrowException(ODBC::GetSQLError( + SQL_HANDLE_STMT, + hStmt, + (char *) "[node-odbc] Error in ODBC::GetColumnValue" + )); break; } From 24a08a721f3454066cea80c7d12a8ed8275e81c5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 3 Sep 2013 16:26:13 -0400 Subject: [PATCH 341/511] 0.5.26 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 16d9032b..aff2e30f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.25", + "version": "0.5.26", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 7b9e8d553c53ff9fdd744a2a67fc2327032594e7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 9 Sep 2013 15:06:07 -0400 Subject: [PATCH 342/511] Add SQLGetDiagField to dynodbc - untested --- src/dynodbc.cpp | 2 ++ src/dynodbc.h | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/dynodbc.cpp b/src/dynodbc.cpp index 924e5cf4..58c3e5fa 100644 --- a/src/dynodbc.cpp +++ b/src/dynodbc.cpp @@ -74,6 +74,7 @@ pfnSQLExecDirect pSQLExecDirect; pfnSQLExecute pSQLExecute; pfnSQLFetch pSQLFetch; pfnSQLGetDiagRec pSQLGetDiagRec; +pfnSQLGetDiagField pSQLGetDiagField; pfnSQLFreeHandle pSQLFreeHandle; pfnSQLFetchScroll pSQLFetchScroll; pfnSQLColAttribute pSQLColAttribute; @@ -157,6 +158,7 @@ BOOL DynLoadODBC( char* odbcModuleName ) if (LOAD_ENTRY( hMod, SQLExecute ) ) if (LOAD_ENTRY( hMod, SQLFetch ) ) if (LOAD_ENTRY( hMod, SQLGetDiagRec ) ) + if (LOAD_ENTRY( hMod, SQLGetDiagField ) ) if (LOAD_ENTRY( hMod, SQLFreeHandle ) ) //Unused-> if (LOAD_ENTRY( hMod, SQLFetchScroll ) ) if (LOAD_ENTRY( hMod, SQLColAttribute ) ) diff --git a/src/dynodbc.h b/src/dynodbc.h index d608f25c..352dd03f 100644 --- a/src/dynodbc.h +++ b/src/dynodbc.h @@ -186,6 +186,12 @@ typedef RETCODE (SQL_API * pfnSQLGetDiagRec)( SQLINTEGER *NativeError, SQLCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLength); +typedef RETCODE (SQL_API * pfnSQLGetDiagField)( + SQLSMALLINT HandleType, SQLHANDLE Handle, + SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, + SQLPOINTER DiagInfoPtr, SQLSMALLINT BufferLength, + SQLSMALLINT *StringLengthPtr + typedef RETCODE (SQL_API * pfnSQLFreeHandle)( SQLSMALLINT HandleType, SQLHANDLE Handle); @@ -302,6 +308,7 @@ extern pfnSQLExecDirect pSQLExecDirect; extern pfnSQLExecute pSQLExecute; extern pfnSQLFetch pSQLFetch; extern pfnSQLGetDiagRec pSQLGetDiagRec; +extern pfnSQLGetDiagField pSQLGetDiagField; extern pfnSQLFreeHandle pSQLFreeHandle; extern pfnSQLFetchScroll pSQLFetchScroll; extern pfnSQLFetchScroll pSQLFetchScroll; @@ -347,6 +354,7 @@ BOOL DynLoadODBC( char* odbcModuleName ); #define SQLPrepare pSQLPrepare #define SQLExecute pSQLExecute #define SQLGetDiagRec pSQLGetDiagRec +#define SQLGetDiagField pSQLGetDiagField #define SQLFreeHandle pSQLFreeHandle #define SQLFreeStmt pSQLFreeStmt #define SQLFetchScroll pSQLFetchScroll From 2c8f408b6178c373899fd0bb89a2350f8b18b9e6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 9 Sep 2013 15:06:22 -0400 Subject: [PATCH 343/511] 0.5.27 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aff2e30f..74756b94 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.26", + "version": "0.5.27", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From c70ca8a08cc437d42e38363c904f12537cdef86f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 9 Sep 2013 15:22:21 -0400 Subject: [PATCH 344/511] Make dynodbc.h compile in dynodbc mode with SQLTCHARS and other such modifications' --- src/dynodbc.h | 50 +++++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/dynodbc.h b/src/dynodbc.h index 352dd03f..083e18aa 100644 --- a/src/dynodbc.h +++ b/src/dynodbc.h @@ -68,9 +68,9 @@ typedef RETCODE (SQL_API * pfnSQLSetConnectAttr)( typedef RETCODE (SQL_API * pfnSQLDriverConnect)( SQLHDBC hdbc, SQLHWND hwnd, - SQLCHAR *szConnStrIn, + SQLTCHAR *szConnStrIn, SQLSMALLINT cbConnStrIn, - SQLCHAR *szConnStrOut, + SQLTCHAR *szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT *pcbConnStrOut, SQLUSMALLINT fDriverCompletion); @@ -93,21 +93,22 @@ typedef RETCODE (SQL_API * pfnSQLEndTran)( typedef RETCODE (SQL_API * pfnSQLExecDirect)( SQLHSTMT StatementHandle, - SQLCHAR *StatementText, SQLINTEGER TextLength); + SQLTCHAR *StatementText, SQLINTEGER TextLength); + typedef RETCODE (SQL_API * pfnSQLTables)( SQLHSTMT StatementHandle, - SQLCHAR *CatalogName, SQLSMALLINT NameLength1, - SQLCHAR *SchemaName, SQLSMALLINT NameLength2, - SQLCHAR *TableName, SQLSMALLINT NameLength3, - SQLCHAR *TableType, SQLSMALLINT NameLength4); + SQLTCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLTCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLTCHAR *TableName, SQLSMALLINT NameLength3, + SQLTCHAR *TableType, SQLSMALLINT NameLength4); typedef RETCODE (SQL_API * pfnSQLColumns)( SQLHSTMT StatementHandle, - SQLCHAR *CatalogName, SQLSMALLINT NameLength1, - SQLCHAR *SchemaName, SQLSMALLINT NameLength2, - SQLCHAR *TableName, SQLSMALLINT NameLength3, - SQLCHAR *ColumnName, SQLSMALLINT NameLength4); + SQLTCHAR *CatalogName, SQLSMALLINT NameLength1, + SQLTCHAR *SchemaName, SQLSMALLINT NameLength2, + SQLTCHAR *TableName, SQLSMALLINT NameLength3, + SQLTCHAR *ColumnName, SQLSMALLINT NameLength4); typedef RETCODE (SQL_API * pfnSQLBindParameter)( SQLHSTMT hstmt, @@ -123,11 +124,11 @@ typedef RETCODE (SQL_API * pfnSQLBindParameter)( typedef RETCODE (SQL_API * pfnSQLPrimaryKeys)( SQLHSTMT hstmt, - SQLCHAR *szCatalogName, + SQLTCHAR *szCatalogName, SQLSMALLINT cbCatalogName, - SQLCHAR *szSchemaName, + SQLTCHAR *szSchemaName, SQLSMALLINT cbSchemaName, - SQLCHAR *szTableName, + SQLTCHAR *szTableName, SQLSMALLINT cbTableName); typedef RETCODE (SQL_API * pfnSQLSetEnvAttr)( @@ -169,11 +170,11 @@ typedef RETCODE (SQL_API * pfnSQLError)( SWORD cbErrorMsgMax, SWORD FAR *pcbErrorMsg); -typedef RETCODE (SQL_API * pfnSQLExecDirect)( +/*typedef RETCODE (SQL_API * pfnSQLExecDirect)( HSTMT hstmt, UCHAR FAR *szSqlStr, SDWORD cbSqlStr); - +*/ typedef RETCODE (SQL_API * pfnSQLExecute)( HSTMT hstmt); @@ -182,15 +183,15 @@ typedef RETCODE (SQL_API * pfnSQLFetch)( typedef RETCODE (SQL_API * pfnSQLGetDiagRec)( SQLSMALLINT HandleType, SQLHANDLE Handle, - SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, - SQLINTEGER *NativeError, SQLCHAR *MessageText, + SQLSMALLINT RecNumber, SQLTCHAR *Sqlstate, + SQLINTEGER *NativeError, SQLTCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLength); typedef RETCODE (SQL_API * pfnSQLGetDiagField)( SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, SQLPOINTER DiagInfoPtr, SQLSMALLINT BufferLength, - SQLSMALLINT *StringLengthPtr + SQLSMALLINT *StringLengthPtr); typedef RETCODE (SQL_API * pfnSQLFreeHandle)( SQLSMALLINT HandleType, SQLHANDLE Handle); @@ -227,9 +228,12 @@ typedef RETCODE (SQL_API * pfnSQLNumResultCols)( SWORD FAR *pccol); typedef RETCODE (SQL_API * pfnSQLPrepare)( - HSTMT hstmt, - UCHAR FAR *szSqlStr, - SDWORD cbSqlStr); + SQLHSTMT StatementHandle, + SQLTCHAR *StatementText, + SQLINTEGER TextLength); +// HSTMT hstmt, +// UCHAR FAR *szSqlStr, +// SDWORD cbSqlStr); typedef RETCODE (SQL_API * pfnSQLRowCount)( HSTMT hstmt, @@ -319,7 +323,7 @@ extern pfnSQLAllocHandle pSQLAllocHandle; extern pfnSQLRowCount pSQLRowCount; extern pfnSQLNumResultCols pSQLNumResultCols; extern pfnSQLEndTran pSQLEndTran; -extern pfnSQLExecDirect pSQLExecDirect; +//extern pfnSQLExecDirect pSQLExecDirect; extern pfnSQLTables pSQLTables; extern pfnSQLColumns pSQLColumns; // extern pfnSQLBindParameter pSQLBindParameter; From 949c3d3be489b040015096fe085864a6bbbe71ac Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 9 Sep 2013 15:22:33 -0400 Subject: [PATCH 345/511] 0.5.28 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 74756b94..28b35df7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.27", + "version": "0.5.28", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From e344eb99e6a0e5bc34fb05a44f4a7675bf4ae232 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 10 Sep 2013 12:37:10 -0400 Subject: [PATCH 346/511] Add new test for #53 --- test/test-prepareSync-bad-sql.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 test/test-prepareSync-bad-sql.js diff --git a/test/test-prepareSync-bad-sql.js b/test/test-prepareSync-bad-sql.js new file mode 100644 index 00000000..eaf6cf84 --- /dev/null +++ b/test/test-prepareSync-bad-sql.js @@ -0,0 +1,24 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.openSync(common.connectionString); +assert.equal(db.connected, true); + +var stmt = db.prepareSync("asdf asdf asdf asdf sadf "); +assert.equal(stmt.constructor.name, "ODBCStatement"); + +stmt.bindSync(["hello world", 1, null]); + +stmt.execute(function (err, result) { + assert.ok(err); + + stmt.executeNonQuery(function (err, count) { + assert.ok(err); + + db.close(function () {}); + }); +}); + From e97fa311c4153bede00a7bd6d2ffc32c10134165 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 10 Sep 2013 13:19:50 -0400 Subject: [PATCH 347/511] Add an open method which returns a new Database instance and an open connection --- lib/odbc.js | 15 +++++++++++++++ test/test-global-open-close.js | 11 +++++++++++ 2 files changed, 26 insertions(+) create mode 100644 test/test-global-open-close.js diff --git a/lib/odbc.js b/lib/odbc.js index ea023d5a..32e9ebd2 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -33,6 +33,21 @@ module.exports.ODBCStatement = odbc.ODBCStatement; module.exports.ODBCResult = odbc.ODBCResult; module.exports.loadODBCLibrary = odbc.loadODBCLibrary; +module.exports.open = function (connectionString, options, cb) { + var db; + + if (typeof options === 'function') { + cb = options; + options = null; + } + + db = new Database(options); + + db.open(connectionString, function (err) { + cb(err, db); + }); +} + function Database(options) { var self = this; diff --git a/test/test-global-open-close.js b/test/test-global-open-close.js new file mode 100644 index 00000000..7bb5f2f3 --- /dev/null +++ b/test/test-global-open-close.js @@ -0,0 +1,11 @@ +var common = require("./common") + , odbc = require("../") + , assert = require("assert"); + +odbc.open(common.connectionString, function (err, conn) { + assert.equal(err, null); + assert.equal(conn.constructor.name, 'Database'); + + conn.close(); +}); + From 944c928ea6d53b096434d91342a760565be5cab4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 10 Sep 2013 13:20:04 -0400 Subject: [PATCH 348/511] 0.5.29 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 28b35df7..1212886d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.28", + "version": "0.5.29", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From b5efe4cb9e355597d30dbab2471a95684382a7c5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 17 Sep 2013 16:31:12 -0400 Subject: [PATCH 349/511] Add local path for macs in binding.gyp. Fixes #52 --- binding.gyp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/binding.gyp b/binding.gyp index 4181ecdf..cb1e8ddb 100644 --- a/binding.gyp +++ b/binding.gyp @@ -22,7 +22,8 @@ ] }], [ 'OS == "mac"', { - 'libraries' : [ + 'libraries' : [ + '-L/usr/local/lib', '-lodbc' ] }], From 332a4c4174ab33d7e0595a444635b50d5daf779c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 17 Sep 2013 16:32:04 -0400 Subject: [PATCH 350/511] 0.5.30 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1212886d..0ac3fac1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.29", + "version": "0.5.30", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From 8387ff33f573cef2aaf27e4e6372bdb9f9374ee7 Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Fri, 27 Sep 2013 13:10:51 +0100 Subject: [PATCH 351/511] Handle errors returned by SQLMoreResults --- src/odbc.cpp | 4 +++- src/odbc.h | 12 ++++++++++++ src/odbc_result.cpp | 7 +++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 61d19497..911a6711 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -834,7 +834,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* DEBUG_PRINTF("ODBC::GetSQLError : after SQLGetDiagRec; i=%i\n", i); if (SQL_SUCCEEDED(ret)) { - DEBUG_PRINTF("ODBC::GetSQLError : errorMessage=%s, errorSQLState=%s\n", errorMessage, errorSQLState); + DEBUG_TPRINTF(SQL_T("ODBC::GetSQLError : errorMessage=%s, errorSQLState=%s\n"), errorMessage, errorSQLState); objError->Set(String::New("error"), String::New(message)); #ifdef UNICODE @@ -846,6 +846,8 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* objError->Set(String::New("message"), String::New(errorMessage)); objError->Set(String::New("state"), String::New(errorSQLState)); #endif + } else if (ret == SQL_NO_DATA) { + break; } } diff --git a/src/odbc.h b/src/odbc.h index 268074ba..33bdb378 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -145,10 +145,22 @@ struct query_request { int result; }; +#ifdef UNICODE + #define SQL_T(x) (L##x) +#else + #define SQL_T(x) (x) +#endif + #ifdef DEBUG #define DEBUG_PRINTF(...) fprintf(stdout, __VA_ARGS__) + #ifdef UNICODE + #define DEBUG_TPRINTF(...) fwprintf(stdout, __VA_ARGS__) + #else + #define DEBUG_TPRINTF(...) fprintf(stdout, __VA_ARGS__) + #endif #else #define DEBUG_PRINTF(...) (void)0 + #define DEBUG_TPRINTF(...) (void)0 #endif #define REQ_ARGS(N) \ diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index d6ebb65c..883678ef 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -721,7 +721,10 @@ Handle ODBCResult::MoreResultsSync(const Arguments& args) { SQLRETURN ret = SQLMoreResults(result->m_hSTMT); - return scope.Close(SQL_SUCCEEDED(ret) ? True() : False()); + if (ret == SQL_ERROR) + return scope.Close(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync")); + + return scope.Close(SQL_SUCCEEDED(ret) || ret == SQL_ERROR ? True() : False()); } /* @@ -747,4 +750,4 @@ Handle ODBCResult::GetColumnNamesSync(const Arguments& args) { } return scope.Close(cols); -} \ No newline at end of file +} From 9289c6a56e66480968a40426e0b7f6972e0f5cf9 Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Fri, 27 Sep 2013 13:25:46 +0100 Subject: [PATCH 352/511] Handle error returned by moreResultsSync() Handle error returned by moreResultsSync() in Database.query. Return the error as a separate result set. --- lib/odbc.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/odbc.js b/lib/odbc.js index 32e9ebd2..50c146cc 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -202,7 +202,15 @@ Database.prototype.query = function (sql, params, cb) { result.closeSync(); } - cb(err, data, moreResults); + cb(err, data, !!moreResults); + + if (typeof moreResults === "object") { + err = moreResults; + moreResults = result.moreResultsSync(); + cb(err, null, moreResults); + + return fetchMore(); + } if (moreResults) { return fetchMore(); From 0b0bccc7bac082c3035eba0e6a2be5221bb2cefd Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 27 Sep 2013 16:44:23 -0400 Subject: [PATCH 353/511] Fixup pull request for issue #54 Added test for the issue which is lacking assertions ThrowException instead of returning the error object Keep looping and emitting errors if encountered when calling moreResultsSync() --- lib/odbc.js | 32 ++++++++++++++++++++++++-------- src/odbc_result.cpp | 5 +++-- test/test-issue-54.js | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 test/test-issue-54.js diff --git a/lib/odbc.js b/lib/odbc.js index 50c146cc..8a677089 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -194,7 +194,16 @@ Database.prototype.query = function (sql, params, cb) { } result.fetchAll(function (err, data) { - var moreResults = result.moreResultsSync(); + var moreResults, moreResultsError = null; + + try { + moreResults = result.moreResultsSync(); + } + catch (e) { + moreResultsError = e; + //force to check for more results + moreResults = true; + } //close the result before calling back //if there are not more result sets @@ -202,14 +211,21 @@ Database.prototype.query = function (sql, params, cb) { result.closeSync(); } - cb(err, data, !!moreResults); + cb(err, data, moreResults); - if (typeof moreResults === "object") { - err = moreResults; - moreResults = result.moreResultsSync(); - cb(err, null, moreResults); - - return fetchMore(); + while (moreResultsError) { + try { + moreResults = result.moreResultsSync(); + } + catch (e) { + //call back with the previous error + cb(moreResultsError, [], moreResults); + moreResultsError = e; + } + finally { + cb(moreResultsError, [], moreResults); + moreResultsError = null; + } } if (moreResults) { diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 883678ef..c83d3677 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -721,8 +721,9 @@ Handle ODBCResult::MoreResultsSync(const Arguments& args) { SQLRETURN ret = SQLMoreResults(result->m_hSTMT); - if (ret == SQL_ERROR) - return scope.Close(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync")); + if (ret == SQL_ERROR) { + ThrowException(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync")); + } return scope.Close(SQL_SUCCEEDED(ret) || ret == SQL_ERROR ? True() : False()); } diff --git a/test/test-issue-54.js b/test/test-issue-54.js new file mode 100644 index 00000000..4dedbad1 --- /dev/null +++ b/test/test-issue-54.js @@ -0,0 +1,38 @@ +//NOTE: this does not assert anything that it should, please fix. + +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + , util = require('util') + , count = 0 + ; + +var sql = +"declare @t table (x int); \ +insert @t values (1); \ +select 'You will get this message' \ +raiserror('You will never get this error!', 16, 100); \ +raiserror('Two errors in a row! WHAT?', 16, 100); \ +select 'You will never get this message, either!' as msg; \ +" + +db.open(common.connectionString, function(err) { + console.log(err || "Connected") + + if (!err) { + db.query(sql, function (err, results, more) { + console.log("q1 result: ", err, results, more) + + if (!more) { + console.log("Running second query") + + db.query("select 1 as x", function(err, results, more) { + console.log("q2 result: ", err, results, more) + + db.close(function(err) { console.log(err || "Closed") }) + }) + } + }) + } +}); \ No newline at end of file From 1c1c2daf7a213b5f6491e832fa912d76c60daf5a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 27 Sep 2013 16:44:55 -0400 Subject: [PATCH 354/511] 0.5.31 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ac3fac1..3fae40d5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.30", + "version": "0.5.31", "main": "lib/odbc.js", "homepage": "http://github.com/w1nk/node-odbc/", "repository": { From f6775b9380e993add1ac7945900286a72ce9851d Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Mon, 30 Sep 2013 11:08:22 +0100 Subject: [PATCH 355/511] Fix blank result in ODBC::GetSQLError It seems with MSSQL at least, SQLGetDiagField needs to be called with record number 0 when retrieving header fields such as the error count (even though the documentation states that the record number is ignored). This stops SQLGetDiagField returning SQL _ERROR and thus numfields being uninitialized. --- src/odbc.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 911a6711..73bb50b8 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -809,14 +809,17 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* char errorSQLState[14]; char errorMessage[512]; - SQLGetDiagField( + ret = SQLGetDiagField( handleType, handle, - 1, + 0, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &len); + + // Windows seems to define SQLINTEGER as long int, unixodbc as just int... %i should cover both + DEBUG_PRINTF("ODBC::GetSQLError : called SQLGetDiagField; ret=%i\n", ret); for (i = 0; i < numfields; i++){ DEBUG_PRINTF("ODBC::GetSQLError : calling SQLGetDiagRec; i=%i, numfields=%i\n", i, numfields); From efad9e6b4dc5c729143b79db5480e98b66006553 Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Mon, 30 Sep 2013 13:52:23 +0100 Subject: [PATCH 356/511] More fixes for errors and multiple result sets 1. If the first result set is an error, still construct a result object as there may be further result sets, e.g. ```SQL select 1/0 as [infinity]; select 'This message should be returned' as [message]; ``` 2. Secondly, fix a case where a spurious result set is produced following multiple errors: ```SQL select 1 as i; raiserror('Error #1', 16, 1); raiserror('Error #2', 16, 1); select 'You should not see a blank result set before this message' as [message]; ``` --- lib/odbc.js | 28 ++++++++++------------------ src/odbc_connection.cpp | 16 +++++++--------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index 8a677089..7cc1a9da 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -179,13 +179,7 @@ Database.prototype.query = function (sql, params, cb) { } self.queue.push(function (next) { - function cbQuery (err, result) { - if (err) { - cb(err, [], false); - - return next(); - } - + function cbQuery (initialErr, result) { fetchMore(); function fetchMore() { @@ -211,21 +205,19 @@ Database.prototype.query = function (sql, params, cb) { result.closeSync(); } - cb(err, data, moreResults); - + cb(err || initialErr, data, moreResults); + initialErr = null; + while (moreResultsError) { try { - moreResults = result.moreResultsSync(); - } - catch (e) { - //call back with the previous error + moreResults = result.moreResultsSync(); + cb(moreResultsError, [], moreResults); // No errors left - still need to report the + // last one, though + moreResultsError = null; + } catch (e) { cb(moreResultsError, [], moreResults); moreResultsError = e; - } - finally { - cb(moreResultsError, [], moreResults); - moreResultsError = null; - } + } } if (moreResults) { diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 1de13859..f3e8a28e 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -870,14 +870,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterQuery : data->result=%i, data->noResultObject=%i\n", data->result, data->noResultObject); - //check to see if there was an error during execution - if(data->result == SQL_ERROR) { - ODBC::CallbackSQLError( - SQL_HANDLE_STMT, - data->hSTMT, - data->cb); - } - else if (data->noResultObject) { + if (data->result != SQL_ERROR && data->noResultObject) { //We have been requested to not create a result object //this means we should release the handle now and call back //with True() @@ -906,7 +899,12 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { Local js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); - args[0] = Local::New(Null()); + // Check now to see if there was an error (as there may be further result sets) + if (data->result == SQL_ERROR) { + args[0] = ODBC::GetSQLError(SQL_HANDLE_STMT, data->hSTMT, "[node-odbc] SQL_ERROR"); + } else { + args[0] = Local::New(Null()); + } args[1] = Local::New(js_result); data->cb->Call(Context::GetCurrent()->Global(), 2, args); From 7364dde1e88b780020fdfe5929d0793b0c02b46e Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Sat, 5 Oct 2013 16:39:26 +0100 Subject: [PATCH 357/511] Fix undefined behaviour in UV_AfterExecute args is declared as "Local args[3];" but then args[3] is assigned... --- src/odbc_statement.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 6c475e5d..58a21611 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -210,7 +210,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { data->cb); } else { - Local args[3]; + Local args[4]; bool* canFreeHandle = new bool(false); args[0] = External::New(self->m_hENV); From 5b170016740fb2b47d4f3037a753edb59c7abeb9 Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Sat, 5 Oct 2013 16:41:37 +0100 Subject: [PATCH 358/511] More arrays declared wrong --- src/odbc_statement.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 58a21611..42b702d7 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -266,7 +266,7 @@ Handle ODBCStatement::ExecuteSync(const Arguments& args) { return scope.Close(Null()); } else { - Local args[3]; + Local args[4]; bool* canFreeHandle = new bool(false); args[0] = External::New(stmt->m_hENV); @@ -499,7 +499,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { data->cb); } else { - Local args[3]; + Local args[4]; bool* canFreeHandle = new bool(false); args[0] = External::New(self->m_hENV); @@ -565,7 +565,7 @@ Handle ODBCStatement::ExecuteDirectSync(const Arguments& args) { return scope.Close(Null()); } else { - Local args[3]; + Local args[4]; bool* canFreeHandle = new bool(false); args[0] = External::New(stmt->m_hENV); From d46c761bd59c9f288dc85e5752200c5d96b35b80 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 27 Sep 2013 16:54:50 -0400 Subject: [PATCH 359/511] Update package.json to point to wankdanker's repo Sorry Lee. Hope you don't mind. :) --- package.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 3fae40d5..e30029c9 100644 --- a/package.json +++ b/package.json @@ -3,17 +3,22 @@ "description": "unixodbc bindings for node", "version": "0.5.31", "main": "lib/odbc.js", - "homepage": "http://github.com/w1nk/node-odbc/", + "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { "type": "git", - "url": "git://github.com/w1nk/node-odbc.git" + "url": "git://github.com/wankdanker/node-odbc.git" }, - "author": "Lee Smith ", + "contributors": [ + { "name" : "Dan VerWeire", + "email" : "dverweire@gmail.com" }, + { "name" : "Lee Smith", + "email" : "notwink@gmail.com" } + ], "directories": { "lib": "." }, "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" }, "scripts": { "preinstall": "node-gyp configure build", From 757332d20ca942b215651e7955e173525ada13f1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 8 Oct 2013 15:11:17 -0400 Subject: [PATCH 360/511] Cast error string to avoid compiler warning --- src/odbc_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index f3e8a28e..a69753a2 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -901,7 +901,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { // Check now to see if there was an error (as there may be further result sets) if (data->result == SQL_ERROR) { - args[0] = ODBC::GetSQLError(SQL_HANDLE_STMT, data->hSTMT, "[node-odbc] SQL_ERROR"); + args[0] = ODBC::GetSQLError(SQL_HANDLE_STMT, data->hSTMT, (char *) "[node-odbc] SQL_ERROR"); } else { args[0] = Local::New(Null()); } From 851efa224582b5888f53064021a1b8819e1cbc85 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 8 Oct 2013 15:53:43 -0400 Subject: [PATCH 361/511] 0.5.32 --- package.json | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e30029c9..08223329 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.31", + "version": "0.5.32", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { @@ -9,10 +9,14 @@ "url": "git://github.com/wankdanker/node-odbc.git" }, "contributors": [ - { "name" : "Dan VerWeire", - "email" : "dverweire@gmail.com" }, - { "name" : "Lee Smith", - "email" : "notwink@gmail.com" } + { + "name": "Dan VerWeire", + "email": "dverweire@gmail.com" + }, + { + "name": "Lee Smith", + "email": "notwink@gmail.com" + } ], "directories": { "lib": "." From bce9cdb5bbdfa810c08b9f23309a0b526cc89b39 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 25 Oct 2013 16:56:39 -0400 Subject: [PATCH 362/511] Finish implementing transactions and update docs. --- README.md | 168 +++++++++++++++ lib/odbc.js | 36 +++- src/odbc_connection.cpp | 214 ++++++++++++++++++++ src/odbc_connection.h | 15 +- test/test-binding-transaction-commit.js | 94 +++++---- test/test-binding-transaction-commitSync.js | 53 +++++ test/test-transaction-commit.js | 77 +++++++ 7 files changed, 614 insertions(+), 43 deletions(-) create mode 100644 test/test-binding-transaction-commitSync.js create mode 100644 test/test-transaction-commit.js diff --git a/README.md b/README.md index c5493104..4b3a1378 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,40 @@ db.openSync(cn); db.closeSync(); ``` +#### .prepare(sql, callback) + +Prepare a statement for execution. + +* **sql** - SQL string to prepare +* **callback** - `callback (err, stmt)` + +Returns a `Statement` object via the callback + +```javascript +var db = require("odbc")() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +//Blocks until the connection is open +db.openSync(cn); + +db.prepare("insert into hits (col1, col2) VALUES (?, ?)", function (err, stmt) { + if (err) { + //could not prepare for some reason + console.log(err); + return db.closeSync(); + } + + //Bind and Execute the statment asynchronously + stmt.execute(['something', 42], function (err, result) { + result.closeSync(); + + //Close the connection + db.closeSync(); + }); +}) +``` + #### .prepareSync(sql) Synchronously prepare a statement for execution. @@ -241,6 +275,140 @@ stmt.execute(['something', 42], function (err, result) { }); ``` +#### .beginTransaction(callback) + +Begin a transaction + +* **callback** - `callback (err)` + +#### .beginTransactionSync(callback) + +Synchronously begin a transaction + +#### .commitTransaction(callback) + +Commit a transaction + +* **callback** - `callback (err)` + +```javascript +var db = require("odbc")() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +//Blocks until the connection is open +db.openSync(cn); + +db.beginTransaction(function (err) { + if (err) { + //could not begin a transaction for some reason. + console.log(err); + return db.closeSync(); + } + + var result = db.querySync("insert into customer (customerCode) values ('stevedave')"); + + db.commitTransaction(function (err) { + if (err) { + //error during commit + console.log(err); + return db.closeSync(); + } + + console.log(db.querySync("select * from customer where customerCode = 'stevedave'")); + + //Close the connection + db.closeSync(); + }); +}) +``` + +#### .commitTransactionSync(callback) + +Synchronously commit a transaction + +```javascript +var db = require("odbc")() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +//Blocks until the connection is open +db.openSync(cn); + +db.beginTransactionSync(); + +var result = db.querySync("insert into customer (customerCode) values ('stevedave')"); + +db.commitTransactionSync(); + +console.log(db.querySync("select * from customer where customerCode = 'stevedave'")); + +//Close the connection +db.closeSync(); +``` + +#### .rollbackTransaction(callback) + +Rollback a transaction + +* **callback** - `callback (err)` + +```javascript +var db = require("odbc")() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +//Blocks until the connection is open +db.openSync(cn); + +db.beginTransaction(function (err) { + if (err) { + //could not begin a transaction for some reason. + console.log(err); + return db.closeSync(); + } + + var result = db.querySync("insert into customer (customerCode) values ('stevedave')"); + + db.rollbackTransaction(function (err) { + if (err) { + //error during rollback + console.log(err); + return db.closeSync(); + } + + console.log(db.querySync("select * from customer where customerCode = 'stevedave'")); + + //Close the connection + db.closeSync(); + }); +}) +``` + +#### .rollbackTransactionSync(callback) + +Synchronously rollback a transaction + +```javascript +var db = require("odbc")() + , cn = "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname" + ; + +//Blocks until the connection is open +db.openSync(cn); + +db.beginTransactionSync(); + +var result = db.querySync("insert into customer (customerCode) values ('stevedave')"); + +db.rollbackTransactionSync(); + +console.log(db.querySync("select * from customer where customerCode = 'stevedave'")); + +//Close the connection +db.closeSync(); +``` + ---------- ### Pool diff --git a/lib/odbc.js b/lib/odbc.js index 7cc1a9da..9c170c9e 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -324,6 +324,38 @@ Database.prototype.querySync = function (sql, params) { return data; }; +Database.prototype.beginTransaction = function (cb) { + var self = this; + + self.conn.beginTransaction(cb); + + return self; +}; + +Database.prototype.endTransaction = function (rollback, cb) { + var self = this; + + self.conn.endTransaction(rollback, cb); + + return self; +}; + +Database.prototype.commitTransaction = function (cb) { + var self = this; + + self.conn.endTransaction(false, cb); //don't rollback + + return self; +}; + +Database.prototype.rollbackTransaction = function (cb) { + var self = this; + + self.conn.endTransaction(true, cb); //rollback + + return self; +}; + Database.prototype.beginTransactionSync = function () { var self = this; @@ -346,7 +378,7 @@ Database.prototype.commitTransactionSync = function () { self.conn.endTransactionSync(false); //don't rollback return self; -} +}; Database.prototype.rollbackTransactionSync = function () { var self = this; @@ -354,7 +386,7 @@ Database.prototype.rollbackTransactionSync = function () { self.conn.endTransactionSync(true); //rollback return self; -} +}; Database.prototype.columns = function(catalog, schema, table, column, callback) { var self = this; diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index a69753a2..35694256 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -64,7 +64,9 @@ void ODBCConnection::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", Query); NODE_SET_PROTOTYPE_METHOD(constructor_template, "querySync", QuerySync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "beginTransaction", BeginTransaction); NODE_SET_PROTOTYPE_METHOD(constructor_template, "beginTransactionSync", BeginTransactionSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "endTransaction", EndTransaction); NODE_SET_PROTOTYPE_METHOD(constructor_template, "endTransactionSync", EndTransactionSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "columns", Columns); @@ -1424,6 +1426,99 @@ Handle ODBCConnection::BeginTransactionSync(const Arguments& args) { return scope.Close(True()); } +/* + * BeginTransaction + * + */ + +Handle ODBCConnection::BeginTransaction(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::BeginTransaction\n"); + HandleScope scope; + + REQ_FUN_ARG(0, cb); + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + query_work_data* data = + (query_work_data *) calloc(1, sizeof(query_work_data)); + + if (!data) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + } + + data->cb = Persistent::New(cb); + data->conn = conn; + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_BeginTransaction, + (uv_after_work_cb)UV_AfterBeginTransaction); + + return scope.Close(Undefined()); +} + +/* + * UV_BeginTransaction + * + */ + +void ODBCConnection::UV_BeginTransaction(uv_work_t* req) { + DEBUG_PRINTF("ODBCConnection::UV_BeginTransaction\n"); + + query_work_data* data = (query_work_data *)(req->data); + + //set the connection manual commits + data->result = SQLSetConnectAttr( + data->conn->self()->m_hDBC, + SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER) SQL_AUTOCOMMIT_OFF, + SQL_NTS); +} + +/* + * UV_AfterBeginTransaction + * + */ + +void ODBCConnection::UV_AfterBeginTransaction(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCConnection::UV_AfterBeginTransaction\n"); + HandleScope scope; + + open_connection_work_data* data = (open_connection_work_data *)(req->data); + + Local argv[1]; + + bool err = false; + + if (!SQL_SUCCEEDED(data->result)) { + err = true; + + Local objError = ODBC::GetSQLError(SQL_HANDLE_DBC, data->conn->self()->m_hDBC); + + argv[0] = objError; + } + + TryCatch try_catch; + + data->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + + free(data); + free(req); + + scope.Close(Undefined()); +} + /* * EndTransactionSync * @@ -1486,3 +1581,122 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { return scope.Close(True()); } } + +/* + * EndTransaction + * + */ + +Handle ODBCConnection::EndTransaction(const Arguments& args) { + DEBUG_PRINTF("ODBCConnection::EndTransaction\n"); + HandleScope scope; + + REQ_BOOL_ARG(0, rollback); + REQ_FUN_ARG(1, cb); + + ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + + uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); + + query_work_data* data = + (query_work_data *) calloc(1, sizeof(query_work_data)); + + if (!data) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + } + + data->completionType = (rollback->Value()) + ? SQL_ROLLBACK + : SQL_COMMIT + ; + data->cb = Persistent::New(cb); + data->conn = conn; + work_req->data = data; + + uv_queue_work( + uv_default_loop(), + work_req, + UV_EndTransaction, + (uv_after_work_cb)UV_AfterEndTransaction); + + return scope.Close(Undefined()); +} + +/* + * UV_EndTransaction + * + */ + +void ODBCConnection::UV_EndTransaction(uv_work_t* req) { + DEBUG_PRINTF("ODBCConnection::UV_EndTransaction\n"); + + query_work_data* data = (query_work_data *)(req->data); + + bool err = false; + + //Call SQLEndTran + SQLRETURN ret = SQLEndTran( + SQL_HANDLE_DBC, + data->conn->m_hDBC, + data->completionType); + + data->result = ret; + + if (!SQL_SUCCEEDED(ret)) { + err = true; + } + + //Reset the connection back to autocommit + ret = SQLSetConnectAttr( + data->conn->m_hDBC, + SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER) SQL_AUTOCOMMIT_ON, + SQL_NTS); + + if (!SQL_SUCCEEDED(ret) && !err) { + //there was not an earlier error, + //so we shall pass the return code from + //this last call. + data->result = ret; + } +} + +/* + * UV_AfterEndTransaction + * + */ + +void ODBCConnection::UV_AfterEndTransaction(uv_work_t* req, int status) { + DEBUG_PRINTF("ODBCConnection::UV_AfterEndTransaction\n"); + HandleScope scope; + + open_connection_work_data* data = (open_connection_work_data *)(req->data); + + Local argv[1]; + + bool err = false; + + if (!SQL_SUCCEEDED(data->result)) { + err = true; + + Local objError = ODBC::GetSQLError(SQL_HANDLE_DBC, data->conn->self()->m_hDBC); + + argv[0] = objError; + } + + TryCatch try_catch; + + data->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + data->cb.Dispose(); + + free(data); + free(req); + + scope.Close(Undefined()); +} \ No newline at end of file diff --git a/src/odbc_connection.h b/src/odbc_connection.h index d5af05ee..ef0bda53 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -48,13 +48,13 @@ class ODBCConnection : public node::ObjectWrap { static void ConnectTimeoutSetter(Local property, Local value, const AccessorInfo &info); //async methods -// static Handle BeginTransaction(const Arguments& args); -// static void UV_BeginTransaction(uv_work_t* work_req); -// static void UV_AfterBeginTransaction(uv_work_t* work_req, int status); -// -// static Handle EndTransaction(const Arguments& args); -// static void UV_EndTransaction(uv_work_t* work_req); -// static void UV_AfterEndTransaction(uv_work_t* work_req, int status); + static Handle BeginTransaction(const Arguments& args); + static void UV_BeginTransaction(uv_work_t* work_req); + static void UV_AfterBeginTransaction(uv_work_t* work_req, int status); + + static Handle EndTransaction(const Arguments& args); + static void UV_EndTransaction(uv_work_t* work_req); + static void UV_AfterEndTransaction(uv_work_t* work_req, int status); static Handle Open(const Arguments& args); static void UV_Open(uv_work_t* work_req); @@ -117,6 +117,7 @@ struct query_work_data { Parameter *params; int paramCount; + int completionType; bool noResultObject; void *sql; diff --git a/test/test-binding-transaction-commit.js b/test/test-binding-transaction-commit.js index dd7409b3..029f4aff 100644 --- a/test/test-binding-transaction-commit.js +++ b/test/test-binding-transaction-commit.js @@ -6,48 +6,74 @@ var common = require("./common") ; db.createConnection(function (err, conn) { + conn.openSync(common.connectionString); common.createTables(conn, function (err, data) { - try { - conn.beginTransactionSync(); - - var result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + test1() + + function test1() { + conn.beginTransaction(function (err) { + if (err) { + console.log("Error beginning transaction."); + console.log(err); + exitCode = 1 + } + + var result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); - conn.endTransactionSync(true); //rollback - - result = conn.querySync("select * from " + common.tableName); - - assert.deepEqual(result.fetchAllSync(), []); + //rollback + conn.endTransaction(true, function (err) { + if (err) { + console.log("Error rolling back transaction"); + console.log(err); + exitCode = 2 + } + + result = conn.querySync("select * from " + common.tableName); + data = result.fetchAllSync(); + + assert.deepEqual(data, []); + + test2(); + }); + }); } - catch (e) { - console.log("Failed when rolling back"); - console.log(e); - exitCode = 1 - } - - try { + + function test2 () { //Start a new transaction - conn.beginTransactionSync(); - - result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + conn.beginTransaction(function (err) { + if (err) { + console.log("Error beginning transaction"); + console.log(err); + exitCode = 3 + } + + result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); - conn.endTransactionSync(false); //commit - - result = conn.querySync("select * from " + common.tableName); - - assert.deepEqual(result.fetchAllSync(), [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); - } - catch (e) { - console.log("Failed when committing"); - console.log(e); - - exitCode = 2; + //commit + conn.endTransaction(false, function (err) { + if (err) { + console.log("Error committing transaction"); + console.log(err); + exitCode = 3 + } + + result = conn.querySync("select * from " + common.tableName); + data = result.fetchAllSync(); + + assert.deepEqual(data, [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); + + finish(); + }); + }); } - common.dropTables(conn, function (err) { - conn.closeSync(); - process.exit(exitCode); - }); + function finish() { + common.dropTables(conn, function (err) { + conn.closeSync(); + process.exit(exitCode); + }); + } }); }); diff --git a/test/test-binding-transaction-commitSync.js b/test/test-binding-transaction-commitSync.js new file mode 100644 index 00000000..dd7409b3 --- /dev/null +++ b/test/test-binding-transaction-commitSync.js @@ -0,0 +1,53 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.ODBC() + , assert = require("assert") + , exitCode = 0 + ; + +db.createConnection(function (err, conn) { + conn.openSync(common.connectionString); + + common.createTables(conn, function (err, data) { + try { + conn.beginTransactionSync(); + + var result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + + conn.endTransactionSync(true); //rollback + + result = conn.querySync("select * from " + common.tableName); + + assert.deepEqual(result.fetchAllSync(), []); + } + catch (e) { + console.log("Failed when rolling back"); + console.log(e); + exitCode = 1 + } + + try { + //Start a new transaction + conn.beginTransactionSync(); + + result = conn.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + + conn.endTransactionSync(false); //commit + + result = conn.querySync("select * from " + common.tableName); + + assert.deepEqual(result.fetchAllSync(), [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); + } + catch (e) { + console.log("Failed when committing"); + console.log(e); + + exitCode = 2; + } + + common.dropTables(conn, function (err) { + conn.closeSync(); + process.exit(exitCode); + }); + }); +}); diff --git a/test/test-transaction-commit.js b/test/test-transaction-commit.js new file mode 100644 index 00000000..cc4f425d --- /dev/null +++ b/test/test-transaction-commit.js @@ -0,0 +1,77 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + , exitCode = 0 + ; + + +db.openSync(common.connectionString); + +common.createTables(db, function (err, data) { + test1() + + function test1() { + db.beginTransaction(function (err) { + if (err) { + console.log("Error beginning transaction."); + console.log(err); + exitCode = 1 + } + + var result = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + + //rollback + db.endTransaction(true, function (err) { + if (err) { + console.log("Error rolling back transaction"); + console.log(err); + exitCode = 2 + } + + data = db.querySync("select * from " + common.tableName); + + assert.deepEqual(data, []); + + test2(); + }); + }); + } + + function test2 () { + //Start a new transaction + db.beginTransaction(function (err) { + if (err) { + console.log("Error beginning transaction"); + console.log(err); + exitCode = 3 + } + + result = db.querySync("insert into " + common.tableName + " (COLINT, COLDATETIME, COLTEXT) VALUES (42, null, null)" ); + + //commit + db.endTransaction(false, function (err) { + if (err) { + console.log("Error committing transaction"); + console.log(err); + exitCode = 3 + } + + data = db.querySync("select * from " + common.tableName); + + assert.deepEqual(data, [ { COLINT: 42, COLDATETIME: null, COLTEXT: null } ]); + + finish(); + }); + }); + } + + function finish() { + common.dropTables(db, function (err) { + db.closeSync(); + process.exit(exitCode); + }); + } +}); + + From 1729c540a9410912db54ec759622eac364cd29d5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 25 Oct 2013 16:57:18 -0400 Subject: [PATCH 363/511] 0.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 08223329..6a8f39a4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.5.32", + "version": "0.6.0", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 7aa59324c171fa4dddd630251ea8868e4b235f4a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 28 Oct 2013 14:00:50 -0400 Subject: [PATCH 364/511] Document additional way to create an instance of Database --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b3a1378..5dfab0cc 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,15 @@ api ### Database The simple api is based on instances of the `Database` class. You may get an -instance in a couple of ways. +instance in one of the following ways: -By using the helper function: +```javascript +require("odbc").open(connectionString, function (err, db){ + //db is already open now if err is falsy +}); +``` + +or by using the helper function: ```javascript var db = require("odbc")(); From 4cdad9a58f25ab6449a8d62778cc8ace03067b90 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 28 Oct 2013 17:10:51 -0400 Subject: [PATCH 365/511] Update issues url --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 6a8f39a4..2cee07aa 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,9 @@ "type": "git", "url": "git://github.com/wankdanker/node-odbc.git" }, + "bugs" : { + "url" : "https://github.com/w1nk/node-odbc/issues" + }, "contributors": [ { "name": "Dan VerWeire", From bba1e2dc8195c5eea3a5b4d44092bdffa6955924 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 28 Oct 2013 17:11:57 -0400 Subject: [PATCH 366/511] 0.6.1 --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2cee07aa..c1139c36 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.0", + "version": "0.6.1", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { "type": "git", "url": "git://github.com/wankdanker/node-odbc.git" }, - "bugs" : { - "url" : "https://github.com/w1nk/node-odbc/issues" + "bugs": { + "url": "https://github.com/w1nk/node-odbc/issues" }, "contributors": [ { From cf198513354ac71343eac4221945fbc1a9ee7782 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 31 Oct 2013 17:55:41 -0400 Subject: [PATCH 367/511] docs: remove callback param from Sync functions; silly copy and paste. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5dfab0cc..0f5a8133 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ db.open(cn, function (err) { }); ``` -#### .closeSync(callback) +#### .closeSync() Synchronously close the currently opened database. @@ -287,7 +287,7 @@ Begin a transaction * **callback** - `callback (err)` -#### .beginTransactionSync(callback) +#### .beginTransactionSync() Synchronously begin a transaction @@ -329,7 +329,7 @@ db.beginTransaction(function (err) { }) ``` -#### .commitTransactionSync(callback) +#### .commitTransactionSync() Synchronously commit a transaction @@ -391,7 +391,7 @@ db.beginTransaction(function (err) { }) ``` -#### .rollbackTransactionSync(callback) +#### .rollbackTransactionSync() Synchronously rollback a transaction From e78d7e867999574a2f7383062a2b08a219724d14 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 4 Nov 2013 16:09:31 -0500 Subject: [PATCH 368/511] Change unix time conversion to use timelocal instead of mktime We noticed that in the normal usage of the module, our times were reported incorrectly. It traced back to using timelocal or timegm. timelocal works as we expect in more cases. There is now a define available if you need it to use timegm (see README.md) --- README.md | 15 +++++++++++++++ src/odbc.cpp | 8 ++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f5a8133..a088cb4b 100644 --- a/README.md +++ b/README.md @@ -568,6 +568,21 @@ work better or faster, you can remove the `UNICODE` define in `binding.gyp` ``` +### timegm vs timelocal + +When converting a database time to a C time one may use `timegm` or `timelocal`. See +`man timegm` for the details of these two functions. By default the node-odbc bindings +use `timelocal`. If you would prefer for it to use `timegm` then specify the `TIMEGM` +define in `binding.gyp` + +```javascript + +'defines' : [ + "TIMEGM" +], + +``` + ### Strict Column Naming When column names are retrieved from ODBC, you can request by SQL_DESC_NAME or diff --git a/src/odbc.cpp b/src/odbc.cpp index 73bb50b8..d979aa20 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -489,9 +489,13 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //and system databases to attempt to determine whether DST is in effect //at the specified time. timeInfo.tm_isdst = -1; - - return scope.Close(Date::New((double(timegm(&timeInfo)) * 1000) +#ifdef TIMEGM + return scope.Close(Date::New((double(timegm(&timeInfo)) * 1000) + + (odbcTime.fraction / 1000000))); +#else + return scope.Close(Date::New((double(timelocal(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000))); +#endif //return Date::New((double(timegm(&timeInfo)) * 1000) // + (odbcTime.fraction / 1000000)); } From d887eeaadcbc5bafe645acb827f4098ccd30347c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 4 Nov 2013 16:09:44 -0500 Subject: [PATCH 369/511] 0.6.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c1139c36..60d45114 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.1", + "version": "0.6.2", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From da893b391cf33127d33604d66e8525e9575b52fa Mon Sep 17 00:00:00 2001 From: RG72 Date: Tue, 25 Feb 2014 14:34:09 +0500 Subject: [PATCH 370/511] Update README.md SERVERNAME instead of SERVER --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a088cb4b..6e610de6 100644 --- a/README.md +++ b/README.md @@ -632,6 +632,9 @@ eio.setMinParallel(threadCount); * If you have column names longer than 30 characters, you should add "TDS_Version=7.0" to your connection string to retrive the full column name. * Example : "DRIVER={FreeTDS};SERVER=host;UID=user;PWD=password;DATABASE=dbname;TDS_Version=7.0" +* If you got error "[unixODBC][FreeTDS][SQL Server]Unable to connect to data source" + Try use SERVERNAME instead of SERVER + * Example : "DRIVER={FreeTDS};SERVERNAME=host;UID=user;PWD=password;DATABASE=dbname" * Be sure that your odbcinst.ini has the proper threading configuration for your FreeTDS driver. If you choose the incorrect threading model it may cause the thread pool to be blocked by long running queries. This is what From a7c17939f903fb397ccd5a9de21513c1ad920edc Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 27 Feb 2014 17:59:14 -0500 Subject: [PATCH 371/511] Fix error message buffer size bug We were passing the size of the message buffer in bytes instead of the number of characters. This caused issues when UNICODE is being used because there are at least 2 bytes per character. This was causing buffer over-runs. Instead of setting the buffer size with a literl number I have used the constants ERROR_MESSAGE_BUFFER_BYTES and ERROR_MESSAGE_BUFFER_CHARS Closes #57 --- src/odbc.cpp | 4 ++-- src/odbc.h | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index d979aa20..d231f7ef 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -811,7 +811,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* SQLINTEGER numfields; SQLRETURN ret; char errorSQLState[14]; - char errorMessage[512]; + char errorMessage[ERROR_MESSAGE_BUFFER_BYTES]; ret = SQLGetDiagField( handleType, @@ -835,7 +835,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* (SQLTCHAR *) errorSQLState, &native, (SQLTCHAR *) errorMessage, - sizeof(errorMessage), + ERROR_MESSAGE_BUFFER_CHARS, &len); DEBUG_PRINTF("ODBC::GetSQLError : after SQLGetDiagRec; i=%i\n", i); diff --git a/src/odbc.h b/src/odbc.h index 33bdb378..348d902f 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -38,6 +38,14 @@ using namespace node; #define MAX_FIELD_SIZE 1024 #define MAX_VALUE_SIZE 1048576 +#ifdef UNICODE +#define ERROR_MESSAGE_BUFFER_BYTES 2048 +#define ERROR_MESSAGE_BUFFER_CHARS 1024 +#else +#define ERROR_MESSAGE_BUFFER_BYTES 2048 +#define ERROR_MESSAGE_BUFFER_CHARS 2048 +#endif + #define MODE_COLLECT_AND_CALLBACK 1 #define MODE_CALLBACK_FOR_EACH 2 #define FETCH_ARRAY 3 From a4a0540a6a7d31d08c978f818b735a3eacbcdbd5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 27 Feb 2014 18:02:23 -0500 Subject: [PATCH 372/511] 0.6.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 60d45114..cd43bffb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.2", + "version": "0.6.3", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From d825b2f1928d93e39e9f48946db82f14e01095b6 Mon Sep 17 00:00:00 2001 From: "Bryan J. Ross" Date: Tue, 1 Apr 2014 23:54:08 -0600 Subject: [PATCH 373/511] Fix for segfault 11 on OSX Probably not a good idea to access memory after it's been freed --- src/odbc_result.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index c83d3677..8675b3e3 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -310,11 +310,11 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { } } + data->objResult->Unref(); + free(data); free(work_req); - data->objResult->Unref(); - return; } From c0a1225021bdaddb790114318611b32fd90159b1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 2 Apr 2014 11:17:07 -0400 Subject: [PATCH 374/511] 0.6.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cd43bffb..fdcfe21f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.3", + "version": "0.6.4", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 573e095a3b816ca2be03f775e3a3c49015ea1940 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Apr 2014 12:23:53 -0400 Subject: [PATCH 375/511] Fix connectTimeout and add loginTimeout This should fix #50. Previously we were using SQL_ATTR_LOGIN_TIMEOUT as the connect attribute for connectionTimeout. I think this is wrong. I have replaced the attribute with SQL_ATTR_CONNECTION_TIMEOUT and introduce another attribute called loginTimeout to set SQL_ATTR_LOGIN_TIMEOUT. --- lib/odbc.js | 11 +++- src/odbc_connection.cpp | 61 +++++++++++++++++--- src/odbc_connection.h | 5 +- test/test-binding-connection-loginTimeout.js | 31 ++++++++++ test/test-open-loginTimeout.js | 24 ++++++++ 5 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 test/test-binding-connection-loginTimeout.js create mode 100644 test/test-open-loginTimeout.js diff --git a/lib/odbc.js b/lib/odbc.js index 9c170c9e..c69296fa 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -70,6 +70,7 @@ function Database(options) { self.fetchMode = options.fetchMode || null; self.connected = false; self.connectTimeout = options.connectTimeout || null; + self.loginTimeout = options.loginTimeout || null; } //Expose constants @@ -103,7 +104,11 @@ Database.prototype.open = function (connectionString, cb) { if (self.connectTimeout || self.connectTimeout === 0) { self.conn.connectTimeout = self.connectTimeout; } - + + if (self.loginTimeout || self.loginTimeout === 0) { + self.conn.loginTimeout = self.loginTimeout; + } + self.conn.open(connectionString, function (err, result) { if (err) return cb(err); @@ -123,6 +128,10 @@ Database.prototype.openSync = function (connectionString) { self.conn.connectTimeout = self.connectTimeout; } + if (self.loginTimeout || self.loginTimeout === 0) { + self.conn.loginTimeout = self.loginTimeout; + } + if (typeof(connectionString) == "object") { var obj = connectionString; connectionString = ""; diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 35694256..4d3643a4 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -53,6 +53,7 @@ void ODBCConnection::Init(v8::Handle target) { //instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); instance_template->SetAccessor(String::New("connected"), ConnectedGetter); instance_template->SetAccessor(String::New("connectTimeout"), ConnectTimeoutGetter, ConnectTimeoutSetter); + instance_template->SetAccessor(String::New("loginTimeout"), LoginTimeoutGetter, LoginTimeoutSetter); // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open); @@ -119,7 +120,9 @@ Handle ODBCConnection::New(const Arguments& args) { //set default connectTimeout to 5 seconds conn->connectTimeout = 5; - + //set default loginTimeout to 5 seconds + conn->loginTimeout = 5; + return scope.Close(args.Holder()); } @@ -149,6 +152,24 @@ void ODBCConnection::ConnectTimeoutSetter(Local property, Local v } } +Handle ODBCConnection::LoginTimeoutGetter(Local property, const AccessorInfo &info) { + HandleScope scope; + + ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + + return scope.Close(Number::New(obj->loginTimeout)); +} + +void ODBCConnection::LoginTimeoutSetter(Local property, Local value, const AccessorInfo &info) { + HandleScope scope; + + ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + + if (value->IsNumber()) { + obj->loginTimeout = value->Int32Value(); + } +} + /* * Open * @@ -206,15 +227,26 @@ void ODBCConnection::UV_Open(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); - int timeOut = self->connectTimeout; + int connectTimeout = self->connectTimeout; - if (timeOut > 0) { + if (connectTimeout > 0) { + //NOTE: SQLSetConnectAttr requires the thread to be locked + SQLSetConnectAttr( + self->m_hDBC, //ConnectionHandle + SQL_ATTR_CONNECTION_TIMEOUT, //Attribute + &connectTimeout, //ValuePtr + sizeof(connectTimeout)); //StringLength + } + + int loginTimeout = self->loginTimeout; + + if (loginTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( self->m_hDBC, //ConnectionHandle SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &timeOut, //ValuePtr - sizeof(timeOut)); //StringLength + &loginTimeout, //ValuePtr + sizeof(loginTimeout)); //StringLength } //Attempt to connect @@ -331,15 +363,26 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { uv_mutex_lock(&ODBC::g_odbcMutex); - int timeOut = conn->connectTimeout; + int connectTimeout = conn->connectTimeout; + + if (connectTimeout > 0) { + //NOTE: SQLSetConnectAttr requires the thread to be locked + SQLSetConnectAttr( + conn->m_hDBC, //ConnectionHandle + SQL_ATTR_CONNECTION_TIMEOUT, //Attribute + &connectTimeout, //ValuePtr + sizeof(connectTimeout)); //StringLength + } + + int loginTimeout = conn->loginTimeout; - if (timeOut > 0) { + if (loginTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( conn->m_hDBC, //ConnectionHandle SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &timeOut, //ValuePtr - sizeof(timeOut)); //StringLength + &loginTimeout, //ValuePtr + sizeof(loginTimeout)); //StringLength } //Attempt to connect diff --git a/src/odbc_connection.h b/src/odbc_connection.h index ef0bda53..6b727ff8 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -46,7 +46,9 @@ class ODBCConnection : public node::ObjectWrap { static Handle ConnectedGetter(Local property, const AccessorInfo &info); static Handle ConnectTimeoutGetter(Local property, const AccessorInfo &info); static void ConnectTimeoutSetter(Local property, Local value, const AccessorInfo &info); - + static Handle LoginTimeoutGetter(Local property, const AccessorInfo &info); + static void LoginTimeoutSetter(Local property, Local value, const AccessorInfo &info); + //async methods static Handle BeginTransaction(const Arguments& args); static void UV_BeginTransaction(uv_work_t* work_req); @@ -101,6 +103,7 @@ class ODBCConnection : public node::ObjectWrap { bool connected; int statements; int connectTimeout; + int loginTimeout; }; struct create_statement_work_data { diff --git a/test/test-binding-connection-loginTimeout.js b/test/test-binding-connection-loginTimeout.js new file mode 100644 index 00000000..e6d77529 --- /dev/null +++ b/test/test-binding-connection-loginTimeout.js @@ -0,0 +1,31 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.ODBC() + , assert = require("assert") + , exitCode = 0 + ; + +db.createConnection(function (err, conn) { + //loginTimeout should be 5 by default as set in C++ + assert.equal(conn.loginTimeout, 5); + + //test the setter and getter + conn.loginTimeout = 1234; + assert.equal(conn.loginTimeout, 1234); + + //set the time out to something small + conn.loginTimeout = 1; + assert.equal(conn.loginTimeout, 1); + + conn.open(common.connectionString, function (err) { + //TODO: it would be nice if we could somehow + //force a timeout to occurr, but most testing is + //done locally and it's hard to get a local server + //to not accept a connection within one second... + + console.log(err); + conn.close(function () { + + }); + }); +}); diff --git a/test/test-open-loginTimeout.js b/test/test-open-loginTimeout.js new file mode 100644 index 00000000..0e036b6e --- /dev/null +++ b/test/test-open-loginTimeout.js @@ -0,0 +1,24 @@ +var common = require("./common") + , odbc = require("../") + , assert = require("assert"); + +//test setting loginTimeout via the constructor works +var db = new odbc.Database({ loginTimeout : 1 }) + +db.open(common.connectionString, function(err) { + assert.equal(db.conn.loginTimeout, 1); + + assert.equal(err, null); + assert.equal(db.connected, true); + + db.close(function () { + assert.equal(db.connected, false); + + db.query("select * from " + common.tableName, function (err, rs, moreResultSets) { + assert.deepEqual(err, { message: 'Connection not open.' }); + assert.deepEqual(rs, []); + assert.equal(moreResultSets, false); + assert.equal(db.connected, false); + }); + }); +}); From 3cd950154cc74ac5d8117fbe7ddf9e9fb0212bfd Mon Sep 17 00:00:00 2001 From: Bryan Ross Date: Thu, 17 Apr 2014 10:40:19 -0400 Subject: [PATCH 376/511] Use C99 designated struct initializer (removes warnings on OSX) --- src/odbc.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index d231f7ef..a0217712 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -447,17 +447,17 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } #else struct tm timeInfo = { - tm_sec : 0 - , tm_min : 0 - , tm_hour : 0 - , tm_mday : 0 - , tm_mon : 0 - , tm_year : 0 - , tm_wday : 0 - , tm_yday : 0 - , tm_isdst : 0 - , tm_gmtoff : 0 - , tm_zone : 0 + .tm_sec = 0 + , .tm_min = 0 + , .tm_hour = 0 + , .tm_mday = 0 + , .tm_mon = 0 + , .tm_year = 0 + , .tm_wday = 0 + , .tm_yday = 0 + , .tm_isdst = 0 + , .tm_gmtoff = 0 + , .tm_zone = 0 }; SQL_TIMESTAMP_STRUCT odbcTime; From 4c8c3334ada32894eef1973bbe96de6123de0ab7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Apr 2014 15:38:03 -0400 Subject: [PATCH 377/511] Properly accept 0 as a value for connectTimeout and loginTimeout --- lib/odbc.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index c69296fa..bf3266c7 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -69,8 +69,14 @@ function Database(options) { self.queue = new SimpleQueue(); self.fetchMode = options.fetchMode || null; self.connected = false; - self.connectTimeout = options.connectTimeout || null; - self.loginTimeout = options.loginTimeout || null; + self.connectTimeout = (options.hasOwnProperty('connectTimeout')) + ? options.connectTimeout + : null + ; + self.loginTimeout = (options.hasOwnProperty('loginTimeout')) + ? options.loginTimeout + : null + ; } //Expose constants From cd266cb3e5ee82b96625500418d17c79b479a5ea Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Apr 2014 15:40:56 -0400 Subject: [PATCH 378/511] Set default connectTimeout to 0 This in effect preserves the previous behavior where we would set SQL_ATTR_LOGIN_TIMEOUT to 5. It seems that setting a timeout value for SQL_ATTR_CONNECTION_TIMEOUT causes an assertion error with the FreeTDS driver --- src/odbc_connection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 4d3643a4..c58e6060 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -118,8 +118,8 @@ Handle ODBCConnection::New(const Arguments& args) { conn->Wrap(args.Holder()); - //set default connectTimeout to 5 seconds - conn->connectTimeout = 5; + //set default connectTimeout to 0 seconds + conn->connectTimeout = 0; //set default loginTimeout to 5 seconds conn->loginTimeout = 5; From b6a9c75e06c296910dcaf254d3f91f93f5b27f32 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Apr 2014 15:42:37 -0400 Subject: [PATCH 379/511] Use SQLUINTEGER for connectTimeout and loginTimeout Before we were using plain int. This is just an attempt to be accurate in hopes of fixing a FreeTDS assertion when connecting with a value for connectTimeout --- src/odbc_connection.cpp | 8 ++++---- src/odbc_connection.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index c58e6060..993de66c 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -148,7 +148,7 @@ void ODBCConnection::ConnectTimeoutSetter(Local property, Local v ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); if (value->IsNumber()) { - obj->connectTimeout = value->Int32Value(); + obj->connectTimeout = value->Uint32Value(); } } @@ -166,7 +166,7 @@ void ODBCConnection::LoginTimeoutSetter(Local property, Local val ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); if (value->IsNumber()) { - obj->loginTimeout = value->Int32Value(); + obj->loginTimeout = value->Uint32Value(); } } @@ -227,7 +227,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { uv_mutex_lock(&ODBC::g_odbcMutex); - int connectTimeout = self->connectTimeout; + SQLUINTEGER connectTimeout = self->connectTimeout; if (connectTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked @@ -238,7 +238,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { sizeof(connectTimeout)); //StringLength } - int loginTimeout = self->loginTimeout; + SQLUINTEGER loginTimeout = self->loginTimeout; if (loginTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 6b727ff8..72b3c4df 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -102,8 +102,8 @@ class ODBCConnection : public node::ObjectWrap { SQLUSMALLINT canHaveMoreResults; bool connected; int statements; - int connectTimeout; - int loginTimeout; + SQLUINTEGER connectTimeout; + SQLUINTEGER loginTimeout; }; struct create_statement_work_data { From d5869ef121c99f7c687cf6a91d78384399db3adc Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Apr 2014 15:44:34 -0400 Subject: [PATCH 380/511] Update test-binding-connection-timeOut to reflect the new default value of connectTimeout --- test/test-binding-connection-timeOut.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-binding-connection-timeOut.js b/test/test-binding-connection-timeOut.js index fe70086d..dc30d0de 100644 --- a/test/test-binding-connection-timeOut.js +++ b/test/test-binding-connection-timeOut.js @@ -6,8 +6,8 @@ var common = require("./common") ; db.createConnection(function (err, conn) { - //connectionTimeout should be 5 by default as set in C++ - assert.equal(conn.connectTimeout, 5); + //connectionTimeout should be 0 by default as set in C++ + assert.equal(conn.connectTimeout, 0); //test the setter and getter conn.connectTimeout = 1234; From b464e9a4b21f0504be17a7823891425300ab84f1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Apr 2014 16:57:34 -0400 Subject: [PATCH 381/511] Use member value of connectTimeout or loginTimeout directly Instead of making a local copy of connectTimeout and loginTimeout use the value directly on the object like: &self->connectTimeout I am weary of doing this because I've had issues in the past with creating a pointer from a member variable and it is just my own lack of knowledge of C/C++ that makes me feel this way. However, in use this way it does not cause an error assertion in FreeTDS --- src/odbc_connection.cpp | 49 ++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 993de66c..6bc6e705 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -224,29 +224,27 @@ void ODBCConnection::UV_Open(uv_work_t* req) { open_connection_work_data* data = (open_connection_work_data *)(req->data); ODBCConnection* self = data->conn->self(); + + DEBUG_PRINTF("ODBCConnection::UV_Open : connectTimeout=%i, loginTimeout = %i\n", *&self->connectTimeout, *&self->loginTimeout); uv_mutex_lock(&ODBC::g_odbcMutex); - SQLUINTEGER connectTimeout = self->connectTimeout; - - if (connectTimeout > 0) { + if (self->connectTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( - self->m_hDBC, //ConnectionHandle - SQL_ATTR_CONNECTION_TIMEOUT, //Attribute - &connectTimeout, //ValuePtr - sizeof(connectTimeout)); //StringLength + self->m_hDBC, //ConnectionHandle + SQL_ATTR_CONNECTION_TIMEOUT, //Attribute + &self->connectTimeout, //ValuePtr + sizeof(self->connectTimeout)); //StringLength } - SQLUINTEGER loginTimeout = self->loginTimeout; - - if (loginTimeout > 0) { + if (self->loginTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( - self->m_hDBC, //ConnectionHandle - SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &loginTimeout, //ValuePtr - sizeof(loginTimeout)); //StringLength + self->m_hDBC, //ConnectionHandle + SQL_ATTR_LOGIN_TIMEOUT, //Attribute + &self->loginTimeout, //ValuePtr + sizeof(self->loginTimeout)); //StringLength } //Attempt to connect @@ -261,7 +259,6 @@ void ODBCConnection::UV_Open(uv_work_t* req) { NULL, //StringLength2Ptr SQL_DRIVER_NOPROMPT); //DriverCompletion - if (SQL_SUCCEEDED(ret)) { HSTMT hStmt; @@ -347,6 +344,8 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { //get reference to the connection object ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + DEBUG_PRINTF("ODBCConnection::OpenSync : connectTimeout=%i, loginTimeout = %i\n", *&conn->connectTimeout, *&conn->loginTimeout); + Local objError; SQLRETURN ret; bool err = false; @@ -363,26 +362,22 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { uv_mutex_lock(&ODBC::g_odbcMutex); - int connectTimeout = conn->connectTimeout; - - if (connectTimeout > 0) { + if (conn->connectTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( conn->m_hDBC, //ConnectionHandle SQL_ATTR_CONNECTION_TIMEOUT, //Attribute - &connectTimeout, //ValuePtr - sizeof(connectTimeout)); //StringLength + &conn->connectTimeout, //ValuePtr + sizeof(conn->connectTimeout));//StringLength } - int loginTimeout = conn->loginTimeout; - - if (loginTimeout > 0) { + if (conn->loginTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( - conn->m_hDBC, //ConnectionHandle - SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &loginTimeout, //ValuePtr - sizeof(loginTimeout)); //StringLength + conn->m_hDBC, //ConnectionHandle + SQL_ATTR_LOGIN_TIMEOUT, //Attribute + &conn->loginTimeout, //ValuePtr + sizeof(conn->loginTimeout)); //StringLength } //Attempt to connect From 0adc2f30e598c173642673997be5f412315ea84e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Apr 2014 17:03:07 -0400 Subject: [PATCH 382/511] Readability for getting the address of members --- src/odbc_connection.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 6bc6e705..4fc7fb30 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -225,7 +225,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { ODBCConnection* self = data->conn->self(); - DEBUG_PRINTF("ODBCConnection::UV_Open : connectTimeout=%i, loginTimeout = %i\n", *&self->connectTimeout, *&self->loginTimeout); + DEBUG_PRINTF("ODBCConnection::UV_Open : connectTimeout=%i, loginTimeout = %i\n", *&(self->connectTimeout), *&(self->loginTimeout)); uv_mutex_lock(&ODBC::g_odbcMutex); @@ -234,7 +234,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { SQLSetConnectAttr( self->m_hDBC, //ConnectionHandle SQL_ATTR_CONNECTION_TIMEOUT, //Attribute - &self->connectTimeout, //ValuePtr + &(self->connectTimeout), //ValuePtr sizeof(self->connectTimeout)); //StringLength } @@ -243,7 +243,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { SQLSetConnectAttr( self->m_hDBC, //ConnectionHandle SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &self->loginTimeout, //ValuePtr + &(self->loginTimeout), //ValuePtr sizeof(self->loginTimeout)); //StringLength } @@ -270,7 +270,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { ret = SQLGetFunctions( self->m_hDBC, SQL_API_SQLMORERESULTS, - &self->canHaveMoreResults); + &(self->canHaveMoreResults)); if (!SQL_SUCCEEDED(ret)) { self->canHaveMoreResults = 0; @@ -344,7 +344,7 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { //get reference to the connection object ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); - DEBUG_PRINTF("ODBCConnection::OpenSync : connectTimeout=%i, loginTimeout = %i\n", *&conn->connectTimeout, *&conn->loginTimeout); + DEBUG_PRINTF("ODBCConnection::OpenSync : connectTimeout=%i, loginTimeout = %i\n", *&(conn->connectTimeout), *&(conn->loginTimeout)); Local objError; SQLRETURN ret; @@ -367,7 +367,7 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { SQLSetConnectAttr( conn->m_hDBC, //ConnectionHandle SQL_ATTR_CONNECTION_TIMEOUT, //Attribute - &conn->connectTimeout, //ValuePtr + &(conn->connectTimeout), //ValuePtr sizeof(conn->connectTimeout));//StringLength } @@ -376,7 +376,7 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { SQLSetConnectAttr( conn->m_hDBC, //ConnectionHandle SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &conn->loginTimeout, //ValuePtr + &(conn->loginTimeout), //ValuePtr sizeof(conn->loginTimeout)); //StringLength } @@ -408,7 +408,7 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { ret = SQLGetFunctions( conn->m_hDBC, SQL_API_SQLMORERESULTS, - &conn->canHaveMoreResults); + &(conn->canHaveMoreResults)); if (!SQL_SUCCEEDED(ret)) { conn->canHaveMoreResults = 0; From e84f3cbff6eb0d1f77627591938e4c77e87b35f5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Apr 2014 18:23:39 -0400 Subject: [PATCH 383/511] Call SQLSetConnectAttr properly Evidently we must pass the value of the connectTimeout or loginTimeout to SQLSetConnectAttr, not a pointer to the value. This is super counter-intuitive because we have to cast the value to a SQLPOINTER in order to get it to even compile. In order to avoid compiler warnings we must cast to size_t before casting to SQLPOINTER. This will properly timeout connections when attempting to connect to a non-running server; at least with the FreeTDS driver. --- src/odbc_connection.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 4fc7fb30..2ae9b867 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -232,19 +232,19 @@ void ODBCConnection::UV_Open(uv_work_t* req) { if (self->connectTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( - self->m_hDBC, //ConnectionHandle - SQL_ATTR_CONNECTION_TIMEOUT, //Attribute - &(self->connectTimeout), //ValuePtr - sizeof(self->connectTimeout)); //StringLength + self->m_hDBC, //ConnectionHandle + SQL_ATTR_CONNECTION_TIMEOUT, //Attribute + (SQLPOINTER) size_t(self->connectTimeout), //ValuePtr + SQL_IS_UINTEGER); //StringLength } if (self->loginTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( - self->m_hDBC, //ConnectionHandle - SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &(self->loginTimeout), //ValuePtr - sizeof(self->loginTimeout)); //StringLength + self->m_hDBC, //ConnectionHandle + SQL_ATTR_LOGIN_TIMEOUT, //Attribute + (SQLPOINTER) size_t(self->loginTimeout), //ValuePtr + SQL_IS_UINTEGER); //StringLength } //Attempt to connect @@ -365,19 +365,19 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { if (conn->connectTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( - conn->m_hDBC, //ConnectionHandle - SQL_ATTR_CONNECTION_TIMEOUT, //Attribute - &(conn->connectTimeout), //ValuePtr - sizeof(conn->connectTimeout));//StringLength + conn->m_hDBC, //ConnectionHandle + SQL_ATTR_CONNECTION_TIMEOUT, //Attribute + (SQLPOINTER) size_t(conn->connectTimeout), //ValuePtr + SQL_IS_UINTEGER); //StringLength } if (conn->loginTimeout > 0) { //NOTE: SQLSetConnectAttr requires the thread to be locked SQLSetConnectAttr( - conn->m_hDBC, //ConnectionHandle - SQL_ATTR_LOGIN_TIMEOUT, //Attribute - &(conn->loginTimeout), //ValuePtr - sizeof(conn->loginTimeout)); //StringLength + conn->m_hDBC, //ConnectionHandle + SQL_ATTR_LOGIN_TIMEOUT, //Attribute + (SQLPOINTER) size_t(conn->loginTimeout), //ValuePtr + SQL_IS_UINTEGER); //StringLength } //Attempt to connect From 622db16181502cb765b6ce86b12bc4e8cc6c6a81 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 23 Apr 2014 13:57:44 -0400 Subject: [PATCH 384/511] Prevent infinite loop when processing string values A corner case occurred which was probably caused by a ODBC driver behaving incorrectly. The first call to SQLGetData would return len == 0 which indicates an empty string. The next time through the loop we would expect SQLGetData to return ret == SQL_NO_DATA. This is not happening in some cases, so an infinite loop occurs. To fix this we check if len == 0 before looping again, if so we break out of the loop. This is a slight optimization because if we know len == 0 then there is no point in calling SQLGetData again to see if there are more data. --- src/odbc.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index a0217712..710e629a 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -568,6 +568,14 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, #endif } + //if len is zero let's break out of the loop now and not attempt to + //call SQLGetData again. The specific reason for this is because + //some ODBC drivers may not correctly report SQL_NO_DATA the next + //time around causing an infinite loop here + if (len == 0) { + break; + } + count += 1; } else { From 9e4cb31f66fe878b523ece60b8864acdfdd4f3c6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 23 Apr 2014 14:09:12 -0400 Subject: [PATCH 385/511] 0.6.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fdcfe21f..e2168160 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.4", + "version": "0.6.5", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 3f288a6c490fbbdea91609b8ec92a010b3883baf Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 23 Apr 2014 18:44:20 -0400 Subject: [PATCH 386/511] Revert "Use C99 designated struct initializer (removes warnings on OSX)" This reverts commit 3cd950154cc74ac5d8117fbe7ddf9e9fb0212bfd. --- src/odbc.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 710e629a..e147e983 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -447,17 +447,17 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } #else struct tm timeInfo = { - .tm_sec = 0 - , .tm_min = 0 - , .tm_hour = 0 - , .tm_mday = 0 - , .tm_mon = 0 - , .tm_year = 0 - , .tm_wday = 0 - , .tm_yday = 0 - , .tm_isdst = 0 - , .tm_gmtoff = 0 - , .tm_zone = 0 + tm_sec : 0 + , tm_min : 0 + , tm_hour : 0 + , tm_mday : 0 + , tm_mon : 0 + , tm_year : 0 + , tm_wday : 0 + , tm_yday : 0 + , tm_isdst : 0 + , tm_gmtoff : 0 + , tm_zone : 0 }; SQL_TIMESTAMP_STRUCT odbcTime; From 4103db45b64dba705532659e1d31c8249f7d15b4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 23 Apr 2014 18:45:35 -0400 Subject: [PATCH 387/511] 0.6.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e2168160..19bfc8f3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.5", + "version": "0.6.6", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 20ba6ccdc6b88d94a2f6deb10c736f87611a22f5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 15 May 2014 19:36:27 -0400 Subject: [PATCH 388/511] Enable setting options in the pool --- lib/odbc.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index bf3266c7..71c970a4 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -663,12 +663,14 @@ module.exports.Pool = Pool; Pool.count = 0; -function Pool () { +function Pool (options) { var self = this; self.index = Pool.count++; self.availablePool = {}; self.usedPool = {}; self.odbc = new odbc.ODBC(); + self.options = options || {} + self.options.odbc = self.odbc; } Pool.prototype.open = function (connectionString, callback) { @@ -684,7 +686,7 @@ Pool.prototype.open = function (connectionString, callback) { callback(null, db); } else { - db = new Database({ odbc : self.odbc }); + db = new Database(self.options); db.realClose = db.close; db.close = function (cb) { From 0830a4ddde4b172df78cd2882de0193e2674af3d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 22 May 2014 14:48:42 -0400 Subject: [PATCH 389/511] Double check that `str` has some data to avoid returning an empty handle --- src/odbc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index e147e983..5fa8cb7f 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -546,6 +546,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, if (SQL_NO_DATA == ret) { //we have captured all of the data + //double check that we have some data else return null + if (str.IsEmpty()){ + return scope.Close(Null()); + } + break; } else if (SQL_SUCCEEDED(ret)) { From 544b138c5920868c5e7023ef5b5dfee7f17e256a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 22 May 2014 14:50:00 -0400 Subject: [PATCH 390/511] Add debug message to ODBCResult destructor --- src/odbc_result.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 8675b3e3..d49a28fc 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -67,6 +67,7 @@ void ODBCResult::Init(v8::Handle target) { } ODBCResult::~ODBCResult() { + DEBUG_PRINTF("ODBCResult::~ODBCResult m_hSTMT=%x\n", m_hSTMT); this->Free(); } From 867ff2b9e06d5e7de0b2472941db330187905d3d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 22 May 2014 14:51:08 -0400 Subject: [PATCH 391/511] Queue the ODBCResult.fetch() method --- lib/odbc.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/odbc.js b/lib/odbc.js index 71c970a4..7b41a65c 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -659,6 +659,23 @@ odbc.ODBCStatement.prototype.bind = function (ary, cb) { }; +//proxy the ODBCResult fetch function so that it is queued +odbc.ODBCResult.prototype._fetch = odbc.ODBCResult.prototype.fetch; + +odbc.ODBCResult.prototype.fetch = function (cb) { + var self = this; + + self.queue = self.queue || new SimpleQueue(); + + self.queue.push(function (next) { + self._fetch(function (err, data) { + if (cb) cb(err, data); + + return next(); + }); + }); +}; + module.exports.Pool = Pool; Pool.count = 0; From 211a8e562201fbd77f6d0d421120d438891537d8 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 22 May 2014 14:51:33 -0400 Subject: [PATCH 392/511] the white spaces --- src/odbc.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 5fa8cb7f..e3f9a595 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -402,7 +402,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Number: index=%i name=%s type=%i len=%i ret=%i val=%f\n", column.index, column.name, column.type, len, ret, value); - if(len == SQL_NULL_DATA) { + if (len == SQL_NULL_DATA) { return scope.Close(Null()); //return Null(); } @@ -430,7 +430,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - W32 Timestamp: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); - if(len == SQL_NULL_DATA) { + if (len == SQL_NULL_DATA) { return scope.Close(Null()); //return Null(); } @@ -473,7 +473,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Unix Timestamp: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); - if(len == SQL_NULL_DATA) { + if (len == SQL_NULL_DATA) { return scope.Close(Null()); //return Null(); } @@ -515,7 +515,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - Bit: index=%i name=%s type=%i len=%i\n", column.index, column.name, column.type, len); - if(len == SQL_NULL_DATA) { + if (len == SQL_NULL_DATA) { return scope.Close(Null()); //return Null(); } @@ -539,7 +539,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); - if(len == SQL_NULL_DATA) { + if (len == SQL_NULL_DATA) { return scope.Close(Null()); //return Null(); } From 20af1ab1f9d4abb29b857e075c9d016c16b9668d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 May 2014 10:48:18 -0400 Subject: [PATCH 393/511] Create a default error message when no diagnostic information available --- src/odbc.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index e3f9a595..85a2f60d 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -821,7 +821,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* SQLINTEGER native; SQLSMALLINT len; - SQLINTEGER numfields; + SQLINTEGER statusRecCount; SQLRETURN ret; char errorSQLState[14]; char errorMessage[ERROR_MESSAGE_BUFFER_BYTES]; @@ -831,15 +831,15 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* handle, 0, SQL_DIAG_NUMBER, - &numfields, + &statusRecCount, SQL_IS_INTEGER, &len); // Windows seems to define SQLINTEGER as long int, unixodbc as just int... %i should cover both - DEBUG_PRINTF("ODBC::GetSQLError : called SQLGetDiagField; ret=%i\n", ret); - - for (i = 0; i < numfields; i++){ - DEBUG_PRINTF("ODBC::GetSQLError : calling SQLGetDiagRec; i=%i, numfields=%i\n", i, numfields); + DEBUG_PRINTF("ODBC::GetSQLError : called SQLGetDiagField; ret=%i, statusRecCount=%i\n", ret, statusRecCount); + + for (i = 0; i < statusRecCount; i++){ + DEBUG_PRINTF("ODBC::GetSQLError : calling SQLGetDiagRec; i=%i, statusRecCount=%i\n", i, statusRecCount); ret = SQLGetDiagRec( handleType, @@ -870,7 +870,15 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* break; } } - + + if (statusRecCount == 0) { + //Create a default error object if there were no diag records + objError->Set(String::New("error"), String::New(message)); + objError->SetPrototype(Exception::Error(String::New(message))); + objError->Set(String::New("message"), String::New( + (const char *) "[node-odbc] An error occurred but no diagnostic information was available.")); + } + return scope.Close(objError); } From a4ccaa38f8b6147512ff02f86edccb3aa8bfaac7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 May 2014 10:52:43 -0400 Subject: [PATCH 394/511] Attempt to use fwprintf when using DEBUG_PRINTF with UNICODE enabled --- src/odbc.cpp | 4 ++-- src/odbc.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 85a2f60d..0153ea3b 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -854,8 +854,8 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* DEBUG_PRINTF("ODBC::GetSQLError : after SQLGetDiagRec; i=%i\n", i); if (SQL_SUCCEEDED(ret)) { - DEBUG_TPRINTF(SQL_T("ODBC::GetSQLError : errorMessage=%s, errorSQLState=%s\n"), errorMessage, errorSQLState); - + DEBUG_PRINTF("ODBC::GetSQLError : errorMessage=%s, errorSQLState=%s\n", errorMessage, errorSQLState); + objError->Set(String::New("error"), String::New(message)); #ifdef UNICODE objError->SetPrototype(Exception::Error(String::New((uint16_t *) errorMessage))); diff --git a/src/odbc.h b/src/odbc.h index 348d902f..6a9c5873 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -160,11 +160,12 @@ struct query_request { #endif #ifdef DEBUG - #define DEBUG_PRINTF(...) fprintf(stdout, __VA_ARGS__) #ifdef UNICODE + #define DEBUG_PRINTF(...) fwprintf(stdout, L##__VA_ARGS__) #define DEBUG_TPRINTF(...) fwprintf(stdout, __VA_ARGS__) #else #define DEBUG_TPRINTF(...) fprintf(stdout, __VA_ARGS__) + #define DEBUG_PRINTF(...) fprintf(stdout, __VA_ARGS__) #endif #else #define DEBUG_PRINTF(...) (void)0 From 5cec256b4cf3a656464f86e8a2960c7a51c13ca5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 May 2014 12:29:10 -0400 Subject: [PATCH 395/511] Don't return Null() when len == SQL_NULL_DATA if we have str data Fixes #60 --- src/odbc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 0153ea3b..45569328 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -539,7 +539,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); - if (len == SQL_NULL_DATA) { + if (len == SQL_NULL_DATA && str.IsEmpty()) { return scope.Close(Null()); //return Null(); } From 744030d7f396cb69dadb70e63a772188ec2ee5e2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 23 May 2014 12:33:02 -0400 Subject: [PATCH 396/511] 0.6.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 19bfc8f3..a9d5c302 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.6", + "version": "0.6.7", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 37dd5e8004c6cc2001db02c330b47a0f98e6bed4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 9 Jun 2014 15:22:12 -0400 Subject: [PATCH 397/511] Concatenate multiple error messages in GetSQLError --- src/odbc.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 45569328..33b0114c 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -816,7 +816,8 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* DEBUG_PRINTF("ODBC::GetSQLError : handleType=%i, handle=%p\n", handleType, handle); Local objError = Object::New(); - + Local str = String::New(""); + SQLINTEGER i = 0; SQLINTEGER native; @@ -858,12 +859,16 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* objError->Set(String::New("error"), String::New(message)); #ifdef UNICODE + str = String::Concat(str, String::New((uint16_t *) errorMessage)); + objError->SetPrototype(Exception::Error(String::New((uint16_t *) errorMessage))); - objError->Set(String::New("message"), String::New((uint16_t *) errorMessage)); + objError->Set(String::New("message"), str); objError->Set(String::New("state"), String::New((uint16_t *) errorSQLState)); #else + str = String::Concat(str, String::New(errorMessage); + objError->SetPrototype(Exception::Error(String::New(errorMessage))); - objError->Set(String::New("message"), String::New(errorMessage)); + objError->Set(String::New("message"), str); objError->Set(String::New("state"), String::New(errorSQLState)); #endif } else if (ret == SQL_NO_DATA) { From 45d00680d5ed70fd7849e3cdf2d675cc7431ee7f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 9 Jun 2014 16:04:22 -0400 Subject: [PATCH 398/511] 0.6.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9d5c302..0ce692e9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.7", + "version": "0.6.8", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 31b20465a5ff6b725f18b7dff5465a569d648bb1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 15 Jun 2014 22:46:25 -0400 Subject: [PATCH 399/511] Fix missing paren when not using UNICODE. Fixes #67 --- src/odbc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 33b0114c..75272cab 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -865,7 +865,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* objError->Set(String::New("message"), str); objError->Set(String::New("state"), String::New((uint16_t *) errorSQLState)); #else - str = String::Concat(str, String::New(errorMessage); + str = String::Concat(str, String::New(errorMessage)); objError->SetPrototype(Exception::Error(String::New(errorMessage))); objError->Set(String::New("message"), str); From bbf20a123d49666d55de68e3de4e15808849a021 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Sun, 15 Jun 2014 22:46:52 -0400 Subject: [PATCH 400/511] 0.6.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ce692e9..e0aaa3e6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.8", + "version": "0.6.9", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 0c4bb92390855a4f51837d1c55380b53e4347c63 Mon Sep 17 00:00:00 2001 From: Xylem Date: Tue, 17 Jun 2014 22:42:02 +0200 Subject: [PATCH 401/511] Fixing memory leak caused by allocating empty array for parameters and not freeing it --- src/odbc.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 75272cab..9c0c85d2 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -663,8 +663,12 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { DEBUG_PRINTF("ODBC::GetParametersFromArray\n"); *paramCount = values->Length(); - Parameter* params = (Parameter *) malloc(*paramCount * sizeof(Parameter)); - + Parameter* params = NULL; + + if (*paramCount > 0) { + params = (Parameter *) malloc(*paramCount * sizeof(Parameter)); + } + for (int i = 0; i < *paramCount; i++) { Local value = values->Get(i); From 18acd6b688e99ec14dcf4f905fa773a461669cf2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 17 Jun 2014 17:24:01 -0400 Subject: [PATCH 402/511] 0.6.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e0aaa3e6..bf471886 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.9", + "version": "0.6.10", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From cdb3044e7e1f91246fecbc8960133123b7e6c1e6 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Jul 2014 11:53:32 -0400 Subject: [PATCH 403/511] Update test to test for issue #70 --- test/test-prepare-bind-execute-long-string.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/test-prepare-bind-execute-long-string.js b/test/test-prepare-bind-execute-long-string.js index 56bd90f0..ee7e65c7 100644 --- a/test/test-prepare-bind-execute-long-string.js +++ b/test/test-prepare-bind-execute-long-string.js @@ -5,9 +5,14 @@ var common = require("./common") ; db.openSync(common.connectionString); -issueQuery(); +issueQuery(100001); +issueQuery(3000); +issueQuery(4000); +issueQuery(5000); +issueQuery(8000); +finish(0); -function issueQuery() { +function issueQuery(length) { var count = 0 , time = new Date().getTime() , stmt @@ -18,7 +23,7 @@ function issueQuery() { var set = 'abcdefghijklmnopqrstuvwxyz'; - for (var x = 0; x < 1000001; x++) { + for (var x = 0; x < length; x++) { str += set[x % set.length]; } @@ -38,9 +43,7 @@ function issueQuery() { data = result.fetchAllSync(); }); -// console.log(data); - console.log(str.length); - console.log(data[0].longString.length); + console.log('expected length: %s, returned length: %s', str.length, data[0].longString.length); for (var x = 0; x < str.length; x++) { if (str[x] != data[0].longString[x]) { @@ -51,8 +54,6 @@ function issueQuery() { } assert.equal(data[0].longString, str); - - finish(0); } function finish(exitCode) { From ffee15847022376d96bd8257b22daa1fa5da689b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Jul 2014 12:43:18 -0400 Subject: [PATCH 404/511] Always use SQL_WLONGVARCHAR or SQL_LONGVARCHAR regardless of string length For issue #70 --- src/odbc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 9c0c85d2..27da0f30 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -686,10 +686,10 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].c_type = SQL_C_TCHAR; #ifdef UNICODE - params[i].type = (length >= 8000) ? SQL_WLONGVARCHAR : SQL_WVARCHAR; + params[i].type = SQL_WLONGVARCHAR; params[i].buffer_length = (length * sizeof(uint16_t)) + sizeof(uint16_t); #else - params[i].type = (length >= 8000) ? SQL_LONGVARCHAR : SQL_VARCHAR; + params[i].type = SQL_LONGVARCHAR; params[i].buffer_length = string->Utf8Length() + 1; #endif params[i].buffer = malloc(params[i].buffer_length); From 17331a1524dc8147f738f7df19f26dc270d77796 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Jul 2014 13:00:16 -0400 Subject: [PATCH 405/511] Update test connections strings so that FreeTDS uses a large TEXTSIZE value --- test/config.testConnectionStrings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/config.testConnectionStrings.json b/test/config.testConnectionStrings.json index 437710d6..85c02d6a 100644 --- a/test/config.testConnectionStrings.json +++ b/test/config.testConnectionStrings.json @@ -1,6 +1,6 @@ [ { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db" } , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;" } - , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test;AutoTranslate=yes" } + , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test;AutoTranslate=yes;TEXTSIZE=10000000" } , { "title" : "MSSQL-NativeCLI-Remote", "connectionString" : "DRIVER={SQL Server Native Client 11.0};SERVER=sql2;DATABASE=test;UID=test;PWD=test;" } ] From 1b0750ece3d284d15e801d4f39744d9cb5fc6d66 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 17 Jul 2014 16:35:19 -0400 Subject: [PATCH 406/511] Update MySQL connection string for driver version 5.3.2 from mysql.com --- test/config.testConnectionStrings.json | 2 +- test/test-global-open-close.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test/config.testConnectionStrings.json b/test/config.testConnectionStrings.json index 85c02d6a..b6c574d8 100644 --- a/test/config.testConnectionStrings.json +++ b/test/config.testConnectionStrings.json @@ -1,6 +1,6 @@ [ { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db" } - , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;USER=test;" } + , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;SOCKET=/var/run/mysqld/mysqld.sock;USER=test;" } , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test;AutoTranslate=yes;TEXTSIZE=10000000" } , { "title" : "MSSQL-NativeCLI-Remote", "connectionString" : "DRIVER={SQL Server Native Client 11.0};SERVER=sql2;DATABASE=test;UID=test;PWD=test;" } ] diff --git a/test/test-global-open-close.js b/test/test-global-open-close.js index 7bb5f2f3..df4aea7d 100644 --- a/test/test-global-open-close.js +++ b/test/test-global-open-close.js @@ -3,6 +3,9 @@ var common = require("./common") , assert = require("assert"); odbc.open(common.connectionString, function (err, conn) { + if (err) { + console.log(err); + } assert.equal(err, null); assert.equal(conn.constructor.name, 'Database'); From c0316d785a478ea30a9fd80c95d4f253c89ebd1e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 21 Jul 2014 12:41:58 -0400 Subject: [PATCH 407/511] 0.6.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bf471886..d2e515f0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.10", + "version": "0.6.11", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From e32b13dcdba7820b6d8ef1b1a776afb34e8c37bf Mon Sep 17 00:00:00 2001 From: Rafee Memon Date: Sun, 20 Jul 2014 15:17:57 -0700 Subject: [PATCH 408/511] Cherry-Pick 03ba6507: Return a date as a string if it fails to parse. Conflicts: src/odbc.cpp --- src/odbc.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 27da0f30..38fd560c 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -435,15 +435,18 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //return Null(); } else { - strptime((char *) buffer, "%Y-%m-%d %H:%M:%S", &timeInfo); - - //a negative value means that mktime() should use timezone information - //and system databases to attempt to determine whether DST is in effect - //at the specified time. - timeInfo.tm_isdst = -1; - - //return Date::New((double(mktime(&timeInfo)) * 1000)); - return scope.Close(Date::New((double(mktime(&timeInfo)) * 1000))); + if (strptime((char *) buffer, "%Y-%m-%d %H:%M:%S", &timeInfo)) { + //a negative value means that mktime() should use timezone information + //and system databases to attempt to determine whether DST is in effect + //at the specified time. + timeInfo.tm_isdst = -1; + + //return Date::New((double(mktime(&timeInfo)) * 1000)); + return scope.Close(Date::New((double(mktime(&timeInfo)) * 1000))); + } + else { + return scope.Close(String::New((char *) buffer)); + } } #else struct tm timeInfo = { From 08f159c66eb986ddd2cb23b0d5b9d21c43d2fc18 Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Thu, 24 Jul 2014 16:56:41 +0100 Subject: [PATCH 409/511] Added SQLSERVER_STRINGS macro to work around fix for #70. * In SQL Server, SQL_WLONGVARCHAR corresponds to the deprecated ntext data type. nvarchar(max) is the preferred option. * To use nvarchar(max), you use SQL_WVARCHAR with a column size of SQL_SS_LENGTH_UNLIMITED (zero). This may or may not probably cause other drivers to send an empty string (?), which is why I've added this as a #define. This isn't the best solution, since it doesn't support connecting using different drivers at the same time, but I don't know what a better solution would be. --- src/odbc.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 27da0f30..e98d4b8f 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -684,17 +684,27 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { Local string = value->ToString(); int length = string->Length(); - params[i].c_type = SQL_C_TCHAR; + params[i].c_type = SQL_C_TCHAR; + params[i].size = params[i].buffer_length; #ifdef UNICODE - params[i].type = SQL_WLONGVARCHAR; +#ifndef SQLSERVER_STRINGS + params[i].type = SQL_WLONGVARCHAR; +#else + params[i].type = SQL_WVARCHAR; + params[i].size = 0; +#endif params[i].buffer_length = (length * sizeof(uint16_t)) + sizeof(uint16_t); #else - params[i].type = SQL_LONGVARCHAR; +#ifndef SQLSERVER_STRINGS + params[i].type = SQL_LONGVARCHAR; +#else + params[i].type = SQL_VARCHAR; + params[i].size = 0; +#endif params[i].buffer_length = string->Utf8Length() + 1; #endif - params[i].buffer = malloc(params[i].buffer_length); - params[i].size = params[i].buffer_length; - params[i].length = SQL_NTS;//params[i].buffer_length; + params[i].buffer = malloc(params[i].buffer_length); + params[i].length = SQL_NTS;//params[i].buffer_length; #ifdef UNICODE string->Write((uint16_t *) params[i].buffer); From 4b28bbf29c1546f7b993a3438cbc24671e3282d1 Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Thu, 24 Jul 2014 16:59:44 +0100 Subject: [PATCH 410/511] Formatting --- src/odbc.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index e98d4b8f..ee3236cb 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -688,23 +688,23 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].size = params[i].buffer_length; #ifdef UNICODE #ifndef SQLSERVER_STRINGS - params[i].type = SQL_WLONGVARCHAR; + params[i].type = SQL_WLONGVARCHAR; #else - params[i].type = SQL_WVARCHAR; - params[i].size = 0; + params[i].type = SQL_WVARCHAR; + params[i].size = 0; #endif params[i].buffer_length = (length * sizeof(uint16_t)) + sizeof(uint16_t); #else #ifndef SQLSERVER_STRINGS - params[i].type = SQL_LONGVARCHAR; + params[i].type = SQL_LONGVARCHAR; #else - params[i].type = SQL_VARCHAR; - params[i].size = 0; + params[i].type = SQL_VARCHAR; + params[i].size = 0; #endif params[i].buffer_length = string->Utf8Length() + 1; #endif - params[i].buffer = malloc(params[i].buffer_length); - params[i].length = SQL_NTS;//params[i].buffer_length; + params[i].buffer = malloc(params[i].buffer_length); + params[i].length = SQL_NTS;//params[i].buffer_length; #ifdef UNICODE string->Write((uint16_t *) params[i].buffer); From 2ed5044fa7cf34f109d1a4fcab8ec3d3db100e7a Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Fri, 25 Jul 2014 12:44:18 +0100 Subject: [PATCH 411/511] Always use SQLExecDirect in UV_Query (works just as well as SQLPrepare, with less overhead) http://msdn.microsoft.com/en-us/library/ms811006.aspx#code-snippet-35 --- src/odbc_connection.cpp | 75 ++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 42 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 2ae9b867..c54d88c4 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -849,52 +849,43 @@ void ODBCConnection::UV_Query(uv_work_t* req) { uv_mutex_unlock(&ODBC::g_odbcMutex); - //check to see if should excute a direct or a parameter bound query - if (!data->paramCount) { - // execute the query directly - ret = SQLExecDirect( - data->hSTMT, - (SQLTCHAR *) data->sql, - data->sqlLen); - } - else { - // prepare statement, bind parameters and execute statement - ret = SQLPrepare( - data->hSTMT, - (SQLTCHAR *) data->sql, - data->sqlLen); - - if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { - for (int i = 0; i < data->paramCount; i++) { - prm = data->params[i]; - - DEBUG_PRINTF( - "ODBCConnection::UV_Query - param[%i]: c_type=%i type=%i " - "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, - prm.buffer_length, prm.size, prm.length, &data->params[i].length); - - ret = SQLBindParameter( - data->hSTMT, //StatementHandle - i + 1, //ParameterNumber - SQL_PARAM_INPUT, //InputOutputType - prm.c_type, //ValueType - prm.type, //ParameterType - prm.size, //ColumnSize - prm.decimals, //DecimalDigits - prm.buffer, //ParameterValuePtr - prm.buffer_length, //BufferLength - //using &prm.length did not work here... - &data->params[i].length); //StrLen_or_IndPtr - - if (ret == SQL_ERROR) {break;} - } - - if (SQL_SUCCEEDED(ret)) { - ret = SQLExecute(data->hSTMT); + // SQLExecDirect will use bound parameters, but without the overhead of SQLPrepare + // for a single execution. + if (data->paramCount) { + for (int i = 0; i < data->paramCount; i++) { + prm = data->params[i]; + + + DEBUG_TPRINTF( + SQL_T("ODBCConnection::UV_Query - param[%i]: c_type=%i type=%i buffer_length=%i size=%i length=%i &length=%X\n"), i, prm.c_type, prm.type, + prm.buffer_length, prm.size, prm.length, &data->params[i].length); + + ret = SQLBindParameter( + data->hSTMT, //StatementHandle + i + 1, //ParameterNumber + SQL_PARAM_INPUT, //InputOutputType + prm.c_type, //ValueType + prm.type, //ParameterType + prm.size, //ColumnSize + prm.decimals, //DecimalDigits + prm.buffer, //ParameterValuePtr + prm.buffer_length, //BufferLength + //using &prm.length did not work here... + &data->params[i].length); //StrLen_or_IndPtr + + if (ret == SQL_ERROR) { + data->result = ret; + return; } } } + // execute the query directly + ret = SQLExecDirect( + data->hSTMT, + (SQLTCHAR *)data->sql, + data->sqlLen); + // this will be checked later in UV_AfterQuery data->result = ret; } From 7761d6652b6e0abb26d3b60f09f94ed182e911a8 Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Fri, 25 Jul 2014 16:07:45 +0100 Subject: [PATCH 412/511] Further SQLExecDirect work (missed QuerySync out), and updated benchmarks. Fixed a couple of cases where many queries were being run concurrently, causing errors. --- src/odbc_connection.cpp | 34 +++++++-------------- test/bench-query-fetch-parameters.js | 44 ++++++++++++++++++++++++++++ test/bench-query-fetch.js | 16 ++++++---- test/bench-query-fetchAll.js | 8 +++-- test/bench-querySync-parameters.js | 27 +++++++++++++++++ 5 files changed, 97 insertions(+), 32 deletions(-) create mode 100644 test/bench-query-fetch-parameters.js create mode 100644 test/bench-querySync-parameters.js diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index c54d88c4..fcc821c1 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -1103,25 +1103,8 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { DEBUG_PRINTF("ODBCConnection::QuerySync - hSTMT=%p\n", hSTMT); - //check to see if should excute a direct or a parameter bound query - if (!SQL_SUCCEEDED(ret)) { - //We'll check again later - } - else if (!paramCount) { - // execute the query directly - ret = SQLExecDirect( - hSTMT, - (SQLTCHAR *) **sql, - sql->length()); - } - else { - // prepare statement, bind parameters and execute statement - ret = SQLPrepare( - hSTMT, - (SQLTCHAR *) **sql, - sql->length()); - - if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { + if (SQL_SUCCEEDED(ret)) { + if (paramCount) { for (int i = 0; i < paramCount; i++) { prm = params[i]; @@ -1145,10 +1128,13 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { if (ret == SQL_ERROR) {break;} } + } - if (SQL_SUCCEEDED(ret)) { - ret = SQLExecute(hSTMT); - } + if (SQL_SUCCEEDED(ret)) { + ret = SQLExecDirect( + hSTMT, + (SQLTCHAR *) **sql, + sql->length()); } // free parameters @@ -1170,7 +1156,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { delete sql; //check to see if there was an error during execution - if(ret == SQL_ERROR) { + if (ret == SQL_ERROR) { ThrowException(ODBC::GetSQLError( SQL_HANDLE_STMT, hSTMT, @@ -1728,4 +1714,4 @@ void ODBCConnection::UV_AfterEndTransaction(uv_work_t* req, int status) { free(req); scope.Close(Undefined()); -} \ No newline at end of file +} diff --git a/test/bench-query-fetch-parameters.js b/test/bench-query-fetch-parameters.js new file mode 100644 index 00000000..746ee5ef --- /dev/null +++ b/test/bench-query-fetch-parameters.js @@ -0,0 +1,44 @@ +var common = require("./common") +, odbc = require("../") +, db = new odbc.Database(); + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , iterations = 10000 + , time = new Date().getTime(); + + function iteration() { + db.query("select ? + ?, ? as test", [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), "This is a string"], cb); + } + + iteration() + + function cb (err, result) { + if (err) { + console.error("query: ", err); + return finish(); + } + + if (++count == iterations) { + var elapsed = new Date().getTime() - time; + + console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); + return finish(); + } else { + iteration(); + } + } + + function finish() { + db.close(function () {}); + } +} diff --git a/test/bench-query-fetch.js b/test/bench-query-fetch.js index ea4d91b4..241589f8 100644 --- a/test/bench-query-fetch.js +++ b/test/bench-query-fetch.js @@ -15,14 +15,16 @@ function issueQuery() { var count = 0 , iterations = 10000 , time = new Date().getTime(); - - for (var x = 0; x < iterations; x++) { - db.queryResult("select 1 + 1 as test", cb); + + function iteration() { + db.queryResult("select 1 + 1 as test", cb); } - + + iteration() + function cb (err, result) { if (err) { - console.error(err); + console.error("queryResult: ", err); return finish(); } @@ -45,6 +47,8 @@ function issueQuery() { console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); return finish(); + } else { + iteration() } } else { @@ -56,4 +60,4 @@ function issueQuery() { function finish() { db.close(function () {}); } -} \ No newline at end of file +} diff --git a/test/bench-query-fetchAll.js b/test/bench-query-fetchAll.js index 04ba202d..30816e82 100644 --- a/test/bench-query-fetchAll.js +++ b/test/bench-query-fetchAll.js @@ -16,9 +16,11 @@ function issueQuery() { , iterations = 10000 , time = new Date().getTime(); - for (var x = 0; x < iterations; x++) { + function iteration() { db.queryResult("select 1 + 1 as test", cb); } + + iteration(); function cb (err, result) { if (err) { @@ -39,6 +41,8 @@ function issueQuery() { console.log("%d queries issued in %d seconds, %d/sec", count, elapsed/1000, Math.floor(count/(elapsed/1000))); return finish(); + } else { + iteration(); } }); } @@ -46,4 +50,4 @@ function issueQuery() { function finish() { db.close(function () {}); } -} \ No newline at end of file +} diff --git a/test/bench-querySync-parameters.js b/test/bench-querySync-parameters.js new file mode 100644 index 00000000..dca0f108 --- /dev/null +++ b/test/bench-querySync-parameters.js @@ -0,0 +1,27 @@ +var common = require("./common") +, odbc = require("../") +, db = new odbc.Database(); + +db.open(common.connectionString, function(err){ + if (err) { + console.error(err); + process.exit(1); + } + + issueQuery(); +}); + +function issueQuery() { + var count = 0 + , iterations = 10000 + , time = new Date().getTime(); + + for (var x = 0; x < iterations; x++) { + db.querySync("select ? + ?, ? as test", [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000), "This is a string"]); + } + + var elapsed = new Date().getTime() - time; + console.log("%d queries issued in %d seconds, %d/sec", iterations, elapsed/1000, Math.floor(iterations/(elapsed/1000))); + + db.close(function () {}); +} From 11cf7a87c7dd9e87dba5d2e1fe320977c5484db8 Mon Sep 17 00:00:00 2001 From: Lee Houghton Date: Fri, 25 Jul 2014 16:11:21 +0100 Subject: [PATCH 413/511] Pass through the STDERR stream of the child process when running benchmarks --- test/run-bench.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/run-bench.js b/test/run-bench.js index c19ab424..1e5181a6 100644 --- a/test/run-bench.js +++ b/test/run-bench.js @@ -32,6 +32,10 @@ function doBench(file, connectionString) { doNextBench(connectionString); }); + bench.stderr.on("data", function (data) { + process.stderr.write(data); + }); + bench.stdout.on("data", function (data) { process.stdout.write(data); }); @@ -72,4 +76,4 @@ function doNextConnectionString() { else { console.log("Done"); } -} \ No newline at end of file +} From ab24063898bdd5de444810eedc8939d0d507e05d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 28 Jul 2014 15:44:01 -0400 Subject: [PATCH 414/511] Remove SQLSERVER_STRINGS define check and always use size 0 for varchars; breaks FreeTDS --- src/odbc.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 4abb3be9..8281cc22 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -688,22 +688,12 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { int length = string->Length(); params[i].c_type = SQL_C_TCHAR; - params[i].size = params[i].buffer_length; + params[i].size = 0; //SQL_SS_LENGTH_UNLIMITED #ifdef UNICODE -#ifndef SQLSERVER_STRINGS - params[i].type = SQL_WLONGVARCHAR; -#else params[i].type = SQL_WVARCHAR; - params[i].size = 0; -#endif params[i].buffer_length = (length * sizeof(uint16_t)) + sizeof(uint16_t); -#else -#ifndef SQLSERVER_STRINGS - params[i].type = SQL_LONGVARCHAR; #else params[i].type = SQL_VARCHAR; - params[i].size = 0; -#endif params[i].buffer_length = string->Utf8Length() + 1; #endif params[i].buffer = malloc(params[i].buffer_length); From 5a5c3a1a189ae526b38d4db430e3472790485388 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 29 Aug 2014 10:29:30 -0400 Subject: [PATCH 415/511] Rename Parameter stuct members to match the parameter names of the SQLBindParameter function I was always try to map in my head what went to what, so it was simpler to just rename the struct members to what they actually are instead of the somewhat cryptic names --- src/odbc.cpp | 90 ++++++++++++++++++++--------------------- src/odbc.h | 14 +++---- src/odbc_connection.cpp | 74 +++++++++++++++++---------------- src/odbc_statement.cpp | 86 +++++++++++++++++++-------------------- 4 files changed, 130 insertions(+), 134 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 8281cc22..18780a7f 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -675,94 +675,94 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { for (int i = 0; i < *paramCount; i++) { Local value = values->Get(i); - params[i].size = 0; - params[i].length = SQL_NULL_DATA; - params[i].buffer_length = 0; - params[i].decimals = 0; + params[i].ColumnSize = 0; + params[i].StrLen_or_IndPtr = SQL_NULL_DATA; + params[i].BufferLength = 0; + params[i].DecimalDigits = 0; DEBUG_PRINTF("ODBC::GetParametersFromArray - ¶m[%i].length = %X\n", - i, ¶ms[i].length); + i, ¶ms[i].StrLen_or_IndPtr); if (value->IsString()) { Local string = value->ToString(); int length = string->Length(); - params[i].c_type = SQL_C_TCHAR; - params[i].size = 0; //SQL_SS_LENGTH_UNLIMITED + params[i].ValueType = SQL_C_TCHAR; + params[i].ColumnSize = 0; //SQL_SS_LENGTH_UNLIMITED #ifdef UNICODE - params[i].type = SQL_WVARCHAR; - params[i].buffer_length = (length * sizeof(uint16_t)) + sizeof(uint16_t); + params[i].ParameterType = SQL_WVARCHAR; + params[i].BufferLength = (length * sizeof(uint16_t)) + sizeof(uint16_t); #else - params[i].type = SQL_VARCHAR; - params[i].buffer_length = string->Utf8Length() + 1; + params[i].ParameterType = SQL_VARCHAR; + params[i].BufferLength = string->Utf8Length() + 1; #endif - params[i].buffer = malloc(params[i].buffer_length); - params[i].length = SQL_NTS;//params[i].buffer_length; + params[i].ParameterValuePtr = malloc(params[i].BufferLength); + params[i].StrLen_or_IndPtr = SQL_NTS;//params[i].BufferLength; #ifdef UNICODE - string->Write((uint16_t *) params[i].buffer); + string->Write((uint16_t *) params[i].ParameterValuePtr); #else - string->WriteUtf8((char *) params[i].buffer); + string->WriteUtf8((char *) params[i].ParameterValuePtr); #endif DEBUG_PRINTF("ODBC::GetParametersFromArray - IsString(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i " - "value=%s\n", i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length, - (char*) params[i].buffer); + "value=%s\n", i, params[i].ValueType, params[i].ParameterType, + params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, + (char*) params[i].ParameterValuePtr); } else if (value->IsNull()) { - params[i].c_type = SQL_C_DEFAULT; - params[i].type = SQL_VARCHAR; - params[i].length = SQL_NULL_DATA; + params[i].ValueType = SQL_C_DEFAULT; + params[i].ParameterType = SQL_VARCHAR; + params[i].StrLen_or_IndPtr = SQL_NULL_DATA; DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNull(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", - i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length); + i, params[i].ValueType, params[i].ParameterType, + params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr); } else if (value->IsInt32()) { int64_t *number = new int64_t(value->IntegerValue()); - params[i].c_type = SQL_C_SBIGINT; - params[i].type = SQL_BIGINT; - params[i].buffer = number; - params[i].length = 0; + params[i].ValueType = SQL_C_SBIGINT; + params[i].ParameterType = SQL_BIGINT; + params[i].ParameterValuePtr = number; + params[i].StrLen_or_IndPtr = 0; DEBUG_PRINTF("ODBC::GetParametersFromArray - IsInt32(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i " - "value=%lld\n", i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length, + "value=%lld\n", i, params[i].ValueType, params[i].ParameterType, + params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, *number); } else if (value->IsNumber()) { double *number = new double(value->NumberValue()); - params[i].c_type = SQL_C_DOUBLE; - params[i].type = SQL_DECIMAL; - params[i].buffer = number; - params[i].buffer_length = sizeof(double); - params[i].length = params[i].buffer_length; - params[i].decimals = 7; - params[i].size = sizeof(double); + params[i].ValueType = SQL_C_DOUBLE; + params[i].ParameterType = SQL_DECIMAL; + params[i].ParameterValuePtr = number; + params[i].BufferLength = sizeof(double); + params[i].StrLen_or_IndPtr = params[i].BufferLength; + params[i].DecimalDigits = 7; + params[i].ColumnSize = sizeof(double); DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i " "value=%f\n", - i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length, + i, params[i].ValueType, params[i].ParameterType, + params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, *number); } else if (value->IsBoolean()) { - bool *boolean = new bool(value->BooleanValue()); - params[i].c_type = SQL_C_BIT; - params[i].type = SQL_BIT; - params[i].buffer = boolean; - params[i].length = 0; + bool *boolean = new bool(value->BooleanValue()); + params[i].ValueType = SQL_C_BIT; + params[i].ParameterType = SQL_BIT; + params[i].ParameterValuePtr = boolean; + params[i].StrLen_or_IndPtr = 0; DEBUG_PRINTF("ODBC::GetParametersFromArray - IsBoolean(): params[%i] " "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", - i, params[i].c_type, params[i].type, - params[i].buffer_length, params[i].size, params[i].length); + i, params[i].ValueType, params[i].ParameterType, + params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr); } } diff --git a/src/odbc.h b/src/odbc.h index 6a9c5873..6f5bc15f 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -61,13 +61,13 @@ typedef struct { } Column; typedef struct { - SQLSMALLINT c_type; - SQLSMALLINT type; - SQLLEN size; - void *buffer; - SQLLEN buffer_length; - SQLLEN length; - SQLSMALLINT decimals; + SQLSMALLINT ValueType; + SQLSMALLINT ParameterType; + SQLLEN ColumnSize; + SQLSMALLINT DecimalDigits; + void *ParameterValuePtr; + SQLLEN BufferLength; + SQLLEN StrLen_or_IndPtr; } Parameter; class ODBC : public node::ObjectWrap { diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index fcc821c1..0c38d200 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -857,21 +857,20 @@ void ODBCConnection::UV_Query(uv_work_t* req) { DEBUG_TPRINTF( - SQL_T("ODBCConnection::UV_Query - param[%i]: c_type=%i type=%i buffer_length=%i size=%i length=%i &length=%X\n"), i, prm.c_type, prm.type, - prm.buffer_length, prm.size, prm.length, &data->params[i].length); + SQL_T("ODBCConnection::UV_Query - param[%i]: ValueType=%i type=%i BufferLength=%i size=%i length=%i &length=%X\n"), i, prm.ValueType, prm.ParameterType, + prm.BufferLength, prm.ColumnSize, prm.length, &data->params[i].length); ret = SQLBindParameter( - data->hSTMT, //StatementHandle - i + 1, //ParameterNumber - SQL_PARAM_INPUT, //InputOutputType - prm.c_type, //ValueType - prm.type, //ParameterType - prm.size, //ColumnSize - prm.decimals, //DecimalDigits - prm.buffer, //ParameterValuePtr - prm.buffer_length, //BufferLength - //using &prm.length did not work here... - &data->params[i].length); //StrLen_or_IndPtr + data->hSTMT, //StatementHandle + i + 1, //ParameterNumber + SQL_PARAM_INPUT, //InputOutputType + prm.ValueType, + prm.ParameterType, + prm.ColumnSize, + prm.DecimalDigits, + prm.ParameterValuePtr, + prm.BufferLength, + &data->params[i].StrLen_or_IndPtr); if (ret == SQL_ERROR) { data->result = ret; @@ -953,13 +952,13 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { Parameter prm; // free parameters for (int i = 0; i < data->paramCount; i++) { - if (prm = data->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_WCHAR: free(prm.buffer); break; - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_LONG: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; + if (prm = data->params[i], prm.ParameterValuePtr != NULL) { + switch (prm.ValueType) { + case SQL_C_WCHAR: free(prm.ParameterValuePtr); break; + case SQL_C_CHAR: free(prm.ParameterValuePtr); break; + case SQL_C_LONG: delete (int64_t *)prm.ParameterValuePtr; break; + case SQL_C_DOUBLE: delete (double *)prm.ParameterValuePtr; break; + case SQL_C_BIT: delete (bool *)prm.ParameterValuePtr; break; } } } @@ -1109,22 +1108,21 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { prm = params[i]; DEBUG_PRINTF( - "ODBCConnection::UV_Query - param[%i]: c_type=%i type=%i " - "buffer_length=%i size=%i length=%i &length=%X\n", i, prm.c_type, prm.type, - prm.buffer_length, prm.size, prm.length, ¶ms[i].length); + "ODBCConnection::UV_Query - param[%i]: ValueType=%i type=%i " + "BufferLength=%i size=%i length=%i &length=%X\n", i, prm.ValueType, prm.ParameterType, + prm.BufferLength, prm.ColumnSize, prm.StrLen_or_IndPtr, ¶ms[i].StrLen_or_IndPtr); ret = SQLBindParameter( hSTMT, //StatementHandle i + 1, //ParameterNumber SQL_PARAM_INPUT, //InputOutputType - prm.c_type, //ValueType - prm.type, //ParameterType - prm.size, //ColumnSize - prm.decimals, //DecimalDigits - prm.buffer, //ParameterValuePtr - prm.buffer_length, //BufferLength - //using &prm.length did not work here... - ¶ms[i].length); //StrLen_or_IndPtr + prm.ValueType, + prm.ParameterType, + prm.ColumnSize, + prm.DecimalDigits, + prm.ParameterValuePtr, + prm.BufferLength, + ¶ms[i].StrLen_or_IndPtr); if (ret == SQL_ERROR) {break;} } @@ -1139,13 +1137,13 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { // free parameters for (int i = 0; i < paramCount; i++) { - if (prm = params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_WCHAR: free(prm.buffer); break; - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_LONG: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; + if (prm = params[i], prm.ParameterValuePtr != NULL) { + switch (prm.ValueType) { + case SQL_C_WCHAR: free(prm.ParameterValuePtr); break; + case SQL_C_CHAR: free(prm.ParameterValuePtr); break; + case SQL_C_LONG: delete (int64_t *)prm.ParameterValuePtr; break; + case SQL_C_DOUBLE: delete (double *)prm.ParameterValuePtr; break; + case SQL_C_BIT: delete (bool *)prm.ParameterValuePtr; break; } } } diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 42b702d7..3e7b04fc 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -85,13 +85,13 @@ void ODBCStatement::Free() { //free parameter memory for (int i = 0; i < count; i++) { - if (prm = params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_WCHAR: free(prm.buffer); break; - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; + if (prm = params[i], prm.ParameterValuePtr != NULL) { + switch (prm.ValueType) { + case SQL_C_WCHAR: free(prm.ParameterValuePtr); break; + case SQL_C_CHAR: free(prm.ParameterValuePtr); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.ParameterValuePtr; break; + case SQL_C_DOUBLE: delete (double *)prm.ParameterValuePtr; break; + case SQL_C_BIT: delete (bool *)prm.ParameterValuePtr; break; } } } @@ -774,13 +774,13 @@ Handle ODBCStatement::BindSync(const Arguments& args) { //free parameter memory for (int i = 0; i < count; i++) { - if (prm = stmt->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_WCHAR: free(prm.buffer); break; - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; + if (prm = stmt->params[i], prm.ParameterValuePtr != NULL) { + switch (prm.ValueType) { + case SQL_C_WCHAR: free(prm.ParameterValuePtr); break; + case SQL_C_CHAR: free(prm.ParameterValuePtr); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.ParameterValuePtr; break; + case SQL_C_DOUBLE: delete (double *)prm.ParameterValuePtr; break; + case SQL_C_BIT: delete (bool *)prm.ParameterValuePtr; break; } } } @@ -801,22 +801,21 @@ Handle ODBCStatement::BindSync(const Arguments& args) { DEBUG_PRINTF( "ODBCStatement::BindSync - param[%i]: c_type=%i type=%i " "buffer_length=%i size=%i length=%i &length=%X decimals=%i value=%s\n", - i, prm.c_type, prm.type, prm.buffer_length, prm.size, prm.length, - &stmt->params[i].length, prm.decimals, prm.buffer + i, prm.ValueType, prm.ParameterType, prm.BufferLength, prm.ColumnSize, prm.length, + &stmt->params[i].StrLen_or_IndPtr, prm.DecimalDigits, prm.ParameterValuePtr ); ret = SQLBindParameter( - stmt->m_hSTMT, //StatementHandle - i + 1, //ParameterNumber - SQL_PARAM_INPUT, //InputOutputType - prm.c_type, //ValueType - prm.type, //ParameterType - prm.size, //ColumnSize - prm.decimals, //DecimalDigits - prm.buffer, //ParameterValuePtr - prm.buffer_length, //BufferLength - //using &prm.length did not work here... - &stmt->params[i].length); //StrLen_or_IndPtr + stmt->m_hSTMT, //StatementHandle + i + 1, //ParameterNumber + SQL_PARAM_INPUT, //InputOutputType + prm.ValueType, + prm.ParameterType, + prm.ColumnSize, + prm.DecimalDigits, + prm.ParameterValuePtr, + prm.BufferLength, + &stmt->params[i].StrLen_or_IndPtr); if (ret == SQL_ERROR) { break; @@ -874,13 +873,13 @@ Handle ODBCStatement::Bind(const Arguments& args) { //free parameter memory for (int i = 0; i < count; i++) { - if (prm = stmt->params[i], prm.buffer != NULL) { - switch (prm.c_type) { - case SQL_C_WCHAR: free(prm.buffer); break; - case SQL_C_CHAR: free(prm.buffer); break; - case SQL_C_SBIGINT: delete (int64_t *)prm.buffer; break; - case SQL_C_DOUBLE: delete (double *)prm.buffer; break; - case SQL_C_BIT: delete (bool *)prm.buffer; break; + if (prm = stmt->params[i], prm.ParameterValuePtr != NULL) { + switch (prm.ValueType) { + case SQL_C_WCHAR: free(prm.ParameterValuePtr); break; + case SQL_C_CHAR: free(prm.ParameterValuePtr); break; + case SQL_C_SBIGINT: delete (int64_t *)prm.ParameterValuePtr; break; + case SQL_C_DOUBLE: delete (double *)prm.ParameterValuePtr; break; + case SQL_C_BIT: delete (bool *)prm.ParameterValuePtr; break; } } } @@ -935,22 +934,21 @@ void ODBCStatement::UV_Bind(uv_work_t* req) { DEBUG_PRINTF( "ODBCStatement::UV_Bind - param[%i]: c_type=%i type=%i " "buffer_length=%i size=%i length=%i &length=%X decimals=%i value=%s\n", - i, prm.c_type, prm.type, prm.buffer_length, prm.size, prm.length, - &data->stmt->params[i].length, prm.decimals, prm.buffer + i, prm.ValueType, prm.ParameterType, prm.BufferLength, prm.ColumnSize, prm.length, + &data->stmt->params[i].StrLen_or_IndPtr, prm.DecimalDigits, prm.ParameterValuePtr ); ret = SQLBindParameter( data->stmt->m_hSTMT, //StatementHandle i + 1, //ParameterNumber SQL_PARAM_INPUT, //InputOutputType - prm.c_type, //ValueType - prm.type, //ParameterType - prm.size, //ColumnSize - prm.decimals, //DecimalDigits - prm.buffer, //ParameterValuePtr - prm.buffer_length, //BufferLength - //using &prm.length did not work here... - &data->stmt->params[i].length); //StrLen_or_IndPtr + prm.ValueType, + prm.ParameterType, + prm.ColumnSize, + prm.DecimalDigits, + prm.ParameterValuePtr, + prm.BufferLength, + &data->stmt->params[i].StrLen_or_IndPtr); if (ret == SQL_ERROR) { break; From a5600952e0216e783ba057be19b77a0bb6c63dee Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 10 Sep 2014 12:02:42 -0400 Subject: [PATCH 416/511] Check to see if self.conn still exists before calling close method; fixes #69 --- lib/odbc.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/odbc.js b/lib/odbc.js index 7b41a65c..dd9dc4c6 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -160,6 +160,12 @@ Database.prototype.close = function (cb) { var self = this; self.queue.push(function (next) { + //check to see if conn still exists (it's deleted when closed) + if (!self.conn) { + if (cb) cb(null); + return next(); + } + self.conn.close(function (err) { self.connected = false; delete self.conn; From 60f5142c9c13724cbb29715b8d08e0654ed178a7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 10 Sep 2014 12:04:21 -0400 Subject: [PATCH 417/511] 0.6.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2e515f0..5f6d5e76 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.11", + "version": "0.6.12", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 52f918e2ade60371c3a28312d8023006eadf8340 Mon Sep 17 00:00:00 2001 From: Xylem Date: Fri, 27 Feb 2015 16:17:45 +0000 Subject: [PATCH 418/511] Fixed leaking V8 handles through unnecessary HandleScope.Close() calls --- src/odbc.cpp | 4 ---- src/odbc_connection.cpp | 14 +------------- src/odbc_result.cpp | 4 ---- src/odbc_statement.cpp | 12 ------------ 4 files changed, 1 insertion(+), 33 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 27da0f30..1377ee75 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -74,8 +74,6 @@ void ODBC::Init(v8::Handle target) { target->Set( v8::String::NewSymbol("ODBC"), constructor_template->GetFunction()); - scope.Close(Undefined()); - #if NODE_VERSION_AT_LEAST(0, 7, 9) // Initialize uv_async so that we can prevent node from exiting uv_async_init( uv_default_loop(), @@ -231,8 +229,6 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { free(data); free(req); - - scope.Close(Undefined()); } /* diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 2ae9b867..9befb7d5 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -76,8 +76,6 @@ void ODBCConnection::Init(v8::Handle target) { // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCConnection"), constructor_template->GetFunction()); - - scope.Close(Undefined()); } ODBCConnection::~ODBCConnection() { @@ -328,7 +326,6 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { free(data->connection); free(data); free(req); - scope.Close(Undefined()); } /* @@ -526,7 +523,6 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { free(data); free(req); - scope.Close(Undefined()); } /* @@ -689,8 +685,6 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { free(data); free(req); - - scope.Close(Undefined()); } /* @@ -984,8 +978,6 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { free(data->column); free(data); free(req); - - scope.Close(Undefined()); } @@ -1553,8 +1545,6 @@ void ODBCConnection::UV_AfterBeginTransaction(uv_work_t* req, int status) { free(data); free(req); - - scope.Close(Undefined()); } /* @@ -1735,6 +1725,4 @@ void ODBCConnection::UV_AfterEndTransaction(uv_work_t* req, int status) { free(data); free(req); - - scope.Close(Undefined()); -} \ No newline at end of file +} diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index d49a28fc..aa0c77b2 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -62,8 +62,6 @@ void ODBCResult::Init(v8::Handle target) { // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCResult"), constructor_template->GetFunction()); - - scope.Close(Undefined()); } ODBCResult::~ODBCResult() { @@ -574,8 +572,6 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { self->Unref(); } - - scope.Close(Undefined()); } /* diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 42b702d7..96f6d368 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -66,8 +66,6 @@ void ODBCStatement::Init(v8::Handle target) { // Attach the Database Constructor to the target object target->Set( v8::String::NewSymbol("ODBCStatement"), constructor_template->GetFunction()); - - scope.Close(Undefined()); } ODBCStatement::~ODBCStatement() { @@ -238,8 +236,6 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { free(data); free(req); - - scope.Close(Undefined()); } /* @@ -376,8 +372,6 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { free(data); free(req); - - scope.Close(Undefined()); } /* @@ -528,8 +522,6 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { free(data->sql); free(data); free(req); - - scope.Close(Undefined()); } /* @@ -736,8 +728,6 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { free(data->sql); free(data); free(req); - - scope.Close(Undefined()); } /* @@ -997,8 +987,6 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { free(data); free(req); - - scope.Close(Undefined()); } /* From 7e55b310464275fc744da0f36be716648855a55e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 27 Feb 2015 15:28:01 -0500 Subject: [PATCH 419/511] 0.6.13 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5f6d5e76..24a40dba 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.12", + "version": "0.6.13", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 04833e4143782412e40273f07c352d88516dc575 Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Sat, 18 Apr 2015 08:44:02 -0700 Subject: [PATCH 420/511] Converted a function to NAN witout node v10 breakage --- binding.gyp | 3 +++ src/odbc_connection.cpp | 7 ++++--- src/odbc_connection.h | 6 +++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/binding.gyp b/binding.gyp index cb1e8ddb..77c59a02 100644 --- a/binding.gyp +++ b/binding.gyp @@ -8,6 +8,9 @@ 'src/odbc_statement.cpp', 'src/odbc_result.cpp', 'src/dynodbc.cpp' + ], + 'include_dirs': [ + " property, Local val * */ -Handle ODBCConnection::Open(const Arguments& args) { +//Handle ODBCConnection::Open(const Arguments& args) { +NAN_METHOD(ODBCConnection::Open) { DEBUG_PRINTF("ODBCConnection::Open\n"); - HandleScope scope; + NanScope(); REQ_STRO_ARG(0, connection); REQ_FUN_ARG(1, cb); @@ -214,7 +215,7 @@ Handle ODBCConnection::Open(const Arguments& args) { conn->Ref(); - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } void ODBCConnection::UV_Open(uv_work_t* req) { diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 72b3c4df..80b59cbf 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -18,6 +18,8 @@ #ifndef _SRC_ODBC_CONNECTION_H #define _SRC_ODBC_CONNECTION_H +#include + class ODBCConnection : public node::ObjectWrap { public: static Persistent OPTION_SQL; @@ -58,7 +60,9 @@ class ODBCConnection : public node::ObjectWrap { static void UV_EndTransaction(uv_work_t* work_req); static void UV_AfterEndTransaction(uv_work_t* work_req, int status); - static Handle Open(const Arguments& args); + + //test + static NAN_METHOD(Open); static void UV_Open(uv_work_t* work_req); static void UV_AfterOpen(uv_work_t* work_req, int status); From 06e7abd5b27198580cc3ac20db88b600fe605f92 Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Sat, 18 Apr 2015 17:42:08 -0700 Subject: [PATCH 421/511] Made all obvious nan changes to odbc_connection. --- src/odbc_connection.cpp | 224 ++++++++++++++++++++-------------------- src/odbc_connection.h | 39 ++++--- 2 files changed, 130 insertions(+), 133 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 4ad309e9..45044751 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -31,29 +31,29 @@ using namespace v8; using namespace node; Persistent ODBCConnection::constructor_template; -Persistent ODBCConnection::OPTION_SQL = Persistent::New(String::New("sql")); -Persistent ODBCConnection::OPTION_PARAMS = Persistent::New(String::New("params")); -Persistent ODBCConnection::OPTION_NORESULTS = Persistent::New(String::New("noResults")); +Persistent ODBCConnection::OPTION_SQL = Persistent::New(NanNew("sql")); +Persistent ODBCConnection::OPTION_PARAMS = Persistent::New(NanNew("params")); +Persistent ODBCConnection::OPTION_NORESULTS = Persistent::New(NanNew("noResults")); -void ODBCConnection::Init(v8::Handle target) { +void ODBCConnection::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCConnection::Init\n"); - HandleScope scope; + NanScope(); - Local t = FunctionTemplate::New(New); + Local t = NanNew(New); // Constructor Template - constructor_template = Persistent::New(t); - constructor_template->SetClassName(String::NewSymbol("ODBCConnection")); + NanAssignPersistent(constructor_template, t); + constructor_template->SetClassName(NanNew("ODBCConnection")); // Reserve space for one Handle Local instance_template = constructor_template->InstanceTemplate(); instance_template->SetInternalFieldCount(1); // Properties - //instance_template->SetAccessor(String::New("mode"), ModeGetter, ModeSetter); - instance_template->SetAccessor(String::New("connected"), ConnectedGetter); - instance_template->SetAccessor(String::New("connectTimeout"), ConnectTimeoutGetter, ConnectTimeoutSetter); - instance_template->SetAccessor(String::New("loginTimeout"), LoginTimeoutGetter, LoginTimeoutSetter); + //instance_template->SetAccessor(NanNew("mode"), ModeGetter, ModeSetter); + instance_template->SetAccessor(NanNew("connected"), ConnectedGetter); + instance_template->SetAccessor(NanNew("connectTimeout"), ConnectTimeoutGetter, ConnectTimeoutSetter); + instance_template->SetAccessor(NanNew("loginTimeout"), LoginTimeoutGetter, LoginTimeoutSetter); // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open); @@ -74,7 +74,7 @@ void ODBCConnection::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "tables", Tables); // Attach the Database Constructor to the target object - target->Set( v8::String::NewSymbol("ODBCConnection"), + exports->Set( NanNew("ODBCConnection"), constructor_template->GetFunction()); } @@ -102,9 +102,9 @@ void ODBCConnection::Free() { * New */ -Handle ODBCConnection::New(const Arguments& args) { +NAN_METHOD(ODBCConnection::New) { DEBUG_PRINTF("ODBCConnection::New\n"); - HandleScope scope; + NanScope(); REQ_EXT_ARG(0, js_henv); REQ_EXT_ARG(1, js_hdbc); @@ -121,47 +121,47 @@ Handle ODBCConnection::New(const Arguments& args) { //set default loginTimeout to 5 seconds conn->loginTimeout = 5; - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } -Handle ODBCConnection::ConnectedGetter(Local property, const AccessorInfo &info) { - HandleScope scope; +NAN_GETTER(ODBCConnection::ConnectedGetter) { + NanScope(); - ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); - return scope.Close(obj->connected ? True() : False()); + NanReturnValue(obj->connected ? NanTrue() : NanFalse()); } -Handle ODBCConnection::ConnectTimeoutGetter(Local property, const AccessorInfo &info) { - HandleScope scope; +NAN_GETTER(ODBCConnection::ConnectTimeoutGetter) { + NanScope(); - ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); - return scope.Close(Number::New(obj->connectTimeout)); + NanReturnValue(Number::New(obj->connectTimeout)); } -void ODBCConnection::ConnectTimeoutSetter(Local property, Local value, const AccessorInfo &info) { - HandleScope scope; +NAN_SETTER(ODBCConnection::ConnectTimeoutSetter) { + NanScope(); - ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); if (value->IsNumber()) { obj->connectTimeout = value->Uint32Value(); } } -Handle ODBCConnection::LoginTimeoutGetter(Local property, const AccessorInfo &info) { - HandleScope scope; +NAN_GETTER(ODBCConnection::LoginTimeoutGetter) { + NanScope(); - ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); - return scope.Close(Number::New(obj->loginTimeout)); + NanReturnValue(Number::New(obj->loginTimeout)); } -void ODBCConnection::LoginTimeoutSetter(Local property, Local value, const AccessorInfo &info) { - HandleScope scope; +NAN_SETTER(ODBCConnection::LoginTimeoutSetter) { + NanScope(); - ODBCConnection *obj = ObjectWrap::Unwrap(info.Holder()); + ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); if (value->IsNumber()) { obj->loginTimeout = value->Uint32Value(); @@ -286,7 +286,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterOpen\n"); - HandleScope scope; + NanScope(); open_connection_work_data* data = (open_connection_work_data *)(req->data); @@ -333,9 +333,9 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { * OpenSync */ -Handle ODBCConnection::OpenSync(const Arguments& args) { +NAN_METHOD(ODBCConnection::OpenSync) { DEBUG_PRINTF("ODBCConnection::OpenSync\n"); - HandleScope scope; + NanScope(); REQ_STRO_ARG(0, connection); @@ -431,10 +431,10 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { if (err) { ThrowException(objError); - return scope.Close(False()); + NanReturnValue(NanFalse()); } else { - return scope.Close(True()); + NanReturnValue(NanTrue()); } } @@ -443,9 +443,9 @@ Handle ODBCConnection::OpenSync(const Arguments& args) { * */ -Handle ODBCConnection::Close(const Arguments& args) { +NAN_METHOD(ODBCConnection::Close) { DEBUG_PRINTF("ODBCConnection::Close\n"); - HandleScope scope; + NanScope(); REQ_FUN_ARG(0, cb); @@ -469,7 +469,7 @@ Handle ODBCConnection::Close(const Arguments& args) { conn->Ref(); - return scope.Close(Undefined()); + NanReturnValue(Undefined()); } void ODBCConnection::UV_Close(uv_work_t* req) { @@ -487,7 +487,7 @@ void ODBCConnection::UV_Close(uv_work_t* req) { void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterClose\n"); - HandleScope scope; + NanScope(); close_connection_work_data* data = (close_connection_work_data *)(req->data); @@ -498,7 +498,7 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { if (data->result) { err = true; - argv[0] = Exception::Error(String::New("Error closing database")); + argv[0] = Exception::Error(NanNew("Error closing database")); } else { conn->connected = false; @@ -530,9 +530,9 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { * CloseSync */ -Handle ODBCConnection::CloseSync(const Arguments& args) { +NAN_METHOD(ODBCConnection::CloseSync) { DEBUG_PRINTF("ODBCConnection::CloseSync\n"); - HandleScope scope; + NanScope(); ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -549,7 +549,7 @@ Handle ODBCConnection::CloseSync(const Arguments& args) { uv_unref(uv_default_loop()); #endif - return scope.Close(True()); + NanReturnValue(NanTrue()); } /* @@ -557,9 +557,9 @@ Handle ODBCConnection::CloseSync(const Arguments& args) { * */ -Handle ODBCConnection::CreateStatementSync(const Arguments& args) { +NAN_METHOD(ODBCConnection::CreateStatementSync) { DEBUG_PRINTF("ODBCConnection::CreateStatementSync\n"); - HandleScope scope; + NanScope(); ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -582,7 +582,7 @@ Handle ODBCConnection::CreateStatementSync(const Arguments& args) { Local js_result(ODBCStatement::constructor_template-> GetFunction()->NewInstance(3, params)); - return scope.Close(js_result); + NanReturnValue(js_result); } /* @@ -590,9 +590,9 @@ Handle ODBCConnection::CreateStatementSync(const Arguments& args) { * */ -Handle ODBCConnection::CreateStatement(const Arguments& args) { +NAN_METHOD(ODBCConnection::CreateStatement) { DEBUG_PRINTF("ODBCConnection::CreateStatement\n"); - HandleScope scope; + NanScope(); REQ_FUN_ARG(0, cb); @@ -618,7 +618,7 @@ Handle ODBCConnection::CreateStatement(const Arguments& args) { conn->Ref(); - return scope.Close(Undefined()); + NanReturnValue(Undefined()); } void ODBCConnection::UV_CreateStatement(uv_work_t* req) { @@ -651,7 +651,7 @@ void ODBCConnection::UV_CreateStatement(uv_work_t* req) { void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterCreateStatement\n"); - HandleScope scope; + NanScope(); create_statement_work_data* data = (create_statement_work_data *)(req->data); @@ -692,10 +692,9 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { * Query */ -Handle ODBCConnection::Query(const Arguments& args) { +NAN_METHOD(ODBCConnection::Query) { DEBUG_PRINTF("ODBCConnection::Query\n"); - - HandleScope scope; + NanScope(); Local cb; @@ -713,17 +712,17 @@ Handle ODBCConnection::Query(const Arguments& args) { if ( !args[0]->IsString() ) { return ThrowException(Exception::TypeError( - String::New("Argument 0 must be an String.") + NanNew("Argument 0 must be an String.") )); } else if ( !args[1]->IsArray() ) { return ThrowException(Exception::TypeError( - String::New("Argument 1 must be an Array.") + NanNew("Argument 1 must be an Array.") )); } else if ( !args[2]->IsFunction() ) { return ThrowException(Exception::TypeError( - String::New("Argument 2 must be a Function.") + NanNew("Argument 2 must be a Function.") )); } @@ -740,7 +739,7 @@ Handle ODBCConnection::Query(const Arguments& args) { if (!args[1]->IsFunction()) { return ThrowException(Exception::TypeError( - String::New("ODBCConnection::Query(): Argument 1 must be a Function.")) + NanNew("ODBCConnection::Query(): Argument 1 must be a Function.")) ); } @@ -765,7 +764,7 @@ Handle ODBCConnection::Query(const Arguments& args) { sql = obj->Get(OPTION_SQL)->ToString(); } else { - sql = String::New(""); + sql = NanNew(""); } if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { @@ -786,13 +785,13 @@ Handle ODBCConnection::Query(const Arguments& args) { } else { return ThrowException(Exception::TypeError( - String::New("ODBCConnection::Query(): Argument 0 must be a String or an Object.")) + NanNew("ODBCConnection::Query(): Argument 0 must be a String or an Object.")) ); } } else { return ThrowException(Exception::TypeError( - String::New("ODBCConnection::Query(): Requires either 2 or 3 Arguments. ")) + NanNew("ODBCConnection::Query(): Requires either 2 or 3 Arguments. ")) ); } //Done checking arguments @@ -887,7 +886,7 @@ void ODBCConnection::UV_Query(uv_work_t* req) { void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterQuery\n"); - HandleScope scope; + NanScope(); query_work_data* data = (query_work_data *)(req->data); @@ -898,7 +897,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { if (data->result != SQL_ERROR && data->noResultObject) { //We have been requested to not create a result object //this means we should release the handle now and call back - //with True() + //with NanTrue() uv_mutex_lock(&ODBC::g_odbcMutex); @@ -908,7 +907,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { Local args[2]; args[0] = Local::New(Null()); - args[1] = Local::New(True()); + args[1] = Local::New(NanTrue()); data->cb->Call(Context::GetCurrent()->Global(), 2, args); } @@ -976,10 +975,9 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { * QuerySync */ -Handle ODBCConnection::QuerySync(const Arguments& args) { +NAN_METHOD(ODBCConnection::QuerySync) { DEBUG_PRINTF("ODBCConnection::QuerySync\n"); - - HandleScope scope; + NanScope(); #ifdef UNICODE String::Value* sql; @@ -1002,12 +1000,12 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { if ( !args[0]->IsString() ) { return ThrowException(Exception::TypeError( - String::New("ODBCConnection::QuerySync(): Argument 0 must be an String.") + NanNew("ODBCConnection::QuerySync(): Argument 0 must be an String.") )); } else if (!args[1]->IsArray()) { return ThrowException(Exception::TypeError( - String::New("ODBCConnection::QuerySync(): Argument 1 must be an Array.") + NanNew("ODBCConnection::QuerySync(): Argument 1 must be an Array.") )); } @@ -1052,9 +1050,9 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { } else { #ifdef UNICODE - sql = new String::Value(String::New("")); + sql = new String::Value(NanNew("")); #else - sql = new String::Utf8Value(String::New("")); + sql = new String::Utf8Value(NanNew("")); #endif } @@ -1073,13 +1071,13 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { } else { return ThrowException(Exception::TypeError( - String::New("ODBCConnection::QuerySync(): Argument 0 must be a String or an Object.")) + NanNew("ODBCConnection::QuerySync(): Argument 0 must be a String or an Object.")) ); } } else { return ThrowException(Exception::TypeError( - String::New("ODBCConnection::QuerySync(): Requires either 1 or 2 Arguments. ")) + NanNew("ODBCConnection::QuerySync(): Requires either 1 or 2 Arguments. ")) ); } //Done checking arguments @@ -1154,7 +1152,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { (char *) "[node-odbc] Error in ODBCConnection::QuerySync" )); - return scope.Close(Undefined()); + NanReturnValue(Undefined()); } else if (noResultObject) { //if there is not result object requested then @@ -1165,7 +1163,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { uv_mutex_unlock(&ODBC::g_odbcMutex); - return scope.Close(True()); + NanReturnValue(NanTrue()); } else { Local args[4]; @@ -1179,7 +1177,7 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { Local js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); - return scope.Close(js_result); + NanReturnValue(js_result); } } @@ -1187,8 +1185,8 @@ Handle ODBCConnection::QuerySync(const Arguments& args) { * Tables */ -Handle ODBCConnection::Tables(const Arguments& args) { - HandleScope scope; +NAN_METHOD(ODBCConnection::Tables) { + NanScope(); REQ_STRO_OR_NULL_ARG(0, catalog); REQ_STRO_OR_NULL_ARG(1, schema); @@ -1205,7 +1203,7 @@ Handle ODBCConnection::Tables(const Arguments& args) { if (!data) { V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + return ThrowException(Exception::Error(NanNew("Could not allocate enough memory"))); } data->sql = NULL; @@ -1216,7 +1214,7 @@ Handle ODBCConnection::Tables(const Arguments& args) { data->column = NULL; data->cb = Persistent::New(cb); - if (!catalog->Equals(String::New("null"))) { + if (!catalog->Equals(NanNew("null"))) { #ifdef UNICODE data->catalog = (uint16_t *) malloc((catalog->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); catalog->Write((uint16_t *) data->catalog); @@ -1226,7 +1224,7 @@ Handle ODBCConnection::Tables(const Arguments& args) { #endif } - if (!schema->Equals(String::New("null"))) { + if (!schema->Equals(NanNew("null"))) { #ifdef UNICODE data->schema = (uint16_t *) malloc((schema->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); schema->Write((uint16_t *) data->schema); @@ -1236,7 +1234,7 @@ Handle ODBCConnection::Tables(const Arguments& args) { #endif } - if (!table->Equals(String::New("null"))) { + if (!table->Equals(NanNew("null"))) { #ifdef UNICODE data->table = (uint16_t *) malloc((table->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); table->Write((uint16_t *) data->table); @@ -1246,7 +1244,7 @@ Handle ODBCConnection::Tables(const Arguments& args) { #endif } - if (!type->Equals(String::New("null"))) { + if (!type->Equals(NanNew("null"))) { #ifdef UNICODE data->type = (uint16_t *) malloc((type->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); type->Write((uint16_t *) data->type); @@ -1267,7 +1265,7 @@ Handle ODBCConnection::Tables(const Arguments& args) { conn->Ref(); - return scope.Close(Undefined()); + NanReturnValue(Undefined()); } void ODBCConnection::UV_Tables(uv_work_t* req) { @@ -1297,8 +1295,8 @@ void ODBCConnection::UV_Tables(uv_work_t* req) { * Columns */ -Handle ODBCConnection::Columns(const Arguments& args) { - HandleScope scope; +NAN_METHOD(ODBCConnection::Columns) { + NanScope(); REQ_STRO_OR_NULL_ARG(0, catalog); REQ_STRO_OR_NULL_ARG(1, schema); @@ -1315,7 +1313,7 @@ Handle ODBCConnection::Columns(const Arguments& args) { if (!data) { V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + return ThrowException(Exception::Error(NanNew("Could not allocate enough memory"))); } data->sql = NULL; @@ -1326,7 +1324,7 @@ Handle ODBCConnection::Columns(const Arguments& args) { data->column = NULL; data->cb = Persistent::New(cb); - if (!catalog->Equals(String::New("null"))) { + if (!catalog->Equals(NanNew("null"))) { #ifdef UNICODE data->catalog = (uint16_t *) malloc((catalog->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); catalog->Write((uint16_t *) data->catalog); @@ -1336,7 +1334,7 @@ Handle ODBCConnection::Columns(const Arguments& args) { #endif } - if (!schema->Equals(String::New("null"))) { + if (!schema->Equals(NanNew("null"))) { #ifdef UNICODE data->schema = (uint16_t *) malloc((schema->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); schema->Write((uint16_t *) data->schema); @@ -1346,7 +1344,7 @@ Handle ODBCConnection::Columns(const Arguments& args) { #endif } - if (!table->Equals(String::New("null"))) { + if (!table->Equals(NanNew("null"))) { #ifdef UNICODE data->table = (uint16_t *) malloc((table->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); table->Write((uint16_t *) data->table); @@ -1356,7 +1354,7 @@ Handle ODBCConnection::Columns(const Arguments& args) { #endif } - if (!column->Equals(String::New("null"))) { + if (!column->Equals(NanNew("null"))) { #ifdef UNICODE data->column = (uint16_t *) malloc((column->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); column->Write((uint16_t *) data->column); @@ -1377,7 +1375,7 @@ Handle ODBCConnection::Columns(const Arguments& args) { conn->Ref(); - return scope.Close(Undefined()); + NanReturnValue(Undefined()); } void ODBCConnection::UV_Columns(uv_work_t* req) { @@ -1406,9 +1404,9 @@ void ODBCConnection::UV_Columns(uv_work_t* req) { * */ -Handle ODBCConnection::BeginTransactionSync(const Arguments& args) { +NAN_METHOD(ODBCConnection::BeginTransactionSync) { DEBUG_PRINTF("ODBCConnection::BeginTransactionSync\n"); - HandleScope scope; + NanScope(); ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -1426,10 +1424,10 @@ Handle ODBCConnection::BeginTransactionSync(const Arguments& args) { ThrowException(objError); - return scope.Close(False()); + NanReturnValue(NanFalse()); } - return scope.Close(True()); + NanReturnValue(NanTrue()); } /* @@ -1437,9 +1435,9 @@ Handle ODBCConnection::BeginTransactionSync(const Arguments& args) { * */ -Handle ODBCConnection::BeginTransaction(const Arguments& args) { +NAN_METHOD(ODBCConnection::BeginTransaction) { DEBUG_PRINTF("ODBCConnection::BeginTransaction\n"); - HandleScope scope; + NanScope(); REQ_FUN_ARG(0, cb); @@ -1452,7 +1450,7 @@ Handle ODBCConnection::BeginTransaction(const Arguments& args) { if (!data) { V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + return ThrowException(Exception::Error(NanNew("Could not allocate enough memory"))); } data->cb = Persistent::New(cb); @@ -1465,7 +1463,7 @@ Handle ODBCConnection::BeginTransaction(const Arguments& args) { UV_BeginTransaction, (uv_after_work_cb)UV_AfterBeginTransaction); - return scope.Close(Undefined()); + NanReturnValue(Undefined()); } /* @@ -1493,7 +1491,7 @@ void ODBCConnection::UV_BeginTransaction(uv_work_t* req) { void ODBCConnection::UV_AfterBeginTransaction(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterBeginTransaction\n"); - HandleScope scope; + NanScope(); open_connection_work_data* data = (open_connection_work_data *)(req->data); @@ -1528,9 +1526,9 @@ void ODBCConnection::UV_AfterBeginTransaction(uv_work_t* req, int status) { * */ -Handle ODBCConnection::EndTransactionSync(const Arguments& args) { +NAN_METHOD(ODBCConnection::EndTransactionSync) { DEBUG_PRINTF("ODBCConnection::EndTransactionSync\n"); - HandleScope scope; + NanScope(); ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); @@ -1579,10 +1577,10 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { if (error) { ThrowException(objError); - return scope.Close(False()); + NanReturnValue(NanFalse()); } else { - return scope.Close(True()); + NanReturnValue(NanTrue()); } } @@ -1591,9 +1589,9 @@ Handle ODBCConnection::EndTransactionSync(const Arguments& args) { * */ -Handle ODBCConnection::EndTransaction(const Arguments& args) { +NAN_METHOD(ODBCConnection::EndTransaction) { DEBUG_PRINTF("ODBCConnection::EndTransaction\n"); - HandleScope scope; + NanScope(); REQ_BOOL_ARG(0, rollback); REQ_FUN_ARG(1, cb); @@ -1607,7 +1605,7 @@ Handle ODBCConnection::EndTransaction(const Arguments& args) { if (!data) { V8::LowMemoryNotification(); - return ThrowException(Exception::Error(String::New("Could not allocate enough memory"))); + return ThrowException(Exception::Error(NanNew("Could not allocate enough memory"))); } data->completionType = (rollback->Value()) @@ -1624,7 +1622,7 @@ Handle ODBCConnection::EndTransaction(const Arguments& args) { UV_EndTransaction, (uv_after_work_cb)UV_AfterEndTransaction); - return scope.Close(Undefined()); + NanReturnUndefined(); } /* @@ -1673,7 +1671,7 @@ void ODBCConnection::UV_EndTransaction(uv_work_t* req) { void ODBCConnection::UV_AfterEndTransaction(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterEndTransaction\n"); - HandleScope scope; + NanScope(); open_connection_work_data* data = (open_connection_work_data *)(req->data); diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 80b59cbf..7d449959 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -42,55 +42,54 @@ class ODBCConnection : public node::ObjectWrap { ~ODBCConnection(); //constructor - static Handle New(const Arguments& args); + static NAN_METHOD(New); //Property Getter/Setters - static Handle ConnectedGetter(Local property, const AccessorInfo &info); - static Handle ConnectTimeoutGetter(Local property, const AccessorInfo &info); - static void ConnectTimeoutSetter(Local property, Local value, const AccessorInfo &info); - static Handle LoginTimeoutGetter(Local property, const AccessorInfo &info); - static void LoginTimeoutSetter(Local property, Local value, const AccessorInfo &info); + static NAN_GETTER(ConnectedGetter); + static NAN_GETTER(ConnectTimeoutGetter); + static NAN_SETTER(ConnectTimeoutSetter); + static NAN_GETTER(LoginTimeoutGetter); + static NAN_SETTER(LoginTimeoutSetter); //async methods - static Handle BeginTransaction(const Arguments& args); + static NAN_METHOD(BeginTransaction); static void UV_BeginTransaction(uv_work_t* work_req); static void UV_AfterBeginTransaction(uv_work_t* work_req, int status); - static Handle EndTransaction(const Arguments& args); + static NAN_METHOD(EndTransaction); static void UV_EndTransaction(uv_work_t* work_req); static void UV_AfterEndTransaction(uv_work_t* work_req, int status); - //test static NAN_METHOD(Open); static void UV_Open(uv_work_t* work_req); static void UV_AfterOpen(uv_work_t* work_req, int status); - static Handle Close(const Arguments& args); + static NAN_METHOD(Close); static void UV_Close(uv_work_t* work_req); static void UV_AfterClose(uv_work_t* work_req, int status); - static Handle CreateStatement(const Arguments& args); + static NAN_METHOD(CreateStatement); static void UV_CreateStatement(uv_work_t* work_req); static void UV_AfterCreateStatement(uv_work_t* work_req, int status); - static Handle Query(const Arguments& args); + static NAN_METHOD(Query); static void UV_Query(uv_work_t* req); static void UV_AfterQuery(uv_work_t* req, int status); - static Handle Columns(const Arguments& args); + static NAN_METHOD(Columns); static void UV_Columns(uv_work_t* req); - static Handle Tables(const Arguments& args); + static NAN_METHOD(Tables); static void UV_Tables(uv_work_t* req); //sync methods - static Handle CloseSync(const Arguments& args); - static Handle CreateStatementSync(const Arguments& args); - static Handle OpenSync(const Arguments& args); - static Handle QuerySync(const Arguments& args); - static Handle BeginTransactionSync(const Arguments& args); - static Handle EndTransactionSync(const Arguments& args); + static NAN_METHOD(CloseSync); + static NAN_METHOD(CreateStatementSync); + static NAN_METHOD(OpenSync); + static NAN_METHOD(QuerySync); + static NAN_METHOD(BeginTransactionSync); + static NAN_METHOD(EndTransactionSync); struct Fetch_Request { Persistent callback; From f1b25f1ee23abcd1e2a64a9e76c97f2466f1687b Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Sat, 18 Apr 2015 19:26:19 -0700 Subject: [PATCH 422/511] Converted odbc_result to use nan, and more in odbc_connection. --- src/odbc_connection.cpp | 58 ++++++------------- src/odbc_connection.h | 2 +- src/odbc_result.cpp | 124 ++++++++++++++++++---------------------- src/odbc_result.h | 22 +++---- 4 files changed, 87 insertions(+), 119 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 45044751..a77c2d9c 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -430,7 +430,7 @@ NAN_METHOD(ODBCConnection::OpenSync) { free(connectionString); if (err) { - ThrowException(objError); + NanThrowError(objError); NanReturnValue(NanFalse()); } else { @@ -711,19 +711,13 @@ NAN_METHOD(ODBCConnection::Query) { //handle Query("sql string", [params], function cb () {}); if ( !args[0]->IsString() ) { - return ThrowException(Exception::TypeError( - NanNew("Argument 0 must be an String.") - )); + NanThrowTypeError("Argument 0 must be an String."); } else if ( !args[1]->IsArray() ) { - return ThrowException(Exception::TypeError( - NanNew("Argument 1 must be an Array.") - )); + NanThrowTypeError("Argument 1 must be an Array."); } else if ( !args[2]->IsFunction() ) { - return ThrowException(Exception::TypeError( - NanNew("Argument 2 must be a Function.") - )); + NanThrowTypeError("Argument 2 must be a Function."); } sql = args[0]->ToString(); @@ -738,9 +732,7 @@ NAN_METHOD(ODBCConnection::Query) { //handle either Query("sql", cb) or Query({ settings }, cb) if (!args[1]->IsFunction()) { - return ThrowException(Exception::TypeError( - NanNew("ODBCConnection::Query(): Argument 1 must be a Function.")) - ); + NanThrowTypeError("ODBCConnection::Query(): Argument 1 must be a Function."); } cb = Local::Cast(args[1]); @@ -784,15 +776,11 @@ NAN_METHOD(ODBCConnection::Query) { } } else { - return ThrowException(Exception::TypeError( - NanNew("ODBCConnection::Query(): Argument 0 must be a String or an Object.")) - ); + NanThrowTypeError("ODBCConnection::Query(): Argument 0 must be a String or an Object."); } } else { - return ThrowException(Exception::TypeError( - NanNew("ODBCConnection::Query(): Requires either 2 or 3 Arguments. ")) - ); + NanThrowTypeError("ODBCConnection::Query(): Requires either 2 or 3 Arguments. "); } //Done checking arguments @@ -823,7 +811,7 @@ NAN_METHOD(ODBCConnection::Query) { conn->Ref(); - return scope.Close(Undefined()); + NanReturnUndefined(); } void ODBCConnection::UV_Query(uv_work_t* req) { @@ -999,14 +987,10 @@ NAN_METHOD(ODBCConnection::QuerySync) { //handle QuerySync("sql string", [params]); if ( !args[0]->IsString() ) { - return ThrowException(Exception::TypeError( - NanNew("ODBCConnection::QuerySync(): Argument 0 must be an String.") - )); + NanThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be an String."); } else if (!args[1]->IsArray()) { - return ThrowException(Exception::TypeError( - NanNew("ODBCConnection::QuerySync(): Argument 1 must be an Array.") - )); + NanThrowTypeError("ODBCConnection::QuerySync(): Argument 1 must be an Array."); } #ifdef UNICODE @@ -1070,15 +1054,11 @@ NAN_METHOD(ODBCConnection::QuerySync) { } } else { - return ThrowException(Exception::TypeError( - NanNew("ODBCConnection::QuerySync(): Argument 0 must be a String or an Object.")) - ); + NanThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be a String or an Object."); } } else { - return ThrowException(Exception::TypeError( - NanNew("ODBCConnection::QuerySync(): Requires either 1 or 2 Arguments. ")) - ); + NanThrowTypeError("ODBCConnection::QuerySync(): Requires either 1 or 2 Arguments."); } //Done checking arguments @@ -1146,7 +1126,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { //check to see if there was an error during execution if (ret == SQL_ERROR) { - ThrowException(ODBC::GetSQLError( + NanThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, hSTMT, (char *) "[node-odbc] Error in ODBCConnection::QuerySync" @@ -1203,7 +1183,7 @@ NAN_METHOD(ODBCConnection::Tables) { if (!data) { V8::LowMemoryNotification(); - return ThrowException(Exception::Error(NanNew("Could not allocate enough memory"))); + NanThrowError("Could not allocate enough memory"); } data->sql = NULL; @@ -1313,7 +1293,7 @@ NAN_METHOD(ODBCConnection::Columns) { if (!data) { V8::LowMemoryNotification(); - return ThrowException(Exception::Error(NanNew("Could not allocate enough memory"))); + NanThrowError("Could not allocate enough memory"); } data->sql = NULL; @@ -1422,7 +1402,7 @@ NAN_METHOD(ODBCConnection::BeginTransactionSync) { if (!SQL_SUCCEEDED(ret)) { Local objError = ODBC::GetSQLError(SQL_HANDLE_DBC, conn->m_hDBC); - ThrowException(objError); + NanThrowError(objError); NanReturnValue(NanFalse()); } @@ -1450,7 +1430,7 @@ NAN_METHOD(ODBCConnection::BeginTransaction) { if (!data) { V8::LowMemoryNotification(); - return ThrowException(Exception::Error(NanNew("Could not allocate enough memory"))); + NanThrowError("Could not allocate enough memory"); } data->cb = Persistent::New(cb); @@ -1575,7 +1555,7 @@ NAN_METHOD(ODBCConnection::EndTransactionSync) { } if (error) { - ThrowException(objError); + NanThrowError(objError); NanReturnValue(NanFalse()); } @@ -1605,7 +1585,7 @@ NAN_METHOD(ODBCConnection::EndTransaction) { if (!data) { V8::LowMemoryNotification(); - return ThrowException(Exception::Error(NanNew("Could not allocate enough memory"))); + NanThrowError("Could not allocate enough memory"); } data->completionType = (rollback->Value()) diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 7d449959..fca0386b 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -27,7 +27,7 @@ class ODBCConnection : public node::ObjectWrap { static Persistent OPTION_NORESULTS; static Persistent constructor_template; - static void Init(v8::Handle target); + static void Init(v8::Handle exports); void Free(); diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index aa0c77b2..bed4d9b2 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -30,17 +30,17 @@ using namespace v8; using namespace node; Persistent ODBCResult::constructor_template; -Persistent ODBCResult::OPTION_FETCH_MODE = Persistent::New(String::New("fetchMode")); +Persistent ODBCResult::OPTION_FETCH_MODE = Persistent::New(NanNew("fetchMode")); -void ODBCResult::Init(v8::Handle target) { +void ODBCResult::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCResult::Init\n"); - HandleScope scope; + NanScope(); Local t = FunctionTemplate::New(New); // Constructor Template - constructor_template = Persistent::New(t); - constructor_template->SetClassName(String::NewSymbol("ODBCResult")); + NanAssignPersistent(constructor_template, t); + constructor_template->SetClassName(NanNew("ODBCResult")); // Reserve space for one Handle Local instance_template = constructor_template->InstanceTemplate(); @@ -57,10 +57,10 @@ void ODBCResult::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "getColumnNamesSync", GetColumnNamesSync); // Properties - instance_template->SetAccessor(String::New("fetchMode"), FetchModeGetter, FetchModeSetter); + instance_template->SetAccessor(NanNew("fetchMode"), FetchModeGetter, FetchModeSetter); // Attach the Database Constructor to the target object - target->Set( v8::String::NewSymbol("ODBCResult"), + exports->Set(NanNew("ODBCResult"), constructor_template->GetFunction()); } @@ -88,10 +88,9 @@ void ODBCResult::Free() { } } -Handle ODBCResult::New(const Arguments& args) { +NAN_METHOD(ODBCResult::New) { DEBUG_PRINTF("ODBCResult::New\n"); - - HandleScope scope; + NanScope(); REQ_EXT_ARG(0, js_henv); REQ_EXT_ARG(1, js_hdbc); @@ -131,21 +130,21 @@ Handle ODBCResult::New(const Arguments& args) { objODBCResult->Wrap(args.Holder()); - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } -Handle ODBCResult::FetchModeGetter(Local property, const AccessorInfo &info) { - HandleScope scope; +NAN_GETTER(ODBCResult::FetchModeGetter) { + NanScope(); - ODBCResult *obj = ObjectWrap::Unwrap(info.Holder()); + ODBCResult *obj = ObjectWrap::Unwrap(args.Holder()); - return scope.Close(Integer::New(obj->m_fetchMode)); + NanReturnValue(Integer::New(obj->m_fetchMode)); } -void ODBCResult::FetchModeSetter(Local property, Local value, const AccessorInfo &info) { - HandleScope scope; +NAN_SETTER(ODBCResult::FetchModeSetter) { + NanScope(); - ODBCResult *obj = ObjectWrap::Unwrap(info.Holder()); + ODBCResult *obj = ObjectWrap::Unwrap(args.Holder()); if (value->IsNumber()) { obj->m_fetchMode = value->Int32Value(); @@ -156,10 +155,9 @@ void ODBCResult::FetchModeSetter(Local property, Local value, con * Fetch */ -Handle ODBCResult::Fetch(const Arguments& args) { +NAN_METHOD(ODBCResult::Fetch) { DEBUG_PRINTF("ODBCResult::Fetch\n"); - - HandleScope scope; + NanScope(); ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); @@ -185,9 +183,7 @@ Handle ODBCResult::Fetch(const Arguments& args) { } } else { - return ThrowException(Exception::TypeError( - String::New("ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function.") - )); + return NanThrowTypeError("ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function."); } data->cb = Persistent::New(cb); @@ -203,7 +199,7 @@ Handle ODBCResult::Fetch(const Arguments& args) { objODBCResult->Ref(); - return scope.Close(Undefined()); + NanReturnUndefined(); } void ODBCResult::UV_Fetch(uv_work_t* work_req) { @@ -216,8 +212,7 @@ void ODBCResult::UV_Fetch(uv_work_t* work_req) { void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { DEBUG_PRINTF("ODBCResult::UV_AfterFetch\n"); - - HandleScope scope; + NanScope(); fetch_work_data* data = (fetch_work_data *)(work_req->data); @@ -321,10 +316,9 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { * FetchSync */ -Handle ODBCResult::FetchSync(const Arguments& args) { +NAN_METHOD(ODBCResult::FetchSync) { DEBUG_PRINTF("ODBCResult::FetchSync\n"); - - HandleScope scope; + NanScope(); ODBCResult* objResult = ObjectWrap::Unwrap(args.Holder()); @@ -388,19 +382,19 @@ Handle ODBCResult::FetchSync(const Arguments& args) { objResult->bufferLength); } - return scope.Close(data); + NanReturnValue(data); } else { ODBC::FreeColumns(objResult->columns, &objResult->colCount); //if there was an error, pass that as arg[0] otherwise Null if (error) { - ThrowException(objError); + NanThrowError(objError); - return scope.Close(Null()); + NanReturnNull(); } else { - return scope.Close(Null()); + NanReturnNull(); } } } @@ -409,10 +403,9 @@ Handle ODBCResult::FetchSync(const Arguments& args) { * FetchAll */ -Handle ODBCResult::FetchAll(const Arguments& args) { +NAN_METHOD(ODBCResult::FetchAll) { DEBUG_PRINTF("ODBCResult::FetchAll\n"); - - HandleScope scope; + NanScope(); ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); @@ -437,9 +430,7 @@ Handle ODBCResult::FetchAll(const Arguments& args) { } } else { - return ThrowException(Exception::TypeError( - String::New("ODBCResult::FetchAll(): 1 or 2 arguments are required. The last argument must be a callback function.") - )); + NanThrowTypeError("ODBCResult::FetchAll(): 1 or 2 arguments are required. The last argument must be a callback function."); } data->rows = Persistent::New(Array::New()); @@ -459,7 +450,7 @@ Handle ODBCResult::FetchAll(const Arguments& args) { data->objResult->Ref(); - return scope.Close(Undefined()); + NanReturnUndefined(); } void ODBCResult::UV_FetchAll(uv_work_t* work_req) { @@ -472,8 +463,7 @@ void ODBCResult::UV_FetchAll(uv_work_t* work_req) { void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll\n"); - - HandleScope scope; + NanScope(); fetch_work_data* data = (fetch_work_data *)(work_req->data); @@ -510,7 +500,7 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { else { if (data->fetchMode == FETCH_ARRAY) { data->rows->Set( - Integer::New(data->count), + NanNew(data->count), ODBC::GetRecordArray( self->m_hSTMT, self->columns, @@ -521,7 +511,7 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { } else { data->rows->Set( - Integer::New(data->count), + NanNew(data->count), ODBC::GetRecordTuple( self->m_hSTMT, self->columns, @@ -547,13 +537,13 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { Handle args[2]; if (data->errorCount > 0) { - args[0] = Local::New(data->objError); + args[0] = NanNew(data->objError); } else { args[0] = Null(); } - args[1] = Local::New(data->rows); + args[1] = NanNew(data->rows); TryCatch try_catch; @@ -578,10 +568,9 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { * FetchAllSync */ -Handle ODBCResult::FetchAllSync(const Arguments& args) { +NAN_METHOD(ODBCResult::FetchAllSync) { DEBUG_PRINTF("ODBCResult::FetchAllSync\n"); - - HandleScope scope; + NanScope(); ODBCResult* self = ObjectWrap::Unwrap(args.Holder()); @@ -635,7 +624,7 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { if (fetchMode == FETCH_ARRAY) { rows->Set( - Integer::New(count), + NanNew(count), ODBC::GetRecordArray( self->m_hSTMT, self->columns, @@ -646,7 +635,7 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { } else { rows->Set( - Integer::New(count), + NanNew(count), ODBC::GetRecordTuple( self->m_hSTMT, self->columns, @@ -664,10 +653,10 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { //throw the error object if there were errors if (errorCount > 0) { - ThrowException(objError); + NanThrowError(objError); } - return scope.Close(rows); + NanReturnValue(rows); } /* @@ -675,10 +664,9 @@ Handle ODBCResult::FetchAllSync(const Arguments& args) { * */ -Handle ODBCResult::CloseSync(const Arguments& args) { +NAN_METHOD(ODBCResult::CloseSync) { DEBUG_PRINTF("ODBCResult::CloseSync\n"); - - HandleScope scope; + NanScope(); OPT_INT_ARG(0, closeOption, SQL_DESTROY); @@ -706,33 +694,31 @@ Handle ODBCResult::CloseSync(const Arguments& args) { uv_mutex_unlock(&ODBC::g_odbcMutex); } - return scope.Close(True()); + NanReturnValue(NanTrue()); } -Handle ODBCResult::MoreResultsSync(const Arguments& args) { +NAN_METHOD(ODBCResult::MoreResultsSync) { DEBUG_PRINTF("ODBCResult::MoreResultsSync\n"); - - HandleScope scope; + NanScope(); ODBCResult* result = ObjectWrap::Unwrap(args.Holder()); SQLRETURN ret = SQLMoreResults(result->m_hSTMT); if (ret == SQL_ERROR) { - ThrowException(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync")); + NanThrowError(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync")); } - return scope.Close(SQL_SUCCEEDED(ret) || ret == SQL_ERROR ? True() : False()); + NanReturnValue(SQL_SUCCEEDED(ret) || ret == SQL_ERROR ? NanTrue() : NanFalse()); } /* * GetColumnNamesSync */ -Handle ODBCResult::GetColumnNamesSync(const Arguments& args) { +NAN_METHOD(ODBCResult::GetColumnNamesSync) { DEBUG_PRINTF("ODBCResult::GetColumnNamesSync\n"); - - HandleScope scope; + NanScope(); ODBCResult* self = ObjectWrap::Unwrap(args.Holder()); @@ -743,9 +729,9 @@ Handle ODBCResult::GetColumnNamesSync(const Arguments& args) { } for (int i = 0; i < self->colCount; i++) { - cols->Set(Integer::New(i), - String::New((const char *) self->columns[i].name)); + cols->Set(NanNew(i), + NanNew((const char *) self->columns[i].name)); } - return scope.Close(cols); + NanReturnValue(cols); } diff --git a/src/odbc_result.h b/src/odbc_result.h index da4a3f8f..9d1d4986 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -17,6 +17,8 @@ #ifndef _SRC_ODBC_RESULT_H #define _SRC_ODBC_RESULT_H +#include + class ODBCResult : public node::ObjectWrap { public: static Persistent OPTION_FETCH_MODE; @@ -38,27 +40,27 @@ class ODBCResult : public node::ObjectWrap { ~ODBCResult(); //constructor - static Handle New(const Arguments& args); + static NAN_METHOD(New); //async methods - static Handle Fetch(const Arguments& args); + static NAN_METHOD(Fetch); static void UV_Fetch(uv_work_t* work_req); static void UV_AfterFetch(uv_work_t* work_req, int status); - static Handle FetchAll(const Arguments& args); + static NAN_METHOD(FetchAll); static void UV_FetchAll(uv_work_t* work_req); static void UV_AfterFetchAll(uv_work_t* work_req, int status); //sync methods - static Handle CloseSync(const Arguments& args); - static Handle MoreResultsSync(const Arguments& args); - static Handle FetchSync(const Arguments& args); - static Handle FetchAllSync(const Arguments& args); - static Handle GetColumnNamesSync(const Arguments& args); + static NAN_METHOD(CloseSync); + static NAN_METHOD(MoreResultsSync); + static NAN_METHOD(FetchSync); + static NAN_METHOD(FetchAllSync); + static NAN_METHOD(GetColumnNamesSync); //property getter/setters - static Handle FetchModeGetter(Local property, const AccessorInfo &info); - static void FetchModeSetter(Local property, Local value, const AccessorInfo &info); + static NAN_GETTER(FetchModeGetter); + static NAN_SETTER(FetchModeSetter); struct fetch_work_data { Persistent cb; From 30f327b96ac6646fdd2a1d0cd9ef941d39fbd05d Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Sat, 18 Apr 2015 20:32:42 -0700 Subject: [PATCH 423/511] More nan conversions. Added missing return statements at thrown exceptions that were accidentally removed on last pass. --- src/odbc.h | 52 +++++++-------- src/odbc_connection.cpp | 46 +++++++------- src/odbc_result.cpp | 13 ++-- src/odbc_result.h | 2 +- src/odbc_statement.cpp | 136 +++++++++++++++++++--------------------- src/odbc_statement.h | 28 +++++---- 6 files changed, 133 insertions(+), 144 deletions(-) diff --git a/src/odbc.h b/src/odbc.h index 6f5bc15f..ba12afcd 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -174,67 +175,57 @@ struct query_request { #define REQ_ARGS(N) \ if (args.Length() < (N)) \ - return ThrowException(Exception::TypeError( \ - String::New("Expected " #N "arguments"))); + return NanThrowTypeError("Expected " #N "arguments"); //Require String Argument; Save String as Utf8 #define REQ_STR_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsString()) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a string"))); \ + return NanThrowTypeError("Argument " #I " must be a string"); \ String::Utf8Value VAR(args[I]->ToString()); //Require String Argument; Save String as Wide String (UCS2) -#define REQ_WSTR_ARG(I, VAR) \ +#define REQ_WSTR_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsString()) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a string"))); \ + return NanThrowTypeError("Argument " #I " must be a string"); \ String::Value VAR(args[I]->ToString()); //Require String Argument; Save String as Object -#define REQ_STRO_ARG(I, VAR) \ +#define REQ_STRO_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsString()) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a string"))); \ + return NanThrowTypeError("Argument " #I " must be a string"); \ Local VAR(args[I]->ToString()); //Require String or Null Argument; Save String as Utf8 #define REQ_STR_OR_NULL_ARG(I, VAR) \ - if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a string or null"))); \ + if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ + return NanThrowTypeError("Argument " #I " must be a string or null"); \ String::Utf8Value VAR(args[I]->ToString()); //Require String or Null Argument; Save String as Wide String (UCS2) -#define REQ_WSTR_OR_NULL_ARG(I, VAR) \ - if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a string or null"))); \ +#define REQ_WSTR_OR_NULL_ARG(I, VAR) \ + if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ + return NanThrowTypeError("Argument " #I " must be a string or null"); \ String::Value VAR(args[I]->ToString()); //Require String or Null Argument; save String as String Object -#define REQ_STRO_OR_NULL_ARG(I, VAR) \ - if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a string or null"))); \ +#define REQ_STRO_OR_NULL_ARG(I, VAR) \ + if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ + return NanThrowTypeError("Argument " #I " must be a string or null"); \ Local VAR(args[I]->ToString()); #define REQ_FUN_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsFunction()) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a function"))); \ + return NanThrowTypeError("Argument " #I " must be a function"); \ Local VAR = Local::Cast(args[I]); -#define REQ_BOOL_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsBoolean()) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a boolean"))); \ +#define REQ_BOOL_ARG(I, VAR) \ + if (args.Length() <= (I) || !args[I]->IsBoolean()) \ + return NanThrowTypeError("Argument " #I " must be a boolean"); \ Local VAR = (args[I]->ToBoolean()); #define REQ_EXT_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsExternal()) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " invalid"))); \ + return NanThrowTypeError("Argument " #I " invalid"); \ Local VAR = Local::Cast(args[I]); #define OPT_INT_ARG(I, VAR, DEFAULT) \ @@ -244,8 +235,7 @@ struct query_request { } else if (args[I]->IsInt32()) { \ VAR = args[I]->Int32Value(); \ } else { \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be an integer"))); \ + return NanThrowTypeError("Argument " #I " must be an integer"); \ } diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index a77c2d9c..5b2f8f21 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -202,7 +202,7 @@ NAN_METHOD(ODBCConnection::Open) { connection->WriteUtf8((char*) data->connection); #endif - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->conn = conn; work_req->data = data; @@ -456,7 +456,7 @@ NAN_METHOD(ODBCConnection::Close) { close_connection_work_data* data = (close_connection_work_data *) (calloc(1, sizeof(close_connection_work_data))); - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->conn = conn; work_req->data = data; @@ -469,7 +469,7 @@ NAN_METHOD(ODBCConnection::Close) { conn->Ref(); - NanReturnValue(Undefined()); + NanReturnUndefined(); } void ODBCConnection::UV_Close(uv_work_t* req) { @@ -605,7 +605,7 @@ NAN_METHOD(ODBCConnection::CreateStatement) { create_statement_work_data* data = (create_statement_work_data *) (calloc(1, sizeof(create_statement_work_data))); - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->conn = conn; work_req->data = data; @@ -711,13 +711,13 @@ NAN_METHOD(ODBCConnection::Query) { //handle Query("sql string", [params], function cb () {}); if ( !args[0]->IsString() ) { - NanThrowTypeError("Argument 0 must be an String."); + return NanThrowTypeError("Argument 0 must be an String."); } else if ( !args[1]->IsArray() ) { - NanThrowTypeError("Argument 1 must be an Array."); + return NanThrowTypeError("Argument 1 must be an Array."); } else if ( !args[2]->IsFunction() ) { - NanThrowTypeError("Argument 2 must be a Function."); + return NanThrowTypeError("Argument 2 must be a Function."); } sql = args[0]->ToString(); @@ -732,7 +732,7 @@ NAN_METHOD(ODBCConnection::Query) { //handle either Query("sql", cb) or Query({ settings }, cb) if (!args[1]->IsFunction()) { - NanThrowTypeError("ODBCConnection::Query(): Argument 1 must be a Function."); + return NanThrowTypeError("ODBCConnection::Query(): Argument 1 must be a Function."); } cb = Local::Cast(args[1]); @@ -776,15 +776,15 @@ NAN_METHOD(ODBCConnection::Query) { } } else { - NanThrowTypeError("ODBCConnection::Query(): Argument 0 must be a String or an Object."); + return NanThrowTypeError("ODBCConnection::Query(): Argument 0 must be a String or an Object."); } } else { - NanThrowTypeError("ODBCConnection::Query(): Requires either 2 or 3 Arguments. "); + return NanThrowTypeError("ODBCConnection::Query(): Requires either 2 or 3 Arguments. "); } //Done checking arguments - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->sqlLen = sql->Length(); #ifdef UNICODE @@ -987,10 +987,10 @@ NAN_METHOD(ODBCConnection::QuerySync) { //handle QuerySync("sql string", [params]); if ( !args[0]->IsString() ) { - NanThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be an String."); + return NanThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be an String."); } else if (!args[1]->IsArray()) { - NanThrowTypeError("ODBCConnection::QuerySync(): Argument 1 must be an Array."); + return NanThrowTypeError("ODBCConnection::QuerySync(): Argument 1 must be an Array."); } #ifdef UNICODE @@ -1054,11 +1054,11 @@ NAN_METHOD(ODBCConnection::QuerySync) { } } else { - NanThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be a String or an Object."); + return NanThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be a String or an Object."); } } else { - NanThrowTypeError("ODBCConnection::QuerySync(): Requires either 1 or 2 Arguments."); + return NanThrowTypeError("ODBCConnection::QuerySync(): Requires either 1 or 2 Arguments."); } //Done checking arguments @@ -1183,7 +1183,7 @@ NAN_METHOD(ODBCConnection::Tables) { if (!data) { V8::LowMemoryNotification(); - NanThrowError("Could not allocate enough memory"); + return NanThrowError("Could not allocate enough memory"); } data->sql = NULL; @@ -1192,7 +1192,7 @@ NAN_METHOD(ODBCConnection::Tables) { data->table = NULL; data->type = NULL; data->column = NULL; - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); if (!catalog->Equals(NanNew("null"))) { #ifdef UNICODE @@ -1293,7 +1293,7 @@ NAN_METHOD(ODBCConnection::Columns) { if (!data) { V8::LowMemoryNotification(); - NanThrowError("Could not allocate enough memory"); + return NanThrowError("Could not allocate enough memory"); } data->sql = NULL; @@ -1302,7 +1302,7 @@ NAN_METHOD(ODBCConnection::Columns) { data->table = NULL; data->type = NULL; data->column = NULL; - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); if (!catalog->Equals(NanNew("null"))) { #ifdef UNICODE @@ -1430,10 +1430,10 @@ NAN_METHOD(ODBCConnection::BeginTransaction) { if (!data) { V8::LowMemoryNotification(); - NanThrowError("Could not allocate enough memory"); + return NanThrowError("Could not allocate enough memory"); } - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->conn = conn; work_req->data = data; @@ -1585,14 +1585,14 @@ NAN_METHOD(ODBCConnection::EndTransaction) { if (!data) { V8::LowMemoryNotification(); - NanThrowError("Could not allocate enough memory"); + return NanThrowError("Could not allocate enough memory"); } data->completionType = (rollback->Value()) ? SQL_ROLLBACK : SQL_COMMIT ; - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->conn = conn; work_req->data = data; diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index bed4d9b2..c39cad6e 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -138,7 +138,7 @@ NAN_GETTER(ODBCResult::FetchModeGetter) { ODBCResult *obj = ObjectWrap::Unwrap(args.Holder()); - NanReturnValue(Integer::New(obj->m_fetchMode)); + NanReturnValue(NanNew(obj->m_fetchMode)); } NAN_SETTER(ODBCResult::FetchModeSetter) { @@ -186,7 +186,7 @@ NAN_METHOD(ODBCResult::Fetch) { return NanThrowTypeError("ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function."); } - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->objResult = objODBCResult; work_req->data = data; @@ -433,12 +433,12 @@ NAN_METHOD(ODBCResult::FetchAll) { NanThrowTypeError("ODBCResult::FetchAll(): 1 or 2 arguments are required. The last argument must be a callback function."); } - data->rows = Persistent::New(Array::New()); + NanAssignPersistent(data->rows, Array::New()); data->errorCount = 0; data->count = 0; - data->objError = Persistent::New(Object::New()); + NanAssignPersistent(data->objError, Object::New()); - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->objResult = objODBCResult; work_req->data = data; @@ -485,7 +485,8 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { else if (data->result == SQL_ERROR) { data->errorCount++; - data->objError = Persistent::New(ODBC::GetSQLError( + //data->objError = Persistent::New(ODBC::GetSQLError( + NanAssignPersistent(data->objError, ODBC::GetSQLError( SQL_HANDLE_STMT, self->m_hSTMT, (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll" diff --git a/src/odbc_result.h b/src/odbc_result.h index 9d1d4986..a50ad471 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -23,7 +23,7 @@ class ODBCResult : public node::ObjectWrap { public: static Persistent OPTION_FETCH_MODE; static Persistent constructor_template; - static void Init(v8::Handle target); + static void Init(v8::Handle exports); void Free(); diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index acdc9fe0..780e0da6 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -31,15 +31,15 @@ using namespace node; Persistent ODBCStatement::constructor_template; -void ODBCStatement::Init(v8::Handle target) { +void ODBCStatement::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCStatement::Init\n"); - HandleScope scope; + NanScope(); Local t = FunctionTemplate::New(New); // Constructor Template - constructor_template = Persistent::New(t); - constructor_template->SetClassName(String::NewSymbol("ODBCStatement")); + NanAssignPersistent(constructor_template, t); + constructor_template->SetClassName(NanNew("ODBCStatement")); // Reserve space for one Handle Local instance_template = constructor_template->InstanceTemplate(); @@ -64,7 +64,7 @@ void ODBCStatement::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); // Attach the Database Constructor to the target object - target->Set( v8::String::NewSymbol("ODBCStatement"), + exports->Set(NanNew("ODBCStatement"), constructor_template->GetFunction()); } @@ -111,9 +111,9 @@ void ODBCStatement::Free() { } } -Handle ODBCStatement::New(const Arguments& args) { +NAN_METHOD(ODBCStatement::New) { DEBUG_PRINTF("ODBCStatement::New\n"); - HandleScope scope; + NanScope(); REQ_EXT_ARG(0, js_henv); REQ_EXT_ARG(1, js_hdbc); @@ -141,17 +141,17 @@ Handle ODBCStatement::New(const Arguments& args) { stmt->Wrap(args.Holder()); - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } /* * Execute */ -Handle ODBCStatement::Execute(const Arguments& args) { +NAN_METHOD(ODBCStatement::Execute) { DEBUG_PRINTF("ODBCStatement::Execute\n"); - HandleScope scope; + NanScope(); REQ_FUN_ARG(0, cb); @@ -162,7 +162,7 @@ Handle ODBCStatement::Execute(const Arguments& args) { execute_work_data* data = (execute_work_data *) calloc(1, sizeof(execute_work_data)); - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->stmt = stmt; work_req->data = data; @@ -175,7 +175,7 @@ Handle ODBCStatement::Execute(const Arguments& args) { stmt->Ref(); - return scope.Close(Undefined()); + NanReturnUndefined(); } void ODBCStatement::UV_Execute(uv_work_t* req) { @@ -195,7 +195,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { execute_work_data* data = (execute_work_data *)(req->data); - HandleScope scope; + NanScope(); //an easy reference to the statment object ODBCStatement* self = data->stmt->self(); @@ -243,23 +243,23 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { * */ -Handle ODBCStatement::ExecuteSync(const Arguments& args) { +NAN_METHOD(ODBCStatement::ExecuteSync) { DEBUG_PRINTF("ODBCStatement::ExecuteSync\n"); - HandleScope scope; + NanScope(); ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); SQLRETURN ret = SQLExecute(stmt->m_hSTMT); if(ret == SQL_ERROR) { - ThrowException(ODBC::GetSQLError( + NanThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); - return scope.Close(Null()); + NanReturnNull(); } else { Local args[4]; @@ -273,7 +273,7 @@ Handle ODBCStatement::ExecuteSync(const Arguments& args) { Local js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); - return scope.Close(js_result); + NanReturnValue(js_result); } } @@ -281,10 +281,10 @@ Handle ODBCStatement::ExecuteSync(const Arguments& args) { * ExecuteNonQuery */ -Handle ODBCStatement::ExecuteNonQuery(const Arguments& args) { +NAN_METHOD(ODBCStatement::ExecuteNonQuery) { DEBUG_PRINTF("ODBCStatement::ExecuteNonQuery\n"); - HandleScope scope; + NanScope(); REQ_FUN_ARG(0, cb); @@ -295,7 +295,7 @@ Handle ODBCStatement::ExecuteNonQuery(const Arguments& args) { execute_work_data* data = (execute_work_data *) calloc(1, sizeof(execute_work_data)); - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->stmt = stmt; work_req->data = data; @@ -308,7 +308,7 @@ Handle ODBCStatement::ExecuteNonQuery(const Arguments& args) { stmt->Ref(); - return scope.Close(Undefined()); + NanReturnUndefined(); } void ODBCStatement::UV_ExecuteNonQuery(uv_work_t* req) { @@ -328,7 +328,7 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { execute_work_data* data = (execute_work_data *)(req->data); - HandleScope scope; + NanScope(); //an easy reference to the statment object ODBCStatement* self = data->stmt->self(); @@ -379,23 +379,23 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { * */ -Handle ODBCStatement::ExecuteNonQuerySync(const Arguments& args) { +NAN_METHOD(ODBCStatement::ExecuteNonQuerySync) { DEBUG_PRINTF("ODBCStatement::ExecuteNonQuerySync\n"); - HandleScope scope; + NanScope(); ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); SQLRETURN ret = SQLExecute(stmt->m_hSTMT); if(ret == SQL_ERROR) { - ThrowException(ODBC::GetSQLError( + NanThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); - return scope.Close(Null()); + NanReturnNull(); } else { SQLLEN rowCount = 0; @@ -410,7 +410,7 @@ Handle ODBCStatement::ExecuteNonQuerySync(const Arguments& args) { SQLFreeStmt(stmt->m_hSTMT, SQL_CLOSE); uv_mutex_unlock(&ODBC::g_odbcMutex); - return scope.Close(Number::New(rowCount)); + NanReturnValue(Number::New(rowCount)); } } @@ -419,10 +419,10 @@ Handle ODBCStatement::ExecuteNonQuerySync(const Arguments& args) { * */ -Handle ODBCStatement::ExecuteDirect(const Arguments& args) { +NAN_METHOD(ODBCStatement::ExecuteDirect) { DEBUG_PRINTF("ODBCStatement::ExecuteDirect\n"); - HandleScope scope; + NanScope(); REQ_STRO_ARG(0, sql); REQ_FUN_ARG(1, cb); @@ -434,7 +434,7 @@ Handle ODBCStatement::ExecuteDirect(const Arguments& args) { execute_direct_work_data* data = (execute_direct_work_data *) calloc(1, sizeof(execute_direct_work_data)); - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->sqlLen = sql->Length(); @@ -457,7 +457,7 @@ Handle ODBCStatement::ExecuteDirect(const Arguments& args) { stmt->Ref(); - return scope.Close(Undefined()); + NanReturnUndefined(); } void ODBCStatement::UV_ExecuteDirect(uv_work_t* req) { @@ -480,7 +480,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { execute_direct_work_data* data = (execute_direct_work_data *)(req->data); - HandleScope scope; + NanScope(); //an easy reference to the statment object ODBCStatement* self = data->stmt->self(); @@ -529,10 +529,10 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { * */ -Handle ODBCStatement::ExecuteDirectSync(const Arguments& args) { +NAN_METHOD(ODBCStatement::ExecuteDirectSync) { DEBUG_PRINTF("ODBCStatement::ExecuteDirectSync\n"); - HandleScope scope; + NanScope(); #ifdef UNICODE REQ_WSTR_ARG(0, sql); @@ -548,13 +548,13 @@ Handle ODBCStatement::ExecuteDirectSync(const Arguments& args) { sql.length()); if(ret == SQL_ERROR) { - ThrowException(ODBC::GetSQLError( + NanThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteDirectSync" )); - return scope.Close(Null()); + NanReturnValue(Null()); } else { Local args[4]; @@ -568,7 +568,7 @@ Handle ODBCStatement::ExecuteDirectSync(const Arguments& args) { Persistent js_result(ODBCResult::constructor_template-> GetFunction()->NewInstance(4, args)); - return scope.Close(js_result); + NanReturnValue(js_result); } } @@ -577,10 +577,10 @@ Handle ODBCStatement::ExecuteDirectSync(const Arguments& args) { * */ -Handle ODBCStatement::PrepareSync(const Arguments& args) { +NAN_METHOD(ODBCStatement::PrepareSync) { DEBUG_PRINTF("ODBCStatement::PrepareSync\n"); - HandleScope scope; + NanScope(); REQ_STRO_ARG(0, sql); @@ -606,16 +606,16 @@ Handle ODBCStatement::PrepareSync(const Arguments& args) { sqlLen); if (SQL_SUCCEEDED(ret)) { - return scope.Close(True()); + NanReturnValue(NanTrue()); } else { - ThrowException(ODBC::GetSQLError( + NanThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::PrepareSync" )); - return scope.Close(False()); + NanReturnValue(NanFalse()); } } @@ -624,10 +624,10 @@ Handle ODBCStatement::PrepareSync(const Arguments& args) { * */ -Handle ODBCStatement::Prepare(const Arguments& args) { +NAN_METHOD(ODBCStatement::Prepare) { DEBUG_PRINTF("ODBCStatement::Prepare\n"); - HandleScope scope; + NanScope(); REQ_STRO_ARG(0, sql); REQ_FUN_ARG(1, cb); @@ -639,7 +639,7 @@ Handle ODBCStatement::Prepare(const Arguments& args) { prepare_work_data* data = (prepare_work_data *) calloc(1, sizeof(prepare_work_data)); - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->sqlLen = sql->Length(); @@ -663,7 +663,7 @@ Handle ODBCStatement::Prepare(const Arguments& args) { stmt->Ref(); - return scope.Close(Undefined()); + NanReturnUndefined(); } void ODBCStatement::UV_Prepare(uv_work_t* req) { @@ -698,7 +698,7 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { data->stmt->m_hSTMT ); - HandleScope scope; + NanScope(); //First thing, let's check if the execution of the query returned any errors if(data->result == SQL_ERROR) { @@ -711,7 +711,7 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { Local args[2]; args[0] = Local::New(Null()); - args[1] = Local::New(True()); + args[1] = Local::New(NanTrue()); TryCatch try_catch; @@ -735,15 +735,13 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { * */ -Handle ODBCStatement::BindSync(const Arguments& args) { +NAN_METHOD(ODBCStatement::BindSync) { DEBUG_PRINTF("ODBCStatement::BindSync\n"); - HandleScope scope; + NanScope(); if ( !args[0]->IsArray() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 1 must be an Array")) - ); + return NanThrowTypeError("Argument 1 must be an Array"); } ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); @@ -813,19 +811,19 @@ Handle ODBCStatement::BindSync(const Arguments& args) { } if (SQL_SUCCEEDED(ret)) { - return scope.Close(True()); + NanReturnValue(NanTrue()); } else { - ThrowException(ODBC::GetSQLError( + NanThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::BindSync" )); - return scope.Close(False()); + NanReturnValue(NanFalse()); } - return scope.Close(Undefined()); + NanReturnUndefined(); } /* @@ -833,15 +831,13 @@ Handle ODBCStatement::BindSync(const Arguments& args) { * */ -Handle ODBCStatement::Bind(const Arguments& args) { +NAN_METHOD(ODBCStatement::Bind) { DEBUG_PRINTF("ODBCStatement::Bind\n"); - HandleScope scope; + NanScope(); if ( !args[0]->IsArray() ) { - return ThrowException(Exception::TypeError( - String::New("Argument 1 must be an Array")) - ); + return NanThrowError("Argument 1 must be an Array"); } REQ_FUN_ARG(1, cb); @@ -885,7 +881,7 @@ Handle ODBCStatement::Bind(const Arguments& args) { data->stmt->m_hSTMT ); - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->stmt->params = ODBC::GetParametersFromArray( Local::Cast(args[0]), @@ -901,7 +897,7 @@ Handle ODBCStatement::Bind(const Arguments& args) { stmt->Ref(); - return scope.Close(Undefined()); + NanReturnUndefined(); } void ODBCStatement::UV_Bind(uv_work_t* req) { @@ -953,7 +949,7 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { bind_work_data* data = (bind_work_data *)(req->data); - HandleScope scope; + NanScope(); //an easy reference to the statment object ODBCStatement* self = data->stmt->self(); @@ -969,7 +965,7 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { Local args[2]; args[0] = Local::New(Null()); - args[1] = Local::New(True()); + args[1] = Local::New(NanTrue()); TryCatch try_catch; @@ -991,10 +987,10 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { * CloseSync */ -Handle ODBCStatement::CloseSync(const Arguments& args) { +NAN_METHOD(ODBCStatement::CloseSync) { DEBUG_PRINTF("ODBCStatement::CloseSync\n"); - HandleScope scope; + NanScope(); OPT_INT_ARG(0, closeOption, SQL_DESTROY); @@ -1014,5 +1010,5 @@ Handle ODBCStatement::CloseSync(const Arguments& args) { uv_mutex_unlock(&ODBC::g_odbcMutex); } - return scope.Close(True()); + NanReturnValue(NanTrue()); } diff --git a/src/odbc_statement.h b/src/odbc_statement.h index 2900cb3a..783329ce 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -17,10 +17,12 @@ #ifndef _SRC_ODBC_STATEMENT_H #define _SRC_ODBC_STATEMENT_H +#include + class ODBCStatement : public node::ObjectWrap { public: static Persistent constructor_template; - static void Init(v8::Handle target); + static void Init(v8::Handle exports); void Free(); @@ -36,36 +38,36 @@ class ODBCStatement : public node::ObjectWrap { ~ODBCStatement(); //constructor - static Handle New(const Arguments& args); + static NAN_METHOD(New); //async methods - static Handle Execute(const Arguments& args); + static NAN_METHOD(Execute); static void UV_Execute(uv_work_t* work_req); static void UV_AfterExecute(uv_work_t* work_req, int status); - static Handle ExecuteDirect(const Arguments& args); + static NAN_METHOD(ExecuteDirect); static void UV_ExecuteDirect(uv_work_t* work_req); static void UV_AfterExecuteDirect(uv_work_t* work_req, int status); - static Handle ExecuteNonQuery(const Arguments& args); + static NAN_METHOD(ExecuteNonQuery); static void UV_ExecuteNonQuery(uv_work_t* work_req); static void UV_AfterExecuteNonQuery(uv_work_t* work_req, int status); - static Handle Prepare(const Arguments& args); + static NAN_METHOD(Prepare); static void UV_Prepare(uv_work_t* work_req); static void UV_AfterPrepare(uv_work_t* work_req, int status); - static Handle Bind(const Arguments& args); + static NAN_METHOD(Bind); static void UV_Bind(uv_work_t* work_req); static void UV_AfterBind(uv_work_t* work_req, int status); //sync methods - static Handle CloseSync(const Arguments& args); - static Handle ExecuteSync(const Arguments& args); - static Handle ExecuteDirectSync(const Arguments& args); - static Handle ExecuteNonQuerySync(const Arguments& args); - static Handle PrepareSync(const Arguments& args); - static Handle BindSync(const Arguments& args); + static NAN_METHOD(CloseSync); + static NAN_METHOD(ExecuteSync); + static NAN_METHOD(ExecuteDirectSync); + static NAN_METHOD(ExecuteNonQuerySync); + static NAN_METHOD(PrepareSync); + static NAN_METHOD(BindSync); struct Fetch_Request { Persistent callback; From 243031342100fe9435bb085f2ea55e7fb934f1d8 Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Sat, 18 Apr 2015 21:52:51 -0700 Subject: [PATCH 424/511] Finished first pass of nan conversion in all files. --- src/odbc.cpp | 158 ++++++++++++++++++++-------------------- src/odbc.h | 8 +- src/odbc_connection.cpp | 6 +- src/odbc_result.cpp | 8 +- src/odbc_statement.cpp | 16 ++-- 5 files changed, 97 insertions(+), 99 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index fb88747b..c979cb29 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -43,15 +43,15 @@ uv_async_t ODBC::g_async; Persistent ODBC::constructor_template; -void ODBC::Init(v8::Handle target) { +void ODBC::Init(v8::Handle exports) { DEBUG_PRINTF("ODBC::Init\n"); - HandleScope scope; + NanScope(); Local t = FunctionTemplate::New(New); // Constructor Template - constructor_template = Persistent::New(t); - constructor_template->SetClassName(String::NewSymbol("ODBC")); + NanAssignPersistent(constructor_template, t); + constructor_template->SetClassName(NanNew("ODBC")); // Reserve space for one Handle Local instance_template = constructor_template->InstanceTemplate(); @@ -71,7 +71,7 @@ void ODBC::Init(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnectionSync", CreateConnectionSync); // Attach the Database Constructor to the target object - target->Set( v8::String::NewSymbol("ODBC"), + exports->Set(NanNew("ODBC"), constructor_template->GetFunction()); #if NODE_VERSION_AT_LEAST(0, 7, 9) @@ -113,9 +113,9 @@ void ODBC::Free() { } } -Handle ODBC::New(const Arguments& args) { +NAN_METHOD(ODBC::New) { DEBUG_PRINTF("ODBC::New\n"); - HandleScope scope; + NanScope(); ODBC* dbo = new ODBC(); dbo->Wrap(args.Holder()); @@ -132,12 +132,12 @@ Handle ODBC::New(const Arguments& args) { Local objError = ODBC::GetSQLError(SQL_HANDLE_ENV, dbo->m_hEnv); - ThrowException(objError); + NanThrowError(objError); } SQLSetEnvAttr(dbo->m_hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER); - return scope.Close(args.Holder()); + NanReturnValue(args.Holder()); } void ODBC::WatcherCallback(uv_async_t *w, int revents) { @@ -149,9 +149,9 @@ void ODBC::WatcherCallback(uv_async_t *w, int revents) { * CreateConnection */ -Handle ODBC::CreateConnection(const Arguments& args) { +NAN_METHOD(ODBC::CreateConnection) { DEBUG_PRINTF("ODBC::CreateConnection\n"); - HandleScope scope; + NanScope(); REQ_FUN_ARG(0, cb); @@ -164,7 +164,7 @@ Handle ODBC::CreateConnection(const Arguments& args) { create_connection_work_data* data = (create_connection_work_data *) (calloc(1, sizeof(create_connection_work_data))); - data->cb = Persistent::New(cb); + NanAssignPersistent(data->cb, cb); data->dbo = dbo; work_req->data = data; @@ -173,7 +173,7 @@ Handle ODBC::CreateConnection(const Arguments& args) { dbo->Ref(); - return scope.Close(Undefined()); + NanReturnValue(Undefined()); } void ODBC::UV_CreateConnection(uv_work_t* req) { @@ -192,7 +192,7 @@ void ODBC::UV_CreateConnection(uv_work_t* req) { void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { DEBUG_PRINTF("ODBC::UV_AfterCreateConnection\n"); - HandleScope scope; + NanScope(); create_connection_work_data* data = (create_connection_work_data *)(req->data); @@ -235,9 +235,9 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { * CreateConnectionSync */ -Handle ODBC::CreateConnectionSync(const Arguments& args) { +NAN_METHOD(ODBC::CreateConnectionSync) { DEBUG_PRINTF("ODBC::CreateConnectionSync\n"); - HandleScope scope; + NanScope(); ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); @@ -261,7 +261,7 @@ Handle ODBC::CreateConnectionSync(const Arguments& args) { Local js_result(ODBCConnection::constructor_template-> GetFunction()->NewInstance(2, params)); - return scope.Close(js_result); + NanReturnValue(js_result); } /* @@ -342,7 +342,7 @@ void ODBC::FreeColumns(Column* columns, short* colCount) { Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength) { - HandleScope scope; + NanScope(); SQLLEN len = 0; //reset the buffer @@ -370,12 +370,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret); if (len == SQL_NULL_DATA) { - return scope.Close(Null()); - //return Null(); + NanReturnValue(Null()); } else { - return scope.Close(Integer::New(value)); - //return Integer::New(value); + NanReturnValue(NanNew(value)); } } break; @@ -399,11 +397,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret, value); if (len == SQL_NULL_DATA) { - return scope.Close(Null()); + NanReturnValue(Null()); //return Null(); } else { - return scope.Close(Number::New(value)); + NanReturnValue(Number::New(value)); //return Number::New(value); } } @@ -427,7 +425,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - return scope.Close(Null()); + NanReturnValue(Null()); //return Null(); } else { @@ -438,10 +436,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, timeInfo.tm_isdst = -1; //return Date::New((double(mktime(&timeInfo)) * 1000)); - return scope.Close(Date::New((double(mktime(&timeInfo)) * 1000))); + NanReturnValue(Date::New((double(mktime(&timeInfo)) * 1000))); } else { - return scope.Close(String::New((char *) buffer)); + NanReturnValue(NanNew((char *) buffer)); } } #else @@ -473,7 +471,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - return scope.Close(Null()); + NanReturnValue(Null()); //return Null(); } else { @@ -489,10 +487,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //at the specified time. timeInfo.tm_isdst = -1; #ifdef TIMEGM - return scope.Close(Date::New((double(timegm(&timeInfo)) * 1000) + NanReturnValue(Date::New((double(timegm(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000))); #else - return scope.Close(Date::New((double(timelocal(&timeInfo)) * 1000) + NanReturnValue(Date::New((double(timelocal(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000))); #endif //return Date::New((double(timegm(&timeInfo)) * 1000) @@ -515,11 +513,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - return scope.Close(Null()); + NanReturnValue(Null()); //return Null(); } else { - return scope.Close(Boolean::New(( *buffer == '0') ? false : true )); + NanReturnValue(Boolean::New(( *buffer == '0') ? false : true )); //return Boolean::New(( *buffer == '0') ? false : true ); } default : @@ -539,7 +537,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); if (len == SQL_NULL_DATA && str.IsEmpty()) { - return scope.Close(Null()); + NanReturnValue(Null()); //return Null(); } @@ -547,7 +545,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //we have captured all of the data //double check that we have some data else return null if (str.IsEmpty()){ - return scope.Close(Null()); + NanReturnValue(Null()); } break; @@ -558,17 +556,17 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, if (count == 0) { //no concatenation required, this is our first pass #ifdef UNICODE - str = String::New((uint16_t*) buffer); + str = NanNew((uint16_t*) buffer); #else - str = String::New((char *) buffer); + str = NanNew((char *) buffer); #endif } else { //we need to concatenate #ifdef UNICODE - str = String::Concat(str, String::New((uint16_t*) buffer)); + str = String::Concat(str, NanNew((uint16_t*) buffer)); #else - str = String::Concat(str, String::New((char *) buffer)); + str = String::Concat(str, NanNew((char *) buffer)); #endif } @@ -594,7 +592,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //Not sure if throwing here will work out well for us but we can try //since we should have a valid handle and the error is something we //can look into - return ThrowException(ODBC::GetSQLError( + return NanThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, hStmt, (char *) "[node-odbc] Error in ODBC::GetColumnValue" @@ -604,7 +602,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, } } while (true); - return scope.Close(str); + NanReturnValue(str); //return str; } } @@ -616,22 +614,22 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { - HandleScope scope; + NanScope(); Local tuple = Object::New(); for(int i = 0; i < *colCount; i++) { #ifdef UNICODE - tuple->Set( String::New((uint16_t *) columns[i].name), + tuple->Set( NanNew((uint16_t *) columns[i].name), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); #else - tuple->Set( String::New((const char *) columns[i].name), + tuple->Set( NanNew((const char *) columns[i].name), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); #endif } //return tuple; - return scope.Close(tuple); + NanReturnValue(tuple); } /* @@ -641,17 +639,17 @@ Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { - HandleScope scope; + NanScope(); Local array = Array::New(); for(int i = 0; i < *colCount; i++) { - array->Set( Integer::New(i), + array->Set( NanNew(i), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); } //return array; - return scope.Close(array); + NanReturnValue(array); } /* @@ -772,9 +770,9 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, Persistent cb) { - HandleScope scope; + NanScope(); - return scope.Close(CallbackSQLError( + NanReturnValue(CallbackSQLError( handleType, handle, (char *) "[node-odbc] SQL_ERROR", @@ -785,7 +783,7 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, Persistent cb) { - HandleScope scope; + NanScope(); Local objError = ODBC::GetSQLError( handleType, @@ -797,7 +795,7 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, args[0] = objError; cb->Call(Context::GetCurrent()->Global(), 1, args); - return scope.Close(Undefined()); + NanReturnValue(Undefined()); } /* @@ -805,21 +803,21 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, */ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle) { - HandleScope scope; + NanScope(); - return scope.Close(GetSQLError( + NanReturnValue(GetSQLError( handleType, handle, (char *) "[node-odbc] SQL_ERROR")); } Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message) { - HandleScope scope; + NanScope(); DEBUG_PRINTF("ODBC::GetSQLError : handleType=%i, handle=%p\n", handleType, handle); Local objError = Object::New(); - Local str = String::New(""); + Local str = NanNew(""); SQLINTEGER i = 0; SQLINTEGER native; @@ -860,19 +858,19 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* if (SQL_SUCCEEDED(ret)) { DEBUG_PRINTF("ODBC::GetSQLError : errorMessage=%s, errorSQLState=%s\n", errorMessage, errorSQLState); - objError->Set(String::New("error"), String::New(message)); + objError->Set(NanNew("error"), NanNew(message)); #ifdef UNICODE - str = String::Concat(str, String::New((uint16_t *) errorMessage)); + str = String::Concat(str, NanNew((uint16_t *) errorMessage)); - objError->SetPrototype(Exception::Error(String::New((uint16_t *) errorMessage))); - objError->Set(String::New("message"), str); - objError->Set(String::New("state"), String::New((uint16_t *) errorSQLState)); + objError->SetPrototype(Exception::Error(NanNew((uint16_t *) errorMessage))); + objError->Set(NanNew("message"), str); + objError->Set(NanNew("state"), NanNew((uint16_t *) errorSQLState)); #else - str = String::Concat(str, String::New(errorMessage)); + str = String::Concat(str, NanNew(errorMessage)); - objError->SetPrototype(Exception::Error(String::New(errorMessage))); - objError->Set(String::New("message"), str); - objError->Set(String::New("state"), String::New(errorSQLState)); + objError->SetPrototype(Exception::Error(NanNew(errorMessage))); + objError->Set(NanNew("message"), str); + objError->Set(NanNew("state"), NanNew(errorSQLState)); #endif } else if (ret == SQL_NO_DATA) { break; @@ -881,13 +879,13 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* if (statusRecCount == 0) { //Create a default error object if there were no diag records - objError->Set(String::New("error"), String::New(message)); - objError->SetPrototype(Exception::Error(String::New(message))); - objError->Set(String::New("message"), String::New( + objError->Set(NanNew("error"), NanNew(message)); + objError->SetPrototype(Exception::Error(NanNew(message))); + objError->Set(NanNew("message"), NanNew( (const char *) "[node-odbc] An error occurred but no diagnostic information was available.")); } - return scope.Close(objError); + NanReturnValue(objError); } /* @@ -901,7 +899,7 @@ Local ODBC::GetAllRecordsSync (HENV hENV, int bufferLength) { DEBUG_PRINTF("ODBC::GetAllRecordsSync\n"); - HandleScope scope; + NanScope(); Local objError = Object::New(); @@ -941,7 +939,7 @@ Local ODBC::GetAllRecordsSync (HENV hENV, } rows->Set( - Integer::New(count), + NanNew(count), ODBC::GetRecordTuple( hSTMT, columns, @@ -954,31 +952,31 @@ Local ODBC::GetAllRecordsSync (HENV hENV, } //TODO: what do we do about errors!?! //we throw them - return scope.Close(rows); + NanReturnValue(rows); } #ifdef dynodbc -Handle ODBC::LoadODBCLibrary(const Arguments& args) { - HandleScope scope; +NAN_METHOD(ODBC::LoadODBCLibrary) { + NanScope(); REQ_STR_ARG(0, js_library); bool result = DynLoadODBC(*js_library); - return scope.Close((result) ? True() : False()); + NanReturnValue((result) ? True() : False()); } #endif -extern "C" void init (v8::Handle target) { +extern "C" void init(v8::Handle exports) { #ifdef dynodbc - target->Set(String::NewSymbol("loadODBCLibrary"), + exports->Set(NanNew("loadODBCLibrary"), FunctionTemplate::New(ODBC::LoadODBCLibrary)->GetFunction()); #endif - ODBC::Init(target); - ODBCResult::Init(target); - ODBCConnection::Init(target); - ODBCStatement::Init(target); + ODBC::Init(exports); + ODBCResult::Init(exports); + ODBCConnection::Init(exports); + ODBCStatement::Init(exports); } NODE_MODULE(odbc_bindings, init) diff --git a/src/odbc.h b/src/odbc.h index ba12afcd..24058a83 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -77,7 +77,7 @@ class ODBC : public node::ObjectWrap { static uv_mutex_t g_odbcMutex; static uv_async_t g_async; - static void Init(v8::Handle target); + static void Init(v8::Handle exports); static Column* GetColumns(SQLHSTMT hStmt, short* colCount); static void FreeColumns(Column* columns, short* colCount); static Handle GetColumnValue(SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength); @@ -100,17 +100,17 @@ class ODBC : public node::ObjectWrap { ~ODBC(); - static Handle New(const Arguments& args); + static NAN_METHOD(New); //async methods - static Handle CreateConnection(const Arguments& args); + static NAN_METHOD(CreateConnection); static void UV_CreateConnection(uv_work_t* work_req); static void UV_AfterCreateConnection(uv_work_t* work_req, int status); static void WatcherCallback(uv_async_t* w, int revents); //sync methods - static Handle CreateConnectionSync(const Arguments& args); + static NAN_METHOD(CreateConnectionSync); ODBC *self(void) { return this; } diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 5b2f8f21..881a3b62 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -469,7 +469,7 @@ NAN_METHOD(ODBCConnection::Close) { conn->Ref(); - NanReturnUndefined(); + NanReturnValue(Undefined()); } void ODBCConnection::UV_Close(uv_work_t* req) { @@ -811,7 +811,7 @@ NAN_METHOD(ODBCConnection::Query) { conn->Ref(); - NanReturnUndefined(); + NanReturnValue(Undefined()); } void ODBCConnection::UV_Query(uv_work_t* req) { @@ -1602,7 +1602,7 @@ NAN_METHOD(ODBCConnection::EndTransaction) { UV_EndTransaction, (uv_after_work_cb)UV_AfterEndTransaction); - NanReturnUndefined(); + NanReturnValue(Undefined()); } /* diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index c39cad6e..c536710b 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -199,7 +199,7 @@ NAN_METHOD(ODBCResult::Fetch) { objODBCResult->Ref(); - NanReturnUndefined(); + NanReturnValue(Undefined()); } void ODBCResult::UV_Fetch(uv_work_t* work_req) { @@ -391,10 +391,10 @@ NAN_METHOD(ODBCResult::FetchSync) { if (error) { NanThrowError(objError); - NanReturnNull(); + NanReturnValue(Null()); } else { - NanReturnNull(); + NanReturnValue(Null()); } } } @@ -450,7 +450,7 @@ NAN_METHOD(ODBCResult::FetchAll) { data->objResult->Ref(); - NanReturnUndefined(); + NanReturnValue(Undefined()); } void ODBCResult::UV_FetchAll(uv_work_t* work_req) { diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 780e0da6..32bb670b 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -175,7 +175,7 @@ NAN_METHOD(ODBCStatement::Execute) { stmt->Ref(); - NanReturnUndefined(); + NanReturnValue(Undefined()); } void ODBCStatement::UV_Execute(uv_work_t* req) { @@ -259,7 +259,7 @@ NAN_METHOD(ODBCStatement::ExecuteSync) { (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); - NanReturnNull(); + NanReturnValue(Null()); } else { Local args[4]; @@ -308,7 +308,7 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuery) { stmt->Ref(); - NanReturnUndefined(); + NanReturnValue(Undefined()); } void ODBCStatement::UV_ExecuteNonQuery(uv_work_t* req) { @@ -395,7 +395,7 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuerySync) { (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); - NanReturnNull(); + NanReturnValue(Null()); } else { SQLLEN rowCount = 0; @@ -457,7 +457,7 @@ NAN_METHOD(ODBCStatement::ExecuteDirect) { stmt->Ref(); - NanReturnUndefined(); + NanReturnValue(Undefined()); } void ODBCStatement::UV_ExecuteDirect(uv_work_t* req) { @@ -663,7 +663,7 @@ NAN_METHOD(ODBCStatement::Prepare) { stmt->Ref(); - NanReturnUndefined(); + NanReturnValue(Undefined()); } void ODBCStatement::UV_Prepare(uv_work_t* req) { @@ -823,7 +823,7 @@ NAN_METHOD(ODBCStatement::BindSync) { NanReturnValue(NanFalse()); } - NanReturnUndefined(); + NanReturnValue(Undefined()); } /* @@ -897,7 +897,7 @@ NAN_METHOD(ODBCStatement::Bind) { stmt->Ref(); - NanReturnUndefined(); + NanReturnValue(Undefined()); } void ODBCStatement::UV_Bind(uv_work_t* req) { From cbd51e2379fd20daf64bf5d12774266901a651b9 Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Mon, 20 Apr 2015 15:02:18 -0700 Subject: [PATCH 425/511] More work toward supporting node v12. --- src/odbc.cpp | 73 ++++++++++--------- src/odbc_connection.cpp | 87 +++++++++++------------ src/odbc_connection.h | 2 +- src/odbc_result.cpp | 14 ++-- src/odbc_result.h | 2 +- src/odbc_statement.cpp | 150 ++++++++++++++++++++-------------------- src/odbc_statement.h | 2 +- 7 files changed, 161 insertions(+), 169 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index c979cb29..3aed6bfc 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -119,10 +119,12 @@ NAN_METHOD(ODBC::New) { ODBC* dbo = new ODBC(); dbo->Wrap(args.Holder()); + dbo->m_hEnv = NULL; uv_mutex_lock(&ODBC::g_odbcMutex); + // Initialize the Environment handle int ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &dbo->m_hEnv); uv_mutex_unlock(&ODBC::g_odbcMutex); @@ -132,9 +134,10 @@ NAN_METHOD(ODBC::New) { Local objError = ODBC::GetSQLError(SQL_HANDLE_ENV, dbo->m_hEnv); - NanThrowError(objError); + return NanThrowError(objError); } + // Use ODBC 3.x behavior SQLSetEnvAttr(dbo->m_hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER); NanReturnValue(args.Holder()); @@ -173,7 +176,7 @@ NAN_METHOD(ODBC::CreateConnection) { dbo->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBC::UV_CreateConnection(uv_work_t* req) { @@ -207,14 +210,13 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { } else { Local args[2]; - args[0] = External::New(data->dbo->m_hEnv); - args[1] = External::New(data->hDBC); + args[0] = NanNew(data->dbo->m_hEnv); + args[1] = NanNew(data->hDBC); - Local js_result(ODBCConnection::constructor_template-> - GetFunction()->NewInstance(2, args)); + Local js_result = NanNew(ODBCConnection::constructor)->NewInstance(2, args); - args[0] = Local::New(Null()); - args[1] = Local::New(js_result); + args[0] = NanNew(NanNull()); + args[1] = NanNew(js_result); data->cb->Call(Context::GetCurrent()->Global(), 2, args); } @@ -255,8 +257,8 @@ NAN_METHOD(ODBC::CreateConnectionSync) { uv_mutex_unlock(&ODBC::g_odbcMutex); Local params[2]; - params[0] = External::New(dbo->m_hEnv); - params[1] = External::New(hDBC); + params[0] = NanNew(dbo->m_hEnv); + params[1] = NanNew(hDBC); Local js_result(ODBCConnection::constructor_template-> GetFunction()->NewInstance(2, params)); @@ -370,7 +372,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret); if (len == SQL_NULL_DATA) { - NanReturnValue(Null()); + NanReturnValue(NanNull()); } else { NanReturnValue(NanNew(value)); @@ -397,7 +399,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret, value); if (len == SQL_NULL_DATA) { - NanReturnValue(Null()); + NanReturnValue(NanNull()); //return Null(); } else { @@ -425,7 +427,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - NanReturnValue(Null()); + NanReturnValue(NanNull()); //return Null(); } else { @@ -471,7 +473,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - NanReturnValue(Null()); + NanReturnValue(NanNull()); //return Null(); } else { @@ -513,7 +515,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - NanReturnValue(Null()); + NanReturnValue(NanNull()); //return Null(); } else { @@ -537,7 +539,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); if (len == SQL_NULL_DATA && str.IsEmpty()) { - NanReturnValue(Null()); + NanReturnValue(NanNull()); //return Null(); } @@ -545,7 +547,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //we have captured all of the data //double check that we have some data else return null if (str.IsEmpty()){ - NanReturnValue(Null()); + NanReturnValue(NanNull()); } break; @@ -699,19 +701,17 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { string->WriteUtf8((char *) params[i].ParameterValuePtr); #endif - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsString(): params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i " - "value=%s\n", i, params[i].ValueType, params[i].ParameterType, - params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, - (char*) params[i].ParameterValuePtr); + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsString(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i value=%s\n", + i, params[i].ValueType, params[i].ParameterType, + params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, + (char*) params[i].ParameterValuePtr); } else if (value->IsNull()) { params[i].ValueType = SQL_C_DEFAULT; params[i].ParameterType = SQL_VARCHAR; params[i].StrLen_or_IndPtr = SQL_NULL_DATA; - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNull(): params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNull(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i\n", i, params[i].ValueType, params[i].ParameterType, params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr); } @@ -722,11 +722,10 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].ParameterValuePtr = number; params[i].StrLen_or_IndPtr = 0; - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsInt32(): params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i " - "value=%lld\n", i, params[i].ValueType, params[i].ParameterType, - params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, - *number); + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsInt32(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i value=%lld\n", + i, params[i].ValueType, params[i].ParameterType, + params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, + *number); } else if (value->IsNumber()) { double *number = new double(value->NumberValue()); @@ -739,12 +738,10 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].DecimalDigits = 7; params[i].ColumnSize = sizeof(double); - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i " - "value=%f\n", - i, params[i].ValueType, params[i].ParameterType, - params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, - *number); + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i value=%f\n", + i, params[i].ValueType, params[i].ParameterType, + params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, + *number); } else if (value->IsBoolean()) { bool *boolean = new bool(value->BooleanValue()); @@ -753,8 +750,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].ParameterValuePtr = boolean; params[i].StrLen_or_IndPtr = 0; - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsBoolean(): params[%i] " - "c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsBoolean(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i\n", i, params[i].ValueType, params[i].ParameterType, params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr); } @@ -795,7 +791,7 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, args[0] = objError; cb->Call(Context::GetCurrent()->Global(), 1, args); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } /* @@ -862,6 +858,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* #ifdef UNICODE str = String::Concat(str, NanNew((uint16_t *) errorMessage)); + //TODO: Should this also be str? objError->SetPrototype(Exception::Error(NanNew((uint16_t *) errorMessage))); objError->Set(NanNew("message"), str); objError->Set(NanNew("state"), NanNew((uint16_t *) errorSQLState)); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 881a3b62..3bd1f257 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -30,7 +30,7 @@ using namespace v8; using namespace node; -Persistent ODBCConnection::constructor_template; +Persistent ODBCConnection::constructor; Persistent ODBCConnection::OPTION_SQL = Persistent::New(NanNew("sql")); Persistent ODBCConnection::OPTION_PARAMS = Persistent::New(NanNew("params")); Persistent ODBCConnection::OPTION_NORESULTS = Persistent::New(NanNew("noResults")); @@ -39,10 +39,9 @@ void ODBCConnection::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCConnection::Init\n"); NanScope(); - Local t = NanNew(New); + Local constructor_template = NanNew(New); // Constructor Template - NanAssignPersistent(constructor_template, t); constructor_template->SetClassName(NanNew("ODBCConnection")); // Reserve space for one Handle @@ -74,8 +73,8 @@ void ODBCConnection::Init(v8::Handle exports) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "tables", Tables); // Attach the Database Constructor to the target object - exports->Set( NanNew("ODBCConnection"), - constructor_template->GetFunction()); + NanAssignPersistent(constructor, constructor_template->GetFunction()); + exports->Set( NanNew("ODBCConnection"), constructor_template->GetFunction()); } ODBCConnection::~ODBCConnection() { @@ -430,8 +429,7 @@ NAN_METHOD(ODBCConnection::OpenSync) { free(connectionString); if (err) { - NanThrowError(objError); - NanReturnValue(NanFalse()); + return NanThrowError(objError); } else { NanReturnValue(NanTrue()); @@ -469,7 +467,7 @@ NAN_METHOD(ODBCConnection::Close) { conn->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCConnection::UV_Close(uv_work_t* req) { @@ -575,12 +573,11 @@ NAN_METHOD(ODBCConnection::CreateStatementSync) { uv_mutex_unlock(&ODBC::g_odbcMutex); Local params[3]; - params[0] = External::New(conn->m_hENV); - params[1] = External::New(conn->m_hDBC); - params[2] = External::New(hSTMT); + params[0] = NanNew(conn->m_hENV); + params[1] = NanNew(conn->m_hDBC); + params[2] = NanNew(hSTMT); - Local js_result(ODBCStatement::constructor_template-> - GetFunction()->NewInstance(3, params)); + Local js_result(NanNew(ODBCStatement::constructor)->NewInstance(3, params)); NanReturnValue(js_result); } @@ -618,7 +615,7 @@ NAN_METHOD(ODBCConnection::CreateStatement) { conn->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCConnection::UV_CreateStatement(uv_work_t* req) { @@ -662,15 +659,14 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { ); Local args[3]; - args[0] = External::New(data->conn->m_hENV); - args[1] = External::New(data->conn->m_hDBC); - args[2] = External::New(data->hSTMT); + args[0] = NanNew(data->conn->m_hENV); + args[1] = NanNew(data->conn->m_hDBC); + args[2] = NanNew(data->hSTMT); - Local js_result(ODBCStatement::constructor_template-> - GetFunction()->NewInstance(3, args)); + Local js_result = NanNew(ODBCStatement::constructor)->NewInstance(3, args); - args[0] = Local::New(Null()); - args[1] = Local::New(js_result); + args[0] = NanNew(NanNull()); + args[1] = NanNew(js_result); TryCatch try_catch; @@ -811,7 +807,7 @@ NAN_METHOD(ODBCConnection::Query) { conn->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCConnection::UV_Query(uv_work_t* req) { @@ -838,9 +834,9 @@ void ODBCConnection::UV_Query(uv_work_t* req) { prm = data->params[i]; - DEBUG_TPRINTF( + /*DEBUG_TPRINTF( SQL_T("ODBCConnection::UV_Query - param[%i]: ValueType=%i type=%i BufferLength=%i size=%i length=%i &length=%X\n"), i, prm.ValueType, prm.ParameterType, - prm.BufferLength, prm.ColumnSize, prm.length, &data->params[i].length); + prm.BufferLength, prm.ColumnSize, prm.length, &data->params[i].length);*/ ret = SQLBindParameter( data->hSTMT, //StatementHandle @@ -894,8 +890,8 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { uv_mutex_unlock(&ODBC::g_odbcMutex); Local args[2]; - args[0] = Local::New(Null()); - args[1] = Local::New(NanTrue()); + args[0] = NanNew(NanNull()); + args[1] = NanNew(NanTrue()); data->cb->Call(Context::GetCurrent()->Global(), 2, args); } @@ -903,21 +899,20 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { Local args[4]; bool* canFreeHandle = new bool(true); - args[0] = External::New(data->conn->m_hENV); - args[1] = External::New(data->conn->m_hDBC); - args[2] = External::New(data->hSTMT); - args[3] = External::New(canFreeHandle); + args[0] = NanNew(data->conn->m_hENV); + args[1] = NanNew(data->conn->m_hDBC); + args[2] = NanNew(data->hSTMT); + args[3] = NanNew(canFreeHandle); - Local js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(4, args)); + Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, args); // Check now to see if there was an error (as there may be further result sets) if (data->result == SQL_ERROR) { args[0] = ODBC::GetSQLError(SQL_HANDLE_STMT, data->hSTMT, (char *) "[node-odbc] SQL_ERROR"); } else { - args[0] = Local::New(Null()); + args[0] = NanNew(NanNull()); } - args[1] = Local::New(js_result); + args[1] = NanNew(js_result); data->cb->Call(Context::GetCurrent()->Global(), 2, args); } @@ -1079,8 +1074,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { prm = params[i]; DEBUG_PRINTF( - "ODBCConnection::UV_Query - param[%i]: ValueType=%i type=%i " - "BufferLength=%i size=%i length=%i &length=%X\n", i, prm.ValueType, prm.ParameterType, + "ODBCConnection::UV_Query - param[%i]: ValueType=%i type=%i BufferLength=%i size=%i length=%i &length=%X\n", i, prm.ValueType, prm.ParameterType, prm.BufferLength, prm.ColumnSize, prm.StrLen_or_IndPtr, ¶ms[i].StrLen_or_IndPtr); ret = SQLBindParameter( @@ -1132,7 +1126,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { (char *) "[node-odbc] Error in ODBCConnection::QuerySync" )); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } else if (noResultObject) { //if there is not result object requested then @@ -1149,13 +1143,12 @@ NAN_METHOD(ODBCConnection::QuerySync) { Local args[4]; bool* canFreeHandle = new bool(true); - args[0] = External::New(conn->m_hENV); - args[1] = External::New(conn->m_hDBC); - args[2] = External::New(hSTMT); - args[3] = External::New(canFreeHandle); + args[0] = NanNew(conn->m_hENV); + args[1] = NanNew(conn->m_hDBC); + args[2] = NanNew(hSTMT); + args[3] = NanNew(canFreeHandle); - Local js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(4, args)); + Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, args); NanReturnValue(js_result); } @@ -1245,7 +1238,7 @@ NAN_METHOD(ODBCConnection::Tables) { conn->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCConnection::UV_Tables(uv_work_t* req) { @@ -1355,7 +1348,7 @@ NAN_METHOD(ODBCConnection::Columns) { conn->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCConnection::UV_Columns(uv_work_t* req) { @@ -1443,7 +1436,7 @@ NAN_METHOD(ODBCConnection::BeginTransaction) { UV_BeginTransaction, (uv_after_work_cb)UV_AfterBeginTransaction); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } /* @@ -1602,7 +1595,7 @@ NAN_METHOD(ODBCConnection::EndTransaction) { UV_EndTransaction, (uv_after_work_cb)UV_AfterEndTransaction); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } /* diff --git a/src/odbc_connection.h b/src/odbc_connection.h index fca0386b..131c780e 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -25,7 +25,7 @@ class ODBCConnection : public node::ObjectWrap { static Persistent OPTION_SQL; static Persistent OPTION_PARAMS; static Persistent OPTION_NORESULTS; - static Persistent constructor_template; + static Persistent constructor; static void Init(v8::Handle exports); diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index c536710b..260f11fc 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -29,17 +29,16 @@ using namespace v8; using namespace node; -Persistent ODBCResult::constructor_template; +Persistent ODBCResult::constructor; Persistent ODBCResult::OPTION_FETCH_MODE = Persistent::New(NanNew("fetchMode")); void ODBCResult::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCResult::Init\n"); NanScope(); - Local t = FunctionTemplate::New(New); + Local constructor_template = NanNew(New); // Constructor Template - NanAssignPersistent(constructor_template, t); constructor_template->SetClassName(NanNew("ODBCResult")); // Reserve space for one Handle @@ -60,6 +59,7 @@ void ODBCResult::Init(v8::Handle exports) { instance_template->SetAccessor(NanNew("fetchMode"), FetchModeGetter, FetchModeSetter); // Attach the Database Constructor to the target object + NanAssignPersistent(constructor, constructor_template->GetFunction()); exports->Set(NanNew("ODBCResult"), constructor_template->GetFunction()); } @@ -199,7 +199,7 @@ NAN_METHOD(ODBCResult::Fetch) { objODBCResult->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCResult::UV_Fetch(uv_work_t* work_req) { @@ -391,10 +391,10 @@ NAN_METHOD(ODBCResult::FetchSync) { if (error) { NanThrowError(objError); - NanReturnValue(Null()); + NanReturnValue(NanNull()); } else { - NanReturnValue(Null()); + NanReturnValue(NanNull()); } } } @@ -450,7 +450,7 @@ NAN_METHOD(ODBCResult::FetchAll) { data->objResult->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCResult::UV_FetchAll(uv_work_t* work_req) { diff --git a/src/odbc_result.h b/src/odbc_result.h index a50ad471..855e696d 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -22,7 +22,7 @@ class ODBCResult : public node::ObjectWrap { public: static Persistent OPTION_FETCH_MODE; - static Persistent constructor_template; + static Persistent constructor; static void Init(v8::Handle exports); void Free(); diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 32bb670b..dd8b0919 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -29,43 +29,43 @@ using namespace v8; using namespace node; -Persistent ODBCStatement::constructor_template; +Persistent ODBCStatement::constructor; void ODBCStatement::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCStatement::Init\n"); NanScope(); - Local t = FunctionTemplate::New(New); + Local t = NanNew(New); // Constructor Template - NanAssignPersistent(constructor_template, t); - constructor_template->SetClassName(NanNew("ODBCStatement")); + + t->SetClassName(NanNew("ODBCStatement")); // Reserve space for one Handle - Local instance_template = constructor_template->InstanceTemplate(); + Local instance_template = t->InstanceTemplate(); instance_template->SetInternalFieldCount(1); // Prototype Methods - NODE_SET_PROTOTYPE_METHOD(constructor_template, "execute", Execute); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeSync", ExecuteSync); + NODE_SET_PROTOTYPE_METHOD(t, "execute", Execute); + NODE_SET_PROTOTYPE_METHOD(t, "executeSync", ExecuteSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirect", ExecuteDirect); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeDirectSync", ExecuteDirectSync); + NODE_SET_PROTOTYPE_METHOD(t, "executeDirect", ExecuteDirect); + NODE_SET_PROTOTYPE_METHOD(t, "executeDirectSync", ExecuteDirectSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeNonQuery", ExecuteNonQuery); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeNonQuerySync", ExecuteNonQuerySync); + NODE_SET_PROTOTYPE_METHOD(t, "executeNonQuery", ExecuteNonQuery); + NODE_SET_PROTOTYPE_METHOD(t, "executeNonQuerySync", ExecuteNonQuerySync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepareSync", PrepareSync); + NODE_SET_PROTOTYPE_METHOD(t, "prepare", Prepare); + NODE_SET_PROTOTYPE_METHOD(t, "prepareSync", PrepareSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "bind", Bind); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "bindSync", BindSync); + NODE_SET_PROTOTYPE_METHOD(t, "bind", Bind); + NODE_SET_PROTOTYPE_METHOD(t, "bindSync", BindSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); + NODE_SET_PROTOTYPE_METHOD(t, "closeSync", CloseSync); // Attach the Database Constructor to the target object - exports->Set(NanNew("ODBCStatement"), - constructor_template->GetFunction()); + NanAssignPersistent(constructor, t->GetFunction()); + exports->Set(NanNew("ODBCStatement"), t->GetFunction()); } ODBCStatement::~ODBCStatement() { @@ -175,7 +175,7 @@ NAN_METHOD(ODBCStatement::Execute) { stmt->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCStatement::UV_Execute(uv_work_t* req) { @@ -210,21 +210,22 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { else { Local args[4]; bool* canFreeHandle = new bool(false); + + args[0] = NanNew(self->m_hENV); + args[1] = NanNew(self->m_hDBC); + args[2] = NanNew(self->m_hSTMT); + args[3] = NanNew(canFreeHandle); - args[0] = External::New(self->m_hENV); - args[1] = External::New(self->m_hDBC); - args[2] = External::New(self->m_hSTMT); - args[3] = External::New(canFreeHandle); - - Persistent js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(4, args)); + // TODO is this object being cleared anywhere? Memory leak? + Persistent js_result; + NanAssignPersistent(js_result, NanNew(ODBCResult::constructor)->NewInstance(4, args)); - args[0] = Local::New(Null()); - args[1] = Local::New(js_result); + args[0] = NanNew(NanNull()); + args[1] = NanNew(js_result); TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb->Call(NanGetCurrentContext()->Global(), 2, args); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -259,20 +260,19 @@ NAN_METHOD(ODBCStatement::ExecuteSync) { (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); - NanReturnValue(Null()); + NanReturnValue(NanNull()); } else { - Local args[4]; + Local result[4]; bool* canFreeHandle = new bool(false); - args[0] = External::New(stmt->m_hENV); - args[1] = External::New(stmt->m_hDBC); - args[2] = External::New(stmt->m_hSTMT); - args[3] = External::New(canFreeHandle); - - Local js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(4, args)); + result[0] = NanNew(stmt->m_hENV); + result[1] = NanNew(stmt->m_hDBC); + result[2] = NanNew(stmt->m_hSTMT); + result[3] = NanNew(canFreeHandle); + Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, result); + NanReturnValue(js_result); } } @@ -307,8 +307,8 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuery) { (uv_after_work_cb)UV_AfterExecuteNonQuery); stmt->Ref(); - - NanReturnValue(Undefined()); + + NanReturnValue(NanUndefined()); } void ODBCStatement::UV_ExecuteNonQuery(uv_work_t* req) { @@ -355,12 +355,12 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { Local args[2]; - args[0] = Local::New(Null()); - args[1] = Local::New(Number::New(rowCount)); + args[0] = NanNew(NanNull()); + args[1] = NanNew(NanNew(rowCount)); TryCatch try_catch; - - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + + data->cb->Call(NanGetCurrentContext()->Global(), 2, args); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -395,7 +395,7 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuerySync) { (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); - NanReturnValue(Null()); + NanReturnValue(NanNull()); } else { SQLLEN rowCount = 0; @@ -410,7 +410,7 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuerySync) { SQLFreeStmt(stmt->m_hSTMT, SQL_CLOSE); uv_mutex_unlock(&ODBC::g_odbcMutex); - NanReturnValue(Number::New(rowCount)); + NanReturnValue(NanNew(rowCount)); } } @@ -457,7 +457,7 @@ NAN_METHOD(ODBCStatement::ExecuteDirect) { stmt->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCStatement::UV_ExecuteDirect(uv_work_t* req) { @@ -496,16 +496,17 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { Local args[4]; bool* canFreeHandle = new bool(false); - args[0] = External::New(self->m_hENV); - args[1] = External::New(self->m_hDBC); - args[2] = External::New(self->m_hSTMT); - args[3] = External::New(canFreeHandle); + args[0] = NanNew(self->m_hENV); + args[1] = NanNew(self->m_hDBC); + args[2] = NanNew(self->m_hSTMT); + args[3] = NanNew(canFreeHandle); - Persistent js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(4, args)); + //TODO persistent leak? + Persistent js_result; + NanAssignPersistent(js_result, NanNew(ODBCResult::constructor)->NewInstance(4, args)); - args[0] = Local::New(Null()); - args[1] = Local::New(js_result); + args[0] = NanNew(NanNull()); + args[1] = NanNew(js_result); TryCatch try_catch; @@ -554,19 +555,20 @@ NAN_METHOD(ODBCStatement::ExecuteDirectSync) { (char *) "[node-odbc] Error in ODBCStatement::ExecuteDirectSync" )); - NanReturnValue(Null()); + NanReturnValue(NanNull()); } else { - Local args[4]; + Local result[4]; bool* canFreeHandle = new bool(false); - args[0] = External::New(stmt->m_hENV); - args[1] = External::New(stmt->m_hDBC); - args[2] = External::New(stmt->m_hSTMT); - args[3] = External::New(canFreeHandle); + result[0] = NanNew(stmt->m_hENV); + result[1] = NanNew(stmt->m_hDBC); + result[2] = NanNew(stmt->m_hSTMT); + result[3] = NanNew(canFreeHandle); - Persistent js_result(ODBCResult::constructor_template-> - GetFunction()->NewInstance(4, args)); + //TODO persistent leak? + Persistent js_result; + NanAssignPersistent(js_result, NanNew(ODBCResult::constructor)->NewInstance(4, result)); NanReturnValue(js_result); } @@ -663,7 +665,7 @@ NAN_METHOD(ODBCStatement::Prepare) { stmt->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCStatement::UV_Prepare(uv_work_t* req) { @@ -710,8 +712,8 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { else { Local args[2]; - args[0] = Local::New(Null()); - args[1] = Local::New(NanTrue()); + args[0] = NanNew(NanNull()); + args[1] = NanNew(NanTrue()); TryCatch try_catch; @@ -786,12 +788,12 @@ NAN_METHOD(ODBCStatement::BindSync) { for (int i = 0; i < stmt->paramCount; i++) { prm = stmt->params[i]; - DEBUG_PRINTF( + /*DEBUG_PRINTF( "ODBCStatement::BindSync - param[%i]: c_type=%i type=%i " "buffer_length=%i size=%i length=%i &length=%X decimals=%i value=%s\n", i, prm.ValueType, prm.ParameterType, prm.BufferLength, prm.ColumnSize, prm.length, &stmt->params[i].StrLen_or_IndPtr, prm.DecimalDigits, prm.ParameterValuePtr - ); + );*/ ret = SQLBindParameter( stmt->m_hSTMT, //StatementHandle @@ -823,7 +825,7 @@ NAN_METHOD(ODBCStatement::BindSync) { NanReturnValue(NanFalse()); } - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } /* @@ -897,7 +899,7 @@ NAN_METHOD(ODBCStatement::Bind) { stmt->Ref(); - NanReturnValue(Undefined()); + NanReturnValue(NanUndefined()); } void ODBCStatement::UV_Bind(uv_work_t* req) { @@ -917,12 +919,12 @@ void ODBCStatement::UV_Bind(uv_work_t* req) { for (int i = 0; i < data->stmt->paramCount; i++) { prm = data->stmt->params[i]; - DEBUG_PRINTF( + /*DEBUG_PRINTF( "ODBCStatement::UV_Bind - param[%i]: c_type=%i type=%i " "buffer_length=%i size=%i length=%i &length=%X decimals=%i value=%s\n", i, prm.ValueType, prm.ParameterType, prm.BufferLength, prm.ColumnSize, prm.length, &data->stmt->params[i].StrLen_or_IndPtr, prm.DecimalDigits, prm.ParameterValuePtr - ); + );*/ ret = SQLBindParameter( data->stmt->m_hSTMT, //StatementHandle @@ -964,8 +966,8 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { else { Local args[2]; - args[0] = Local::New(Null()); - args[1] = Local::New(NanTrue()); + args[0] = NanNew(NanNull()); + args[1] = NanNew(NanTrue()); TryCatch try_catch; diff --git a/src/odbc_statement.h b/src/odbc_statement.h index 783329ce..be59fc22 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -21,7 +21,7 @@ class ODBCStatement : public node::ObjectWrap { public: - static Persistent constructor_template; + static Persistent constructor; static void Init(v8::Handle exports); void Free(); From 2baf449413d18a2ffe4bc39b655fc90675ae8af6 Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Mon, 20 Apr 2015 22:21:26 -0700 Subject: [PATCH 426/511] Reworked an async call for v12. Also reworked constants. --- src/odbc.cpp | 47 ++++++++++++++++++++++----------------- src/odbc.h | 49 ++++++++++++++++++++++++++++------------- src/odbc_connection.cpp | 4 ++-- src/odbc_result.cpp | 2 +- src/odbc_statement.cpp | 4 ++-- 5 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 3aed6bfc..5a70906d 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -41,16 +41,15 @@ using namespace node; uv_mutex_t ODBC::g_odbcMutex; uv_async_t ODBC::g_async; -Persistent ODBC::constructor_template; +Persistent ODBC::constructor; void ODBC::Init(v8::Handle exports) { DEBUG_PRINTF("ODBC::Init\n"); NanScope(); - Local t = FunctionTemplate::New(New); + Local constructor_template = FunctionTemplate::New(New); // Constructor Template - NanAssignPersistent(constructor_template, t); constructor_template->SetClassName(NanNew("ODBC")); // Reserve space for one Handle @@ -58,19 +57,26 @@ void ODBC::Init(v8::Handle exports) { instance_template->SetInternalFieldCount(1); // Constants - NODE_DEFINE_CONSTANT(constructor_template, SQL_CLOSE); - NODE_DEFINE_CONSTANT(constructor_template, SQL_DROP); - NODE_DEFINE_CONSTANT(constructor_template, SQL_UNBIND); - NODE_DEFINE_CONSTANT(constructor_template, SQL_RESET_PARAMS); - NODE_DEFINE_CONSTANT(constructor_template, SQL_DESTROY); //SQL_DESTROY is non-standard - NODE_DEFINE_CONSTANT(constructor_template, FETCH_ARRAY); - NODE_DEFINE_CONSTANT(constructor_template, FETCH_OBJECT); +#if (NODE_MODULE_VERSION < NODE_0_12_MODULE_VERSION) + +#else + +#endif + + NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_CLOSE); + NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_DROP); + NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_UNBIND); + NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_RESET_PARAMS); + NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_DESTROY); //SQL_DESTROY is non-standard + NODE_ODBC_DEFINE_CONSTANT(constructor_template, FETCH_ARRAY); + NODE_ODBC_DEFINE_CONSTANT(constructor_template, FETCH_OBJECT); // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnection", CreateConnection); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnectionSync", CreateConnectionSync); // Attach the Database Constructor to the target object + NanAssignPersistent(constructor, constructor_template->GetFunction()); exports->Set(NanNew("ODBC"), constructor_template->GetFunction()); @@ -156,7 +162,9 @@ NAN_METHOD(ODBC::CreateConnection) { DEBUG_PRINTF("ODBC::CreateConnection\n"); NanScope(); - REQ_FUN_ARG(0, cb); + Local cb = args[0].As(); + NanCallback *callback = new NanCallback(cb); + //REQ_FUN_ARG(0, cb); ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); @@ -167,7 +175,7 @@ NAN_METHOD(ODBC::CreateConnection) { create_connection_work_data* data = (create_connection_work_data *) (calloc(1, sizeof(create_connection_work_data))); - NanAssignPersistent(data->cb, cb); + data->cb = callback; data->dbo = dbo; work_req->data = data; @@ -206,7 +214,7 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { args[0] = ODBC::GetSQLError(SQL_HANDLE_ENV, data->dbo->m_hEnv); - data->cb->Call(Context::GetCurrent()->Global(), 1, args); + data->cb->Call(1, args); } else { Local args[2]; @@ -216,9 +224,9 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { Local js_result = NanNew(ODBCConnection::constructor)->NewInstance(2, args); args[0] = NanNew(NanNull()); - args[1] = NanNew(js_result); + args[1] = NanNew(js_result); - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb->Call(2, args); } if (try_catch.HasCaught()) { @@ -227,7 +235,7 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { data->dbo->Unref(); - data->cb.Dispose(); + delete data->cb; free(data); free(req); @@ -260,8 +268,7 @@ NAN_METHOD(ODBC::CreateConnectionSync) { params[0] = NanNew(dbo->m_hEnv); params[1] = NanNew(hDBC); - Local js_result(ODBCConnection::constructor_template-> - GetFunction()->NewInstance(2, params)); + Local js_result = NanNew(ODBCConnection::constructor)->NewInstance(2, params); NanReturnValue(js_result); } @@ -403,7 +410,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //return Null(); } else { - NanReturnValue(Number::New(value)); + NanReturnValue(NanNew(value)); //return Number::New(value); } } @@ -813,7 +820,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* DEBUG_PRINTF("ODBC::GetSQLError : handleType=%i, handle=%p\n", handleType, handle); Local objError = Object::New(); - Local str = NanNew(""); + Local str = NanNew(""); SQLINTEGER i = 0; SQLINTEGER native; diff --git a/src/odbc.h b/src/odbc.h index 24058a83..38be6973 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -73,7 +73,7 @@ typedef struct { class ODBC : public node::ObjectWrap { public: - static Persistent constructor_template; + static Persistent constructor; static uv_mutex_t g_odbcMutex; static uv_async_t g_async; @@ -119,7 +119,7 @@ class ODBC : public node::ObjectWrap { }; struct create_connection_work_data { - Persistent cb; + NanCallback* cb; ODBC *dbo; HDBC hDBC; int result; @@ -155,22 +155,22 @@ struct query_request { }; #ifdef UNICODE - #define SQL_T(x) (L##x) +#define SQL_T(x) (L##x) #else - #define SQL_T(x) (x) +#define SQL_T(x) (x) #endif #ifdef DEBUG - #ifdef UNICODE - #define DEBUG_PRINTF(...) fwprintf(stdout, L##__VA_ARGS__) - #define DEBUG_TPRINTF(...) fwprintf(stdout, __VA_ARGS__) - #else - #define DEBUG_TPRINTF(...) fprintf(stdout, __VA_ARGS__) - #define DEBUG_PRINTF(...) fprintf(stdout, __VA_ARGS__) - #endif +#ifdef UNICODE +#define DEBUG_PRINTF(...) fwprintf(stdout, L##__VA_ARGS__) +#define DEBUG_TPRINTF(...) fwprintf(stdout, __VA_ARGS__) +#else +#define DEBUG_TPRINTF(...) fprintf(stdout, __VA_ARGS__) +#define DEBUG_PRINTF(...) fprintf(stdout, __VA_ARGS__) +#endif #else - #define DEBUG_PRINTF(...) (void)0 - #define DEBUG_TPRINTF(...) (void)0 +#define DEBUG_PRINTF(...) (void)0 +#define DEBUG_TPRINTF(...) (void)0 #endif #define REQ_ARGS(N) \ @@ -222,7 +222,7 @@ struct query_request { if (args.Length() <= (I) || !args[I]->IsBoolean()) \ return NanThrowTypeError("Argument " #I " must be a boolean"); \ Local VAR = (args[I]->ToBoolean()); - + #define REQ_EXT_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsExternal()) \ return NanThrowTypeError("Argument " #I " invalid"); \ @@ -232,11 +232,30 @@ struct query_request { int VAR; \ if (args.Length() <= (I)) { \ VAR = (DEFAULT); \ - } else if (args[I]->IsInt32()) { \ + } else if (args[I]->IsInt32()) { \ VAR = args[I]->Int32Value(); \ } else { \ return NanThrowTypeError("Argument " #I " must be an integer"); \ } +#if (NODE_MODULE_VERSION < NODE_0_12_MODULE_VERSION) + +// From node v10 NODE_DEFINE_CONSTANT +#define NODE_ODBC_DEFINE_CONSTANT(constructor_template, constant) \ + (constructor_template)->Set(NanNew(#constant), \ + NanNew(constant), \ + static_cast(v8::ReadOnly|v8::DontDelete)) + +#else + +// From node v12 NODE_DEFINE_CONSTANT +#define NODE_ODBC_DEFINE_CONSTANT(constructor_template, constant) \ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); \ + v8::Local constant_name = NanNew(#constant); \ + v8::Local constant_value = NanNew(static_cast(SQL_CLOSE)); \ + v8::PropertyAttribute constant_attributes = static_cast(v8::ReadOnly | v8::DontDelete); \ + constructor_template->PrototypeTemplate()->Set(constant_name, constant_value, constant_attributes); + +#endif #endif diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 3bd1f257..1cd1c3f6 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -666,7 +666,7 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { Local js_result = NanNew(ODBCStatement::constructor)->NewInstance(3, args); args[0] = NanNew(NanNull()); - args[1] = NanNew(js_result); + args[1] = NanNew(js_result); TryCatch try_catch; @@ -912,7 +912,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { } else { args[0] = NanNew(NanNull()); } - args[1] = NanNew(js_result); + args[1] = NanNew(js_result); data->cb->Call(Context::GetCurrent()->Global(), 2, args); } diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 260f11fc..4a069d95 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -45,7 +45,7 @@ void ODBCResult::Init(v8::Handle exports) { Local instance_template = constructor_template->InstanceTemplate(); instance_template->SetInternalFieldCount(1); - // Prototype Methods + // Prototype Methods NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAll", FetchAll); NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetch", Fetch); diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index dd8b0919..91f57bbb 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -221,7 +221,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { NanAssignPersistent(js_result, NanNew(ODBCResult::constructor)->NewInstance(4, args)); args[0] = NanNew(NanNull()); - args[1] = NanNew(js_result); + args[1] = NanNew(js_result); TryCatch try_catch; @@ -506,7 +506,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { NanAssignPersistent(js_result, NanNew(ODBCResult::constructor)->NewInstance(4, args)); args[0] = NanNew(NanNull()); - args[1] = NanNew(js_result); + args[1] = NanNew(js_result); TryCatch try_catch; From 9718ad31710d64ec5f6466d20ba97ff8859829e5 Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Tue, 21 Apr 2015 10:08:32 -0700 Subject: [PATCH 427/511] Node v12 support. Code compiles with node v12. --- src/odbc.cpp | 122 ++++++++++++++++----------------- src/odbc.h | 25 ++----- src/odbc_connection.cpp | 147 ++++++++++++++++++++++------------------ src/odbc_connection.h | 10 +-- src/odbc_result.cpp | 68 ++++++++++--------- src/odbc_result.h | 2 +- src/odbc_statement.cpp | 28 ++++---- src/odbc_statement.h | 10 +-- 8 files changed, 208 insertions(+), 204 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 5a70906d..b8da8589 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -47,7 +47,7 @@ void ODBC::Init(v8::Handle exports) { DEBUG_PRINTF("ODBC::Init\n"); NanScope(); - Local constructor_template = FunctionTemplate::New(New); + Local constructor_template = NanNew(New); // Constructor Template constructor_template->SetClassName(NanNew("ODBC")); @@ -62,13 +62,14 @@ void ODBC::Init(v8::Handle exports) { #else #endif - - NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_CLOSE); - NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_DROP); - NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_UNBIND); - NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_RESET_PARAMS); - NODE_ODBC_DEFINE_CONSTANT(constructor_template, SQL_DESTROY); //SQL_DESTROY is non-standard - NODE_ODBC_DEFINE_CONSTANT(constructor_template, FETCH_ARRAY); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + PropertyAttribute constant_attributes = static_cast(ReadOnly | DontDelete); + constructor_template->Set(NanNew("SQL_CLOSE"), NanNew(SQL_CLOSE), constant_attributes); + constructor_template->Set(NanNew("SQL_DROP"), NanNew(SQL_DROP), constant_attributes); + constructor_template->Set(NanNew("SQL_UNBIND"), NanNew(SQL_UNBIND), constant_attributes); + constructor_template->Set(NanNew("SQL_RESET_PARAMS"), NanNew(SQL_RESET_PARAMS), constant_attributes); + constructor_template->Set(NanNew("SQL_DESTROY"), NanNew(SQL_DESTROY), constant_attributes); + constructor_template->Set(NanNew("FETCH_ARRAY"), NanNew(FETCH_ARRAY), constant_attributes); NODE_ODBC_DEFINE_CONSTANT(constructor_template, FETCH_OBJECT); // Prototype Methods @@ -82,9 +83,9 @@ void ODBC::Init(v8::Handle exports) { #if NODE_VERSION_AT_LEAST(0, 7, 9) // Initialize uv_async so that we can prevent node from exiting - uv_async_init( uv_default_loop(), - &ODBC::g_async, - ODBC::WatcherCallback); + //uv_async_init( uv_default_loop(), + // &ODBC::g_async, + // ODBC::WatcherCallback); // Not sure if the init automatically calls uv_ref() because there is weird // behavior going on. When ODBC::Init is called which initializes the @@ -93,7 +94,7 @@ void ODBC::Init(v8::Handle exports) { // we have a connection. // so to work around this, I am possibly mistakenly calling uv_unref() once // so that there are no references on the loop. - uv_unref((uv_handle_t *)&ODBC::g_async); + //uv_unref((uv_handle_t *)&ODBC::g_async); #endif // Initialize the cross platform mutex provided by libuv @@ -149,10 +150,10 @@ NAN_METHOD(ODBC::New) { NanReturnValue(args.Holder()); } -void ODBC::WatcherCallback(uv_async_t *w, int revents) { - DEBUG_PRINTF("ODBC::WatcherCallback\n"); - //i don't know if we need to do anything here -} +//void ODBC::WatcherCallback(uv_async_t *w, int revents) { +// DEBUG_PRINTF("ODBC::WatcherCallback\n"); +// //i don't know if we need to do anything here +//} /* * CreateConnection @@ -351,7 +352,7 @@ void ODBC::FreeColumns(Column* columns, short* colCount) { Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength) { - NanScope(); + NanEscapableScope(); SQLLEN len = 0; //reset the buffer @@ -379,10 +380,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret); if (len == SQL_NULL_DATA) { - NanReturnValue(NanNull()); + return NanEscapeScope(NanNull()); } else { - NanReturnValue(NanNew(value)); + return NanEscapeScope(NanNew(value)); } } break; @@ -406,11 +407,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret, value); if (len == SQL_NULL_DATA) { - NanReturnValue(NanNull()); + return NanEscapeScope(NanNull()); //return Null(); } else { - NanReturnValue(NanNew(value)); + return NanEscapeScope(NanNew(value)); //return Number::New(value); } } @@ -434,7 +435,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - NanReturnValue(NanNull()); + return NanEscapeScope(NanNull()); //return Null(); } else { @@ -443,12 +444,12 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //and system databases to attempt to determine whether DST is in effect //at the specified time. timeInfo.tm_isdst = -1; - - //return Date::New((double(mktime(&timeInfo)) * 1000)); - NanReturnValue(Date::New((double(mktime(&timeInfo)) * 1000))); + + //return NanEscapeScope(Date::New(Isolate::GetCurrent(), (double(mktime(&timeInfo)) * 1000)); + return NanEscapeScope(NanNew(double(mktime(&timeInfo)) * 1000)); } else { - NanReturnValue(NanNew((char *) buffer)); + return NanEscapeScope(NanNew((char *)buffer)); } } #else @@ -480,7 +481,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - NanReturnValue(NanNull()); + return NanEscapeScope(NanNull()); //return Null(); } else { @@ -496,10 +497,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //at the specified time. timeInfo.tm_isdst = -1; #ifdef TIMEGM - NanReturnValue(Date::New((double(timegm(&timeInfo)) * 1000) + return NanEscapeScope(NanNew((double(timegm(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000))); #else - NanReturnValue(Date::New((double(timelocal(&timeInfo)) * 1000) + return NanEscapeScope(NanNew((double(timelocal(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000))); #endif //return Date::New((double(timegm(&timeInfo)) * 1000) @@ -522,12 +523,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - NanReturnValue(NanNull()); - //return Null(); + return NanEscapeScope(NanNull()); } else { - NanReturnValue(Boolean::New(( *buffer == '0') ? false : true )); - //return Boolean::New(( *buffer == '0') ? false : true ); + return NanEscapeScope(NanNew((*buffer == '0') ? false : true)); } default : Local str; @@ -546,7 +545,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); if (len == SQL_NULL_DATA && str.IsEmpty()) { - NanReturnValue(NanNull()); + return NanEscapeScope(NanNull()); //return Null(); } @@ -554,7 +553,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //we have captured all of the data //double check that we have some data else return null if (str.IsEmpty()){ - NanReturnValue(NanNull()); + return NanEscapeScope(NanNull()); } break; @@ -601,18 +600,17 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //Not sure if throwing here will work out well for us but we can try //since we should have a valid handle and the error is something we //can look into - return NanThrowError(ODBC::GetSQLError( + NanThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, hStmt, (char *) "[node-odbc] Error in ODBC::GetColumnValue" )); - + return NanEscapeScope(NanUndefined()); break; } } while (true); - NanReturnValue(str); - //return str; + return NanEscapeScope(str); } } @@ -623,9 +621,9 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { - NanScope(); + NanEscapableScope(); - Local tuple = Object::New(); + Local tuple = NanNew(); for(int i = 0; i < *colCount; i++) { #ifdef UNICODE @@ -637,8 +635,7 @@ Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, #endif } - //return tuple; - NanReturnValue(tuple); + return NanEscapeScope(tuple); } /* @@ -648,17 +645,16 @@ Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { - NanScope(); + NanEscapableScope(); - Local array = Array::New(); + Local array = NanNew(); for(int i = 0; i < *colCount; i++) { array->Set( NanNew(i), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); } - //return array; - NanReturnValue(array); + return NanEscapeScope(array); } /* @@ -772,10 +768,10 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, - Persistent cb) { - NanScope(); + NanCallback* cb) { + NanEscapableScope(); - NanReturnValue(CallbackSQLError( + return NanEscapeScope(CallbackSQLError( handleType, handle, (char *) "[node-odbc] SQL_ERROR", @@ -785,8 +781,8 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, - Persistent cb) { - NanScope(); + NanCallback* cb) { + NanEscapableScope(); Local objError = ODBC::GetSQLError( handleType, @@ -796,9 +792,9 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, Local args[1]; args[0] = objError; - cb->Call(Context::GetCurrent()->Global(), 1, args); + cb->Call(1, args); - NanReturnValue(NanUndefined()); + return NanEscapeScope(NanUndefined()); } /* @@ -806,20 +802,20 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, */ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle) { - NanScope(); + NanEscapableScope(); - NanReturnValue(GetSQLError( + return NanEscapeScope(GetSQLError( handleType, handle, (char *) "[node-odbc] SQL_ERROR")); } Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message) { - NanScope(); + NanEscapableScope(); DEBUG_PRINTF("ODBC::GetSQLError : handleType=%i, handle=%p\n", handleType, handle); - Local objError = Object::New(); + Local objError = NanNew(); Local str = NanNew(""); SQLINTEGER i = 0; @@ -889,7 +885,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* (const char *) "[node-odbc] An error occurred but no diagnostic information was available.")); } - NanReturnValue(objError); + return NanEscapeScope(objError); } /* @@ -903,9 +899,9 @@ Local ODBC::GetAllRecordsSync (HENV hENV, int bufferLength) { DEBUG_PRINTF("ODBC::GetAllRecordsSync\n"); - NanScope(); + NanEscapableScope(); - Local objError = Object::New(); + Local objError = NanNew(); int count = 0; int errorCount = 0; @@ -913,7 +909,7 @@ Local ODBC::GetAllRecordsSync (HENV hENV, Column* columns = GetColumns(hSTMT, &colCount); - Local rows = Array::New(); + Local rows = NanNew(); //loop through all records while (true) { @@ -956,7 +952,7 @@ Local ODBC::GetAllRecordsSync (HENV hENV, } //TODO: what do we do about errors!?! //we throw them - NanReturnValue(rows); + return NanEscapeScope(rows); } #ifdef dynodbc diff --git a/src/odbc.h b/src/odbc.h index 38be6973..8b8a101b 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -83,8 +83,8 @@ class ODBC : public node::ObjectWrap { static Handle GetColumnValue(SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength); static Local GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); - static Handle CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, Persistent cb); - static Handle CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, Persistent cb); + static Handle CallbackSQLError(SQLSMALLINT handleType, SQLHANDLE handle, NanCallback* cb); + static Handle CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, NanCallback* cb); static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle); static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message); static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); @@ -209,8 +209,10 @@ struct query_request { //Require String or Null Argument; save String as String Object #define REQ_STRO_OR_NULL_ARG(I, VAR) \ - if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ - return NanThrowTypeError("Argument " #I " must be a string or null"); \ + if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) { \ + NanThrowTypeError("Argument " #I " must be a string or null"); \ + NanReturnUndefined(); \ + } \ Local VAR(args[I]->ToString()); #define REQ_FUN_ARG(I, VAR) \ @@ -232,13 +234,12 @@ struct query_request { int VAR; \ if (args.Length() <= (I)) { \ VAR = (DEFAULT); \ - } else if (args[I]->IsInt32()) { \ + } else if (args[I]->IsInt32()) { \ VAR = args[I]->Int32Value(); \ } else { \ return NanThrowTypeError("Argument " #I " must be an integer"); \ } -#if (NODE_MODULE_VERSION < NODE_0_12_MODULE_VERSION) // From node v10 NODE_DEFINE_CONSTANT #define NODE_ODBC_DEFINE_CONSTANT(constructor_template, constant) \ @@ -246,16 +247,4 @@ struct query_request { NanNew(constant), \ static_cast(v8::ReadOnly|v8::DontDelete)) -#else - -// From node v12 NODE_DEFINE_CONSTANT -#define NODE_ODBC_DEFINE_CONSTANT(constructor_template, constant) \ - v8::Isolate* isolate = v8::Isolate::GetCurrent(); \ - v8::Local constant_name = NanNew(#constant); \ - v8::Local constant_value = NanNew(static_cast(SQL_CLOSE)); \ - v8::PropertyAttribute constant_attributes = static_cast(v8::ReadOnly | v8::DontDelete); \ - constructor_template->PrototypeTemplate()->Set(constant_name, constant_value, constant_attributes); - -#endif - #endif diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 1cd1c3f6..f76aa407 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -31,14 +31,18 @@ using namespace v8; using namespace node; Persistent ODBCConnection::constructor; -Persistent ODBCConnection::OPTION_SQL = Persistent::New(NanNew("sql")); -Persistent ODBCConnection::OPTION_PARAMS = Persistent::New(NanNew("params")); -Persistent ODBCConnection::OPTION_NORESULTS = Persistent::New(NanNew("noResults")); +Persistent ODBCConnection::OPTION_SQL; +Persistent ODBCConnection::OPTION_PARAMS; +Persistent ODBCConnection::OPTION_NORESULTS; void ODBCConnection::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCConnection::Init\n"); NanScope(); + NanAssignPersistent(OPTION_SQL, NanNew("sql")); + NanAssignPersistent(OPTION_PARAMS, NanNew("params")); + NanAssignPersistent(OPTION_NORESULTS, NanNew("noResults")); + Local constructor_template = NanNew(New); // Constructor Template @@ -136,7 +140,7 @@ NAN_GETTER(ODBCConnection::ConnectTimeoutGetter) { ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); - NanReturnValue(Number::New(obj->connectTimeout)); + NanReturnValue(NanNew(obj->connectTimeout)); } NAN_SETTER(ODBCConnection::ConnectTimeoutSetter) { @@ -154,7 +158,7 @@ NAN_GETTER(ODBCConnection::LoginTimeoutGetter) { ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); - NanReturnValue(Number::New(obj->loginTimeout)); + NanReturnValue(NanNew(obj->loginTimeout)); } NAN_SETTER(ODBCConnection::LoginTimeoutSetter) { @@ -200,8 +204,8 @@ NAN_METHOD(ODBCConnection::Open) { data->connection = (char *) malloc(sizeof(char) * data->connectionLength); connection->WriteUtf8((char*) data->connection); #endif - - NanAssignPersistent(data->cb, cb); + + data->cb = new NanCallback(cb); data->conn = conn; work_req->data = data; @@ -305,23 +309,23 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { data->conn->self()->connected = true; //only uv_ref if the connection was successful -#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_ref((uv_handle_t *)&ODBC::g_async); -#else - uv_ref(uv_default_loop()); -#endif +//#if NODE_VERSION_AT_LEAST(0, 7, 9) +// uv_ref((uv_handle_t *)&ODBC::g_async); +//#else +// uv_ref(uv_default_loop()); +//#endif } TryCatch try_catch; data->conn->Unref(); - data->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); + data->cb->Call(err ? 1 : 0, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } - data->cb.Dispose(); + delete data->cb; free(data->connection); free(data); @@ -417,11 +421,11 @@ NAN_METHOD(ODBCConnection::OpenSync) { conn->self()->connected = true; //only uv_ref if the connection was successful - #if NODE_VERSION_AT_LEAST(0, 7, 9) + /*#if NODE_VERSION_AT_LEAST(0, 7, 9) uv_ref((uv_handle_t *)&ODBC::g_async); #else uv_ref(uv_default_loop()); - #endif + #endif*/ } uv_mutex_unlock(&ODBC::g_odbcMutex); @@ -454,7 +458,7 @@ NAN_METHOD(ODBCConnection::Close) { close_connection_work_data* data = (close_connection_work_data *) (calloc(1, sizeof(close_connection_work_data))); - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->conn = conn; work_req->data = data; @@ -502,23 +506,23 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { conn->connected = false; //only unref if the connection was closed -#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_unref((uv_handle_t *)&ODBC::g_async); -#else - uv_unref(uv_default_loop()); -#endif +//#if NODE_VERSION_AT_LEAST(0, 7, 9) +// uv_unref((uv_handle_t *)&ODBC::g_async); +//#else +// uv_unref(uv_default_loop()); +//#endif } TryCatch try_catch; data->conn->Unref(); - data->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); + data->cb->Call(err ? 1 : 0, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } - data->cb.Dispose(); + delete data->cb; free(data); free(req); @@ -602,7 +606,7 @@ NAN_METHOD(ODBCConnection::CreateStatement) { create_statement_work_data* data = (create_statement_work_data *) (calloc(1, sizeof(create_statement_work_data))); - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->conn = conn; work_req->data = data; @@ -671,14 +675,14 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb->Call( 2, args); if (try_catch.HasCaught()) { FatalException(try_catch); } data->conn->Unref(); - data->cb.Dispose(); + delete data->cb; free(data); free(req); @@ -748,24 +752,27 @@ NAN_METHOD(ODBCConnection::Query) { Local obj = args[0]->ToObject(); - if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { - sql = obj->Get(OPTION_SQL)->ToString(); + Local optionSqlKey = NanNew(OPTION_SQL); + if (obj->Has(optionSqlKey) && obj->Get(optionSqlKey)->IsString()) { + sql = obj->Get(optionSqlKey)->ToString(); } else { sql = NanNew(""); } - if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { + Local optionParamsKey = NanNew(OPTION_PARAMS); + if (obj->Has(optionParamsKey) && obj->Get(optionParamsKey)->IsArray()) { data->params = ODBC::GetParametersFromArray( - Local::Cast(obj->Get(OPTION_PARAMS)), + Local::Cast(obj->Get(optionParamsKey)), &data->paramCount); } else { data->paramCount = 0; } - if (obj->Has(OPTION_NORESULTS) && obj->Get(OPTION_NORESULTS)->IsBoolean()) { - data->noResultObject = obj->Get(OPTION_NORESULTS)->ToBoolean()->Value(); + Local optionNoResultsKey = NanNew(OPTION_NORESULTS); + if (obj->Has(optionParamsKey) && obj->Get(optionParamsKey)->IsBoolean()) { + data->noResultObject = obj->Get(optionParamsKey)->ToBoolean()->Value(); } else { data->noResultObject = false; @@ -780,7 +787,7 @@ NAN_METHOD(ODBCConnection::Query) { } //Done checking arguments - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->sqlLen = sql->Length(); #ifdef UNICODE @@ -893,7 +900,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { args[0] = NanNew(NanNull()); args[1] = NanNew(NanTrue()); - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb->Call(2, args); } else { Local args[4]; @@ -914,7 +921,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { } args[1] = NanNew(js_result); - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb->Call(2, args); } data->conn->Unref(); @@ -923,7 +930,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { FatalException(try_catch); } - data->cb.Dispose(); + delete data->cb; if (data->paramCount) { Parameter prm; @@ -1020,9 +1027,10 @@ NAN_METHOD(ODBCConnection::QuerySync) { Local obj = args[0]->ToObject(); - if (obj->Has(OPTION_SQL) && obj->Get(OPTION_SQL)->IsString()) { + Local optionSqlKey = NanNew(OPTION_SQL); + if (obj->Has(optionSqlKey) && obj->Get(optionSqlKey)->IsString()) { #ifdef UNICODE - sql = new String::Value(obj->Get(OPTION_SQL)->ToString()); + sql = new String::Value(obj->Get(optionSqlKey)->ToString()); #else sql = new String::Utf8Value(obj->Get(OPTION_SQL)->ToString()); #endif @@ -1034,18 +1042,20 @@ NAN_METHOD(ODBCConnection::QuerySync) { sql = new String::Utf8Value(NanNew("")); #endif } - - if (obj->Has(OPTION_PARAMS) && obj->Get(OPTION_PARAMS)->IsArray()) { + + Local optionParamsKey = NanNew(OPTION_PARAMS); + if (obj->Has(optionParamsKey) && obj->Get(optionParamsKey)->IsArray()) { params = ODBC::GetParametersFromArray( - Local::Cast(obj->Get(OPTION_PARAMS)), + Local::Cast(obj->Get(optionParamsKey)), ¶mCount); } else { paramCount = 0; } - if (obj->Has(OPTION_NORESULTS) && obj->Get(OPTION_NORESULTS)->IsBoolean()) { - noResultObject = obj->Get(OPTION_NORESULTS)->ToBoolean()->Value(); + Local optionNoResultsKey = NanNew(OPTION_NORESULTS); + if (obj->Has(optionNoResultsKey) && obj->Get(optionNoResultsKey)->IsBoolean()) { + noResultObject = obj->Get(optionNoResultsKey)->ToBoolean()->Value(); } } else { @@ -1126,7 +1136,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { (char *) "[node-odbc] Error in ODBCConnection::QuerySync" )); - NanReturnValue(NanUndefined()); + NanReturnUndefined(); } else if (noResultObject) { //if there is not result object requested then @@ -1140,15 +1150,15 @@ NAN_METHOD(ODBCConnection::QuerySync) { NanReturnValue(NanTrue()); } else { - Local args[4]; + Local result[4]; bool* canFreeHandle = new bool(true); - args[0] = NanNew(conn->m_hENV); - args[1] = NanNew(conn->m_hDBC); - args[2] = NanNew(hSTMT); - args[3] = NanNew(canFreeHandle); + result[0] = NanNew(conn->m_hENV); + result[1] = NanNew(conn->m_hDBC); + result[2] = NanNew(hSTMT); + result[3] = NanNew(canFreeHandle); - Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, args); + Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, result); NanReturnValue(js_result); } @@ -1175,8 +1185,9 @@ NAN_METHOD(ODBCConnection::Tables) { (query_work_data *) calloc(1, sizeof(query_work_data)); if (!data) { - V8::LowMemoryNotification(); - return NanThrowError("Could not allocate enough memory"); + NanLowMemoryNotification(); + NanThrowError("Could not allocate enough memory"); + NanReturnUndefined(); } data->sql = NULL; @@ -1185,7 +1196,7 @@ NAN_METHOD(ODBCConnection::Tables) { data->table = NULL; data->type = NULL; data->column = NULL; - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); if (!catalog->Equals(NanNew("null"))) { #ifdef UNICODE @@ -1285,8 +1296,9 @@ NAN_METHOD(ODBCConnection::Columns) { query_work_data* data = (query_work_data *) calloc(1, sizeof(query_work_data)); if (!data) { - V8::LowMemoryNotification(); - return NanThrowError("Could not allocate enough memory"); + NanLowMemoryNotification(); + NanThrowError("Could not allocate enough memory"); + NanReturnUndefined(); } data->sql = NULL; @@ -1295,7 +1307,7 @@ NAN_METHOD(ODBCConnection::Columns) { data->table = NULL; data->type = NULL; data->column = NULL; - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); if (!catalog->Equals(NanNew("null"))) { #ifdef UNICODE @@ -1422,11 +1434,11 @@ NAN_METHOD(ODBCConnection::BeginTransaction) { (query_work_data *) calloc(1, sizeof(query_work_data)); if (!data) { - V8::LowMemoryNotification(); + NanLowMemoryNotification(); return NanThrowError("Could not allocate enough memory"); } - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->conn = conn; work_req->data = data; @@ -1436,7 +1448,7 @@ NAN_METHOD(ODBCConnection::BeginTransaction) { UV_BeginTransaction, (uv_after_work_cb)UV_AfterBeginTransaction); - NanReturnValue(NanUndefined()); + NanReturnUndefined(); } /* @@ -1465,7 +1477,8 @@ void ODBCConnection::UV_BeginTransaction(uv_work_t* req) { void ODBCConnection::UV_AfterBeginTransaction(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterBeginTransaction\n"); NanScope(); - + + //TODO: Is this supposed to be of type query_work_data? open_connection_work_data* data = (open_connection_work_data *)(req->data); Local argv[1]; @@ -1482,13 +1495,13 @@ void ODBCConnection::UV_AfterBeginTransaction(uv_work_t* req, int status) { TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); + data->cb->Call( err ? 1 : 0, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } - data->cb.Dispose(); + delete data->cb; free(data); free(req); @@ -1577,7 +1590,7 @@ NAN_METHOD(ODBCConnection::EndTransaction) { (query_work_data *) calloc(1, sizeof(query_work_data)); if (!data) { - V8::LowMemoryNotification(); + NanLowMemoryNotification(); return NanThrowError("Could not allocate enough memory"); } @@ -1585,7 +1598,7 @@ NAN_METHOD(ODBCConnection::EndTransaction) { ? SQL_ROLLBACK : SQL_COMMIT ; - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->conn = conn; work_req->data = data; @@ -1662,13 +1675,13 @@ void ODBCConnection::UV_AfterEndTransaction(uv_work_t* req, int status) { TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); + data->cb->Call(err ? 1 : 0, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } - data->cb.Dispose(); + delete data->cb; free(data); free(req); diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 131c780e..76aa67d4 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -92,7 +92,7 @@ class ODBCConnection : public node::ObjectWrap { static NAN_METHOD(EndTransactionSync); struct Fetch_Request { - Persistent callback; + NanCallback* callback; ODBCConnection *objResult; SQLRETURN result; }; @@ -110,14 +110,14 @@ class ODBCConnection : public node::ObjectWrap { }; struct create_statement_work_data { - Persistent cb; + NanCallback* cb; ODBCConnection *conn; HSTMT hSTMT; int result; }; struct query_work_data { - Persistent cb; + NanCallback* cb; ODBCConnection *conn; HSTMT hSTMT; @@ -140,7 +140,7 @@ struct query_work_data { }; struct open_connection_work_data { - Persistent cb; + NanCallback* cb; ODBCConnection *conn; int result; int connectionLength; @@ -148,7 +148,7 @@ struct open_connection_work_data { }; struct close_connection_work_data { - Persistent cb; + NanCallback* cb; ODBCConnection *conn; int result; }; diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 4a069d95..c5cf0a3b 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -30,7 +30,7 @@ using namespace v8; using namespace node; Persistent ODBCResult::constructor; -Persistent ODBCResult::OPTION_FETCH_MODE = Persistent::New(NanNew("fetchMode")); +Persistent ODBCResult::OPTION_FETCH_MODE; void ODBCResult::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCResult::Init\n"); @@ -56,6 +56,7 @@ void ODBCResult::Init(v8::Handle exports) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "getColumnNamesSync", GetColumnNamesSync); // Properties + NanAssignPersistent(OPTION_FETCH_MODE, NanNew("fetchMode")); instance_template->SetAccessor(NanNew("fetchMode"), FetchModeGetter, FetchModeSetter); // Attach the Database Constructor to the target object @@ -178,15 +179,16 @@ NAN_METHOD(ODBCResult::Fetch) { Local 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(); + Local fetchModeKey = NanNew(OPTION_FETCH_MODE); + if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { + data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } } else { return NanThrowTypeError("ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function."); } - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->objResult = objODBCResult; work_req->data = data; @@ -252,7 +254,7 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { if (moreWork) { Handle args[2]; - args[0] = Null(); + args[0] = NanNull(); if (data->fetchMode == FETCH_ARRAY) { args[1] = ODBC::GetRecordArray( data->objResult->m_hSTMT, @@ -272,8 +274,8 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), 2, args); - data->cb.Dispose(); + data->cb->Call(2, args); + delete data->cb; if (try_catch.HasCaught()) { FatalException(try_catch); @@ -289,15 +291,15 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { args[0] = objError; } else { - args[0] = Null(); + args[0] = NanNull(); } - args[1] = Null(); + args[1] = NanNull(); TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), 2, args); - data->cb.Dispose(); + data->cb->Call(2, args); + delete data->cb; if (try_catch.HasCaught()) { FatalException(try_catch); @@ -330,8 +332,9 @@ NAN_METHOD(ODBCResult::FetchSync) { if (args.Length() == 1 && args[0]->IsObject()) { Local obj = args[0]->ToObject(); - if (obj->Has(OPTION_FETCH_MODE) && obj->Get(OPTION_FETCH_MODE)->IsInt32()) { - fetchMode = obj->Get(OPTION_FETCH_MODE)->ToInt32()->Value(); + Local fetchModeKey = NanNew(OPTION_FETCH_MODE); + if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { + fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } } @@ -425,20 +428,21 @@ NAN_METHOD(ODBCResult::FetchAll) { Local 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(); + Local fetchModeKey = NanNew(OPTION_FETCH_MODE); + if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { + data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } } else { NanThrowTypeError("ODBCResult::FetchAll(): 1 or 2 arguments are required. The last argument must be a callback function."); } - NanAssignPersistent(data->rows, Array::New()); + NanAssignPersistent(data->rows, NanNew()); data->errorCount = 0; data->count = 0; - NanAssignPersistent(data->objError, Object::New()); + NanAssignPersistent(data->objError, NanNew()); - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->objResult = objODBCResult; work_req->data = data; @@ -485,7 +489,6 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { else if (data->result == SQL_ERROR) { data->errorCount++; - //data->objError = Persistent::New(ODBC::GetSQLError( NanAssignPersistent(data->objError, ODBC::GetSQLError( SQL_HANDLE_STMT, self->m_hSTMT, @@ -499,8 +502,10 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { 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 rows = NanNew(data->rows); if (data->fetchMode == FETCH_ARRAY) { - data->rows->Set( + rows->Set( NanNew(data->count), ODBC::GetRecordArray( self->m_hSTMT, @@ -511,7 +516,7 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { ); } else { - data->rows->Set( + rows->Set( NanNew(data->count), ODBC::GetRecordTuple( self->m_hSTMT, @@ -541,17 +546,17 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { args[0] = NanNew(data->objError); } else { - args[0] = Null(); + args[0] = NanNull(); } args[1] = NanNew(data->rows); TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), 2, args); - data->cb.Dispose(); - data->rows.Dispose(); - data->objError.Dispose(); + data->cb->Call(2, args); + delete data->cb; + NanDisposePersistent(data->rows); + NanDisposePersistent(data->objError); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -575,7 +580,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { ODBCResult* self = ObjectWrap::Unwrap(args.Holder()); - Local objError = Object::New(); + Local objError = NanNew(); SQLRETURN ret; int count = 0; @@ -585,8 +590,9 @@ NAN_METHOD(ODBCResult::FetchAllSync) { if (args.Length() == 1 && args[0]->IsObject()) { Local obj = args[0]->ToObject(); - if (obj->Has(OPTION_FETCH_MODE) && obj->Get(OPTION_FETCH_MODE)->IsInt32()) { - fetchMode = obj->Get(OPTION_FETCH_MODE)->ToInt32()->Value(); + Local fetchModeKey = NanNew(OPTION_FETCH_MODE); + if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { + fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } } @@ -594,7 +600,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); } - Local rows = Array::New(); + Local rows = NanNew(); //Only loop through the recordset if there are columns if (self->colCount > 0) { @@ -723,7 +729,7 @@ NAN_METHOD(ODBCResult::GetColumnNamesSync) { ODBCResult* self = ObjectWrap::Unwrap(args.Holder()); - Local cols = Array::New(); + Local cols = NanNew(); if (self->colCount == 0) { self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); diff --git a/src/odbc_result.h b/src/odbc_result.h index 855e696d..87c489c5 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -63,7 +63,7 @@ class ODBCResult : public node::ObjectWrap { static NAN_SETTER(FetchModeSetter); struct fetch_work_data { - Persistent cb; + NanCallback* cb; ODBCResult *objResult; SQLRETURN result; diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 91f57bbb..de15a724 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -162,7 +162,7 @@ NAN_METHOD(ODBCStatement::Execute) { execute_work_data* data = (execute_work_data *) calloc(1, sizeof(execute_work_data)); - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->stmt = stmt; work_req->data = data; @@ -225,7 +225,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { TryCatch try_catch; - data->cb->Call(NanGetCurrentContext()->Global(), 2, args); + data->cb->Call(2, args); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -233,7 +233,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { } self->Unref(); - data->cb.Dispose(); + delete data->cb; free(data); free(req); @@ -295,7 +295,7 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuery) { execute_work_data* data = (execute_work_data *) calloc(1, sizeof(execute_work_data)); - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->stmt = stmt; work_req->data = data; @@ -368,7 +368,7 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { } self->Unref(); - data->cb.Dispose(); + delete data->cb; free(data); free(req); @@ -434,7 +434,7 @@ NAN_METHOD(ODBCStatement::ExecuteDirect) { execute_direct_work_data* data = (execute_direct_work_data *) calloc(1, sizeof(execute_direct_work_data)); - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->sqlLen = sql->Length(); @@ -510,7 +510,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb->Call(2, args); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -518,7 +518,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { } self->Unref(); - data->cb.Dispose(); + delete data->cb; free(data->sql); free(data); @@ -641,7 +641,7 @@ NAN_METHOD(ODBCStatement::Prepare) { prepare_work_data* data = (prepare_work_data *) calloc(1, sizeof(prepare_work_data)); - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->sqlLen = sql->Length(); @@ -717,7 +717,7 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb->Call( 2, args); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -725,7 +725,7 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { } data->stmt->Unref(); - data->cb.Dispose(); + delete data->cb; free(data->sql); free(data); @@ -883,7 +883,7 @@ NAN_METHOD(ODBCStatement::Bind) { data->stmt->m_hSTMT ); - NanAssignPersistent(data->cb, cb); + data->cb = new NanCallback(cb); data->stmt->params = ODBC::GetParametersFromArray( Local::Cast(args[0]), @@ -971,7 +971,7 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { TryCatch try_catch; - data->cb->Call(Context::GetCurrent()->Global(), 2, args); + data->cb->Call( 2, args); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -979,7 +979,7 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { } self->Unref(); - data->cb.Dispose(); + delete data->cb; free(data); free(req); diff --git a/src/odbc_statement.h b/src/odbc_statement.h index be59fc22..925dc35f 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -70,7 +70,7 @@ class ODBCStatement : public node::ObjectWrap { static NAN_METHOD(BindSync); struct Fetch_Request { - Persistent callback; + NanCallback* callback; ODBCStatement *objResult; SQLRETURN result; }; @@ -92,7 +92,7 @@ class ODBCStatement : public node::ObjectWrap { }; struct execute_direct_work_data { - Persistent cb; + NanCallback* cb; ODBCStatement *stmt; int result; void *sql; @@ -100,13 +100,13 @@ struct execute_direct_work_data { }; struct execute_work_data { - Persistent cb; + NanCallback* cb; ODBCStatement *stmt; int result; }; struct prepare_work_data { - Persistent cb; + NanCallback* cb; ODBCStatement *stmt; int result; void *sql; @@ -114,7 +114,7 @@ struct prepare_work_data { }; struct bind_work_data { - Persistent cb; + NanCallback* cb; ODBCStatement *stmt; int result; }; From deeeec1a5f7a9b0747ba732d7f996d911a5533fe Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 21 Apr 2015 21:54:12 -0400 Subject: [PATCH 428/511] Initialize long value to 0 --- src/odbc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index b8da8589..03f03546 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -366,7 +366,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, case SQL_INTEGER : case SQL_SMALLINT : case SQL_TINYINT : { - long value; + long value = 0; ret = SQLGetData( hStmt, From 030f646fa3cfb3d58ed9a792b87abd04745367d3 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 21 Apr 2015 21:58:12 -0400 Subject: [PATCH 429/511] Remove unused isolate reference --- src/odbc.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 03f03546..34fe2481 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -62,7 +62,6 @@ void ODBC::Init(v8::Handle exports) { #else #endif - v8::Isolate* isolate = v8::Isolate::GetCurrent(); PropertyAttribute constant_attributes = static_cast(ReadOnly | DontDelete); constructor_template->Set(NanNew("SQL_CLOSE"), NanNew(SQL_CLOSE), constant_attributes); constructor_template->Set(NanNew("SQL_DROP"), NanNew(SQL_DROP), constant_attributes); From da0d135c5af79850e829895392474348d64fc36b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 21 Apr 2015 22:12:54 -0400 Subject: [PATCH 430/511] Add nan@1.7.0 as a dependency --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 24a40dba..27fed5c0 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "test": "cd test && node run-tests.js" }, "dependencies": { - "bindings": "~1.0.0" + "bindings": "~1.0.0", + "nan": "^1.7.0" } } From 57800affa582c2c4b8e794e8d80218b4e1b5b40d Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 21 Apr 2015 22:16:12 -0400 Subject: [PATCH 431/511] Remove preinstall script and specyify gypfile:true in package.json --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 27fed5c0..cd0af9c3 100644 --- a/package.json +++ b/package.json @@ -34,5 +34,6 @@ "dependencies": { "bindings": "~1.0.0", "nan": "^1.7.0" - } + }, + "gypfile" : true } From 87b892cc6f32f9531cb155fc49784a965039458b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 21 Apr 2015 22:17:34 -0400 Subject: [PATCH 432/511] 1.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cd0af9c3..a585f400 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "0.6.13", + "version": "1.0.0", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From ffde830278bdfc2f353f79fcd0a2058cecd54ec7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 21 Apr 2015 22:51:49 -0400 Subject: [PATCH 433/511] Remove pre-install script --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index a585f400..00cf8dac 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "node": ">=0.8.0" }, "scripts": { - "preinstall": "node-gyp configure build", "test": "cd test && node run-tests.js" }, "dependencies": { From fe4ee6a5deca46422535cf704fe34f5b8c4a5c19 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 21 Apr 2015 22:52:04 -0400 Subject: [PATCH 434/511] 1.0.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 00cf8dac..17fe9cbc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.0.0", + "version": "1.0.1", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { @@ -34,5 +34,5 @@ "bindings": "~1.0.0", "nan": "^1.7.0" }, - "gypfile" : true + "gypfile": true } From c1cc7fb0ca546ca5a95dcecbf756e3c52338afe4 Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Tue, 21 Apr 2015 19:54:59 -0700 Subject: [PATCH 435/511] Error objects now have an errors property with list of errors. First error is assumed primary error. --- src/odbc.cpp | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 34fe2481..21658828 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -815,7 +815,6 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* DEBUG_PRINTF("ODBC::GetSQLError : handleType=%i, handle=%p\n", handleType, handle); Local objError = NanNew(); - Local str = NanNew(""); SQLINTEGER i = 0; SQLINTEGER native; @@ -838,6 +837,9 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* // Windows seems to define SQLINTEGER as long int, unixodbc as just int... %i should cover both DEBUG_PRINTF("ODBC::GetSQLError : called SQLGetDiagField; ret=%i, statusRecCount=%i\n", ret, statusRecCount); + Local errors = NanNew(); + objError->Set(NanNew("errors"), errors); + for (i = 0; i < statusRecCount; i++){ DEBUG_PRINTF("ODBC::GetSQLError : calling SQLGetDiagRec; i=%i, statusRecCount=%i\n", i, statusRecCount); @@ -855,22 +857,32 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* if (SQL_SUCCEEDED(ret)) { DEBUG_PRINTF("ODBC::GetSQLError : errorMessage=%s, errorSQLState=%s\n", errorMessage, errorSQLState); - - objError->Set(NanNew("error"), NanNew(message)); + + if (i == 0) { + // First error is assumed the primary error + objError->Set(NanNew("error"), NanNew(message)); #ifdef UNICODE - str = String::Concat(str, NanNew((uint16_t *) errorMessage)); - - //TODO: Should this also be str? - objError->SetPrototype(Exception::Error(NanNew((uint16_t *) errorMessage))); - objError->Set(NanNew("message"), str); - objError->Set(NanNew("state"), NanNew((uint16_t *) errorSQLState)); + objError->SetPrototype(Exception::Error(NanNew((uint16_t *)errorMessage))); + objError->Set(NanNew("message"), NanNew((uint16_t *)errorMessage)); + objError->Set(NanNew("state"), NanNew((uint16_t *)errorSQLState)); #else - str = String::Concat(str, NanNew(errorMessage)); + objError->SetPrototype(Exception::Error(NanNew(errorMessage))); + objError->Set(NanNew("message"), NanNew(errorMessage)); + objError->Set(NanNew("state"), NanNew(errorSQLState)); +#endif + } + + Local subError = NanNew(); - objError->SetPrototype(Exception::Error(NanNew(errorMessage))); - objError->Set(NanNew("message"), str); - objError->Set(NanNew("state"), NanNew(errorSQLState)); +#ifdef UNICODE + subError->Set(NanNew("message"), NanNew((uint16_t *)errorMessage)); + subError->Set(NanNew("state"), NanNew((uint16_t *)errorSQLState)); +#else + subError->Set(NanNew("message"), NanNew(errorMessage)); + subError->Set(NanNew("state"), NanNew(errorSQLState)); #endif + errors->Set(NanNew(i), subError); + } else if (ret == SQL_NO_DATA) { break; } From 7a3074441a2f8146bd9f08a867fd382fd2a78772 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 22 Apr 2015 10:25:15 -0400 Subject: [PATCH 436/511] 1.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 17fe9cbc..4958e3a1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.0.1", + "version": "1.1.0", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From f2b9259fe932c54d4a6eb658b9fa2b239366420c Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Fri, 24 Apr 2015 12:00:01 -0700 Subject: [PATCH 437/511] Fixed variable that was copied and pasted wrong in 1.0.0 conversion. --- src/odbc_connection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index f76aa407..2aac40b8 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -771,8 +771,8 @@ NAN_METHOD(ODBCConnection::Query) { } Local optionNoResultsKey = NanNew(OPTION_NORESULTS); - if (obj->Has(optionParamsKey) && obj->Get(optionParamsKey)->IsBoolean()) { - data->noResultObject = obj->Get(optionParamsKey)->ToBoolean()->Value(); + if (obj->Has(optionNoResultsKey) && obj->Get(optionNoResultsKey)->IsBoolean()) { + data->noResultObject = obj->Get(optionNoResultsKey)->ToBoolean()->Value(); } else { data->noResultObject = false; From f995a8d4708562cfdb4302f726908c227beeb02b Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 24 Apr 2015 15:18:41 -0400 Subject: [PATCH 438/511] 1.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4958e3a1..7832bbdc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.1.0", + "version": "1.1.1", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From ed4ccac1520abf2670015e512654cc9f4f8c19c1 Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Fri, 24 Apr 2015 15:22:33 -0700 Subject: [PATCH 439/511] Fixed some persistent leaks. Attempted fix to issue 84. Removed some old TODO comments. --- src/odbc_result.cpp | 2 -- src/odbc_statement.cpp | 15 +++++---------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index c5cf0a3b..21ed9bc6 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -502,7 +502,6 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { 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 rows = NanNew(data->rows); if (data->fetchMode == FETCH_ARRAY) { rows->Set( @@ -562,7 +561,6 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { FatalException(try_catch); } - //TODO: Do we need to free self->rows somehow? free(data); free(work_req); diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index de15a724..56c0a74a 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -216,9 +216,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { args[2] = NanNew(self->m_hSTMT); args[3] = NanNew(canFreeHandle); - // TODO is this object being cleared anywhere? Memory leak? - Persistent js_result; - NanAssignPersistent(js_result, NanNew(ODBCResult::constructor)->NewInstance(4, args)); + Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, args); args[0] = NanNew(NanNull()); args[1] = NanNew(js_result); @@ -356,11 +354,12 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { Local args[2]; args[0] = NanNew(NanNull()); + // We get a potential loss of precision here. Number isn't as big as int64. Probably fine though. args[1] = NanNew(NanNew(rowCount)); TryCatch try_catch; - data->cb->Call(NanGetCurrentContext()->Global(), 2, args); + data->cb->Call(2, args); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -501,9 +500,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { args[2] = NanNew(self->m_hSTMT); args[3] = NanNew(canFreeHandle); - //TODO persistent leak? - Persistent js_result; - NanAssignPersistent(js_result, NanNew(ODBCResult::constructor)->NewInstance(4, args)); + Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, args); args[0] = NanNew(NanNull()); args[1] = NanNew(js_result); @@ -566,9 +563,7 @@ NAN_METHOD(ODBCStatement::ExecuteDirectSync) { result[2] = NanNew(stmt->m_hSTMT); result[3] = NanNew(canFreeHandle); - //TODO persistent leak? - Persistent js_result; - NanAssignPersistent(js_result, NanNew(ODBCResult::constructor)->NewInstance(4, result)); + Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, result); NanReturnValue(js_result); } From a21732ab1bedc6870f5cbb7837af8f3e4898e0b0 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 22 Apr 2015 21:13:37 -0400 Subject: [PATCH 440/511] Nanize some things that were hidden behind the dynodbc flag --- src/odbc.cpp | 4 ++-- src/odbc.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 21658828..bd2eff61 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -974,14 +974,14 @@ NAN_METHOD(ODBC::LoadODBCLibrary) { bool result = DynLoadODBC(*js_library); - NanReturnValue((result) ? True() : False()); + NanReturnValue((result) ? NanTrue() : NanFalse()); } #endif extern "C" void init(v8::Handle exports) { #ifdef dynodbc exports->Set(NanNew("loadODBCLibrary"), - FunctionTemplate::New(ODBC::LoadODBCLibrary)->GetFunction()); + NanNew(ODBC::LoadODBCLibrary)->GetFunction()); #endif ODBC::Init(exports); diff --git a/src/odbc.h b/src/odbc.h index 8b8a101b..90014381 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -89,7 +89,7 @@ class ODBC : public node::ObjectWrap { static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message); static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); #ifdef dynodbc - static Handle LoadODBCLibrary(const Arguments& args); + static NAN_METHOD(LoadODBCLibrary); #endif static Parameter* GetParametersFromArray (Local values, int* paramCount); From c2b19a50bfee92af59c8e6ee404205c298c6dc1a Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Fri, 24 Apr 2015 21:14:45 -0700 Subject: [PATCH 441/511] Fixed a function arg we missed in conversion to nan that was only used when UNICODE was not defined. --- src/odbc_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 2aac40b8..f93456f5 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -1032,7 +1032,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { #ifdef UNICODE sql = new String::Value(obj->Get(optionSqlKey)->ToString()); #else - sql = new String::Utf8Value(obj->Get(OPTION_SQL)->ToString()); + sql = new String::Utf8Value(obj->Get(optionSqlKey)->ToString()); #endif } else { From 8d3aebae8eeeb21560804579b6efc6ac39705a64 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 1 May 2015 12:01:51 -0400 Subject: [PATCH 442/511] 1.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7832bbdc..d88f527d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.1.1", + "version": "1.1.2", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 5532856bfed3d4a98fa411e93faa478304bfe749 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 12 May 2015 12:18:02 -0400 Subject: [PATCH 443/511] Revert "Remove pre-install script" This reverts commit ffde830278bdfc2f353f79fcd0a2058cecd54ec7. --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index d88f527d..12722761 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "node": ">=0.8.0" }, "scripts": { + "preinstall": "node-gyp configure build", "test": "cd test && node run-tests.js" }, "dependencies": { From 3629df2a8f3442a100e8a500c19e33d971221fa2 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 12 May 2015 12:18:38 -0400 Subject: [PATCH 444/511] 1.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 12722761..36c2acb0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.1.2", + "version": "1.1.3", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 6458b5f6a48d1c275798e7260bedaae1cd72cdc9 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 12 May 2015 12:39:16 -0400 Subject: [PATCH 445/511] Update deps using nan@1.8.4 for iojs-v2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 36c2acb0..01e05df3 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "bindings": "~1.0.0", - "nan": "^1.7.0" + "nan": "^1.8.4" }, "gypfile": true } From cd446f393758a472629d560c9d3b2f59b1e1726c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 12 May 2015 12:39:22 -0400 Subject: [PATCH 446/511] 1.1.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01e05df3..466f07da 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.1.3", + "version": "1.1.4", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 42fc893b82ad73a8d893c1d3e56e302bbc0c093d Mon Sep 17 00:00:00 2001 From: Raphael Medaer Date: Tue, 12 May 2015 21:20:54 +0200 Subject: [PATCH 447/511] Fix install directive with 'node-gyp configure build' --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 466f07da..3bea5789 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "node": ">=0.8.0" }, "scripts": { - "preinstall": "node-gyp configure build", + "install": "node-gyp configure build", "test": "cd test && node run-tests.js" }, "dependencies": { From 18ef7a65af113cd67ce28a2744cf30a934edadc4 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 12 May 2015 15:54:31 -0400 Subject: [PATCH 448/511] 1.1.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3bea5789..acfe1bc2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.1.4", + "version": "1.1.5", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 9914fe27a56369f8d9436780ca5e97a8dcc9e948 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 13 May 2015 12:30:38 -0400 Subject: [PATCH 449/511] Add test for issue #85 --- test/common.js | 9 ++++++--- test/config.testConnectionStrings.json | 8 ++++---- test/test-issue-85.js | 25 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 test/test-issue-85.js diff --git a/test/common.js b/test/common.js index 18a60337..fb3b8d8c 100644 --- a/test/common.js +++ b/test/common.js @@ -4,6 +4,8 @@ var odbc = require("../"); //odbc.library = '/opt/sqlncli-11.0.1790.0/lib64/libsqlncli-11.0'; exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; +exports.title = "Sqlite3"; +exports.dialect = "sqlite"; if (process.argv.length === 3) { exports.connectionString = process.argv[2]; @@ -18,14 +20,14 @@ try { exports.testConnectionStrings = require('./config.testConnectionStrings.json'); } catch (e) { - exports.testConnectionStrings = [{ title : "Sqlite3", connectionString : exports.connectionString }]; + exports.testConnectionStrings = [{ title : exports.title, connectionString : exports.connectionString, dialect : exports.dialect }]; } try { exports.benchConnectionStrings = require('./config.benchConnectionStrings.json'); } catch (e) { - exports.benchConnectionStrings = [{ title : "Sqlite3", connectionString : exports.connectionString }]; + exports.benchConnectionStrings = [{ title : exports.title, connectionString : exports.connectionString, dialect : exports.dialect }]; } if (process.argv.length === 3) { @@ -35,7 +37,8 @@ if (process.argv.length === 3) { exports.testConnectionStrings.forEach(function (connectionString) { if (connectionString && connectionString.title && connectionString.title == lookup) { - exports.connectionString = connectionString.connectionString + exports.connectionString = connectionString.connectionString; + exports.dialect = connectionString.dialect; } }); } diff --git a/test/config.testConnectionStrings.json b/test/config.testConnectionStrings.json index b6c574d8..0e5b7168 100644 --- a/test/config.testConnectionStrings.json +++ b/test/config.testConnectionStrings.json @@ -1,6 +1,6 @@ [ - { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db" } - , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;SOCKET=/var/run/mysqld/mysqld.sock;USER=test;" } - , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test;AutoTranslate=yes;TEXTSIZE=10000000" } - , { "title" : "MSSQL-NativeCLI-Remote", "connectionString" : "DRIVER={SQL Server Native Client 11.0};SERVER=sql2;DATABASE=test;UID=test;PWD=test;" } + { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db", "dialect" : "sqlite" } + , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;SOCKET=/var/run/mysqld/mysqld.sock;USER=test;", "dialect" : "mysql" } + , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test;AutoTranslate=yes;TEXTSIZE=10000000", "dialect" : "mssql" } + , { "title" : "MSSQL-NativeCLI-Remote", "connectionString" : "DRIVER={SQL Server Native Client 11.0};SERVER=sql2;DATABASE=test;UID=test;PWD=test;", "dialect": "mssql" } ] diff --git a/test/test-issue-85.js b/test/test-issue-85.js new file mode 100644 index 00000000..01f4ba41 --- /dev/null +++ b/test/test-issue-85.js @@ -0,0 +1,25 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + , util = require('util') + , count = 0 + ; + +var sql = (common.dialect == 'sqlite' || common.dialect =='mysql') + ? 'select cast(-1 as signed) as test;' + : 'select cast(-1 as int) as test;' + ; + +db.open(common.connectionString, function(err) { + console.error(err || "Connected") + + if (!err) { + db.query(sql, function (err, results, more) { + assert.equal(err, null); + assert.equal(results[0].test, -1); + + db.close(function(err) { console.log(err || "Closed") }) + }) + } +}); From f98cf367c1d9fa9ca2516ab0d2dead2b80f7848a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 13 May 2015 13:02:25 -0400 Subject: [PATCH 450/511] Update test to include min and max of integer range --- test/test-issue-85.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/test-issue-85.js b/test/test-issue-85.js index 01f4ba41..3d7f499a 100644 --- a/test/test-issue-85.js +++ b/test/test-issue-85.js @@ -7,8 +7,8 @@ var common = require("./common") ; var sql = (common.dialect == 'sqlite' || common.dialect =='mysql') - ? 'select cast(-1 as signed) as test;' - : 'select cast(-1 as int) as test;' + ? 'select cast(-1 as signed) as test, cast(-2147483648 as signed) as test2, cast(2147483647 as signed) as test3;' + : 'select cast(-1 as int) as test, cast(-2147483648 as int) as test2, cast(2147483647 as int) as test3;' ; db.open(common.connectionString, function(err) { @@ -16,8 +16,12 @@ db.open(common.connectionString, function(err) { if (!err) { db.query(sql, function (err, results, more) { + console.log(results); + assert.equal(err, null); assert.equal(results[0].test, -1); + assert.equal(results[0].test2, -2147483648); + assert.equal(results[0].test3, 2147483647); db.close(function(err) { console.log(err || "Closed") }) }) From 341f200e80829f36c4ed542545d70a8395b92415 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 13 May 2015 14:45:30 -0400 Subject: [PATCH 451/511] Update test runner to properly set dialect --- test/common.js | 2 +- test/run-tests.js | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/common.js b/test/common.js index fb3b8d8c..2941e72d 100644 --- a/test/common.js +++ b/test/common.js @@ -36,7 +36,7 @@ if (process.argv.length === 3) { var lookup = process.argv[2]; exports.testConnectionStrings.forEach(function (connectionString) { - if (connectionString && connectionString.title && connectionString.title == lookup) { + if (connectionString && (connectionString.title == lookup || connectionString.connectionString == lookup)) { exports.connectionString = connectionString.connectionString; exports.dialect = connectionString.dialect; } diff --git a/test/run-tests.js b/test/run-tests.js index 9a596325..4a82a9ac 100644 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -43,7 +43,11 @@ function doTest(file, connectionString) { process.stdout.write(" ... "); testCount += 1; - + + //TODO: process the following if some flag is set + //test.stdout.pipe(process.stdout); + //test.stderr.pipe(process.stderr); + test.on("exit", function (code, signal) { clearTimeout(timer); @@ -112,4 +116,4 @@ function doNextConnectionString() { console.log("Results : All tests were successful."); } } -} \ No newline at end of file +} From 53d4a2353445325e610444dc7c5a39bb43eb3679 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 13 May 2015 15:06:24 -0400 Subject: [PATCH 452/511] GetColumnValue: modify SQL_INTEGER handling to use a int32_t and return a NanNew --- src/odbc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index bd2eff61..1e59b327 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -365,7 +365,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, case SQL_INTEGER : case SQL_SMALLINT : case SQL_TINYINT : { - long value = 0; + int32_t value = 0; ret = SQLGetData( hStmt, @@ -375,14 +375,14 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, sizeof(value), &len); - DEBUG_PRINTF("ODBC::GetColumnValue - Integer: index=%i name=%s type=%i len=%i ret=%i\n", - column.index, column.name, column.type, len, ret); + DEBUG_PRINTF("ODBC::GetColumnValue - Integer: index=%i name=%s type=%i len=%i ret=%i val=%li\n", + column.index, column.name, column.type, len, ret, value); if (len == SQL_NULL_DATA) { return NanEscapeScope(NanNull()); } else { - return NanEscapeScope(NanNew(value)); + return NanEscapeScope(NanNew(value)); } } break; From e79ed5222dce4bc4ead83f58333600a8dced7d23 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 13 May 2015 15:07:00 -0400 Subject: [PATCH 453/511] test: fix test-bad-connection-string.js for use with newer error objects --- test/test-bad-connection-string.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test-bad-connection-string.js b/test/test-bad-connection-string.js index e742a402..67b4e63d 100644 --- a/test/test-bad-connection-string.js +++ b/test/test-bad-connection-string.js @@ -17,6 +17,10 @@ db.open("this is wrong", function(err) { error: '[node-odbc] SQL_ERROR', message: '[unixODBC][Driver Manager]Data source name not found, and no default driver specified', state: 'IM002' + , errors : [{ + message: '[unixODBC][Driver Manager]Data source name not found, and no default driver specified', + state: 'IM002' + }] }); assert.equal(db.connected, false); From de66ae910d05b337adec3f3405b1c92618087545 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 13 May 2015 15:08:10 -0400 Subject: [PATCH 454/511] 1.1.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index acfe1bc2..3b5a2784 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.1.5", + "version": "1.1.6", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 0be19eb944d06bf0b00c656bc560819778f6acff Mon Sep 17 00:00:00 2001 From: Ben Zuill-Smith Date: Fri, 7 Aug 2015 17:07:54 -0700 Subject: [PATCH 455/511] Hotfix for issue where nan's latest matching version is breaking functionality --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3b5a2784..1809670c 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "bindings": "~1.0.0", - "nan": "^1.8.4" + "nan": "*1.8.4" }, "gypfile": true } From 7c89b80b0f1d75ec932f92ddbb02ebef268f989c Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 11 Aug 2015 10:45:40 -0400 Subject: [PATCH 456/511] 1.1.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1809670c..aa7529cf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.1.6", + "version": "1.1.7", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 6d4921d29f3d290267fe35a2dee3dcfca2369972 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 16 Sep 2015 13:24:05 -0400 Subject: [PATCH 457/511] Require node < 4.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aa7529cf..632fd940 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "lib": "." }, "engines": { - "node": ">=0.8.0" + "node": ">=0.8.0 <4.0.0" }, "scripts": { "install": "node-gyp configure build", From a06b168db0c5448cdebc5d8fc36318c8fdc46b1a Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 16 Sep 2015 13:27:31 -0400 Subject: [PATCH 458/511] 1.1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 632fd940..95d88cad 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.1.7", + "version": "1.1.8", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From 8d0c244feb870766469983d4e873e982b0a393f4 Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Wed, 23 Sep 2015 13:52:24 -0400 Subject: [PATCH 459/511] Initial conversion using bash search/replace script. --- .gitignore | 1 + src/odbc.cpp | 226 +++++++++++----------- src/odbc.h | 88 ++++----- src/odbc_connection.cpp | 414 ++++++++++++++++++++-------------------- src/odbc_connection.h | 22 +-- src/odbc_result.cpp | 206 ++++++++++---------- src/odbc_result.h | 14 +- src/odbc_statement.cpp | 240 +++++++++++------------ src/odbc_statement.h | 16 +- 9 files changed, 614 insertions(+), 613 deletions(-) diff --git a/.gitignore b/.gitignore index 6c270187..e17ea04b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ a.out build node_modules deps +.idea \ No newline at end of file diff --git a/src/odbc.cpp b/src/odbc.cpp index 1e59b327..dee2ab8d 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -41,16 +41,16 @@ using namespace node; uv_mutex_t ODBC::g_odbcMutex; uv_async_t ODBC::g_async; -Persistent ODBC::constructor; +Nan::Persistent ODBC::constructor; void ODBC::Init(v8::Handle exports) { DEBUG_PRINTF("ODBC::Init\n"); - NanScope(); + Nan::HandleScope scope; - Local constructor_template = NanNew(New); + Local constructor_template = Nan::New(New); // Constructor Template - constructor_template->SetClassName(NanNew("ODBC")); + constructor_template->SetClassName(Nan::New("ODBC").ToLocalChecked()); // Reserve space for one Handle Local instance_template = constructor_template->InstanceTemplate(); @@ -63,21 +63,21 @@ void ODBC::Init(v8::Handle exports) { #endif PropertyAttribute constant_attributes = static_cast(ReadOnly | DontDelete); - constructor_template->Set(NanNew("SQL_CLOSE"), NanNew(SQL_CLOSE), constant_attributes); - constructor_template->Set(NanNew("SQL_DROP"), NanNew(SQL_DROP), constant_attributes); - constructor_template->Set(NanNew("SQL_UNBIND"), NanNew(SQL_UNBIND), constant_attributes); - constructor_template->Set(NanNew("SQL_RESET_PARAMS"), NanNew(SQL_RESET_PARAMS), constant_attributes); - constructor_template->Set(NanNew("SQL_DESTROY"), NanNew(SQL_DESTROY), constant_attributes); - constructor_template->Set(NanNew("FETCH_ARRAY"), NanNew(FETCH_ARRAY), constant_attributes); + constructor_template->Set(Nan::New("SQL_CLOSE").ToLocalChecked(), Nan::New(SQL_CLOSE), constant_attributes); + constructor_template->Set(Nan::New("SQL_DROP").ToLocalChecked(), Nan::New(SQL_DROP), constant_attributes); + constructor_template->Set(Nan::New("SQL_UNBIND").ToLocalChecked(), Nan::New(SQL_UNBIND), constant_attributes); + constructor_template->Set(Nan::New("SQL_RESET_PARAMS").ToLocalChecked(), Nan::New(SQL_RESET_PARAMS), constant_attributes); + constructor_template->Set(Nan::New("SQL_DESTROY").ToLocalChecked(), Nan::New(SQL_DESTROY), constant_attributes); + constructor_template->Set(Nan::New("FETCH_ARRAY").ToLocalChecked(), Nan::New(FETCH_ARRAY), constant_attributes); NODE_ODBC_DEFINE_CONSTANT(constructor_template, FETCH_OBJECT); // Prototype Methods - NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnection", CreateConnection); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "createConnectionSync", CreateConnectionSync); + Nan::SetPrototypeMethod(constructor_template, "createConnection", CreateConnection); + Nan::SetPrototypeMethod(constructor_template, "createConnectionSync", CreateConnectionSync); // Attach the Database Constructor to the target object - NanAssignPersistent(constructor, constructor_template->GetFunction()); - exports->Set(NanNew("ODBC"), + constructor.Reset(constructor_template->GetFunction()); + exports->Set(Nan::New("ODBC").ToLocalChecked(), constructor_template->GetFunction()); #if NODE_VERSION_AT_LEAST(0, 7, 9) @@ -121,10 +121,10 @@ void ODBC::Free() { NAN_METHOD(ODBC::New) { DEBUG_PRINTF("ODBC::New\n"); - NanScope(); + Nan::HandleScope scope; ODBC* dbo = new ODBC(); - dbo->Wrap(args.Holder()); + dbo->Wrap(info.Holder()); dbo->m_hEnv = NULL; @@ -140,13 +140,13 @@ NAN_METHOD(ODBC::New) { Local objError = ODBC::GetSQLError(SQL_HANDLE_ENV, dbo->m_hEnv); - return NanThrowError(objError); + return Nan::ThrowError(objError); } // Use ODBC 3.x behavior SQLSetEnvAttr(dbo->m_hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER); - NanReturnValue(args.Holder()); + info.GetReturnValue().Set(info.Holder()); } //void ODBC::WatcherCallback(uv_async_t *w, int revents) { @@ -160,13 +160,13 @@ NAN_METHOD(ODBC::New) { NAN_METHOD(ODBC::CreateConnection) { DEBUG_PRINTF("ODBC::CreateConnection\n"); - NanScope(); + Nan::HandleScope scope; - Local cb = args[0].As(); - NanCallback *callback = new NanCallback(cb); + Local cb = info[0].As(); + Nan::Callback *callback = new Nan::Callback(cb); //REQ_FUN_ARG(0, cb); - ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); + ODBC* dbo = Nan::ObjectWrap::Unwrap(info.Holder()); //initialize work request uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -184,7 +184,7 @@ NAN_METHOD(ODBC::CreateConnection) { dbo->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBC::UV_CreateConnection(uv_work_t* req) { @@ -203,30 +203,30 @@ void ODBC::UV_CreateConnection(uv_work_t* req) { void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { DEBUG_PRINTF("ODBC::UV_AfterCreateConnection\n"); - NanScope(); + Nan::HandleScope scope; create_connection_work_data* data = (create_connection_work_data *)(req->data); TryCatch try_catch; if (!SQL_SUCCEEDED(data->result)) { - Local args[1]; + Local info[1]; - args[0] = ODBC::GetSQLError(SQL_HANDLE_ENV, data->dbo->m_hEnv); + info[0] = ODBC::GetSQLError(SQL_HANDLE_ENV, data->dbo->m_hEnv); - data->cb->Call(1, args); + data->cb->Call(1, info); } else { - Local args[2]; - args[0] = NanNew(data->dbo->m_hEnv); - args[1] = NanNew(data->hDBC); + Local info[2]; + info[0] = Nan::New(data->dbo->m_hEnv); + info[1] = Nan::New(data->hDBC); - Local js_result = NanNew(ODBCConnection::constructor)->NewInstance(2, args); + Local js_result = Nan::New(ODBCConnection::constructor)->NewInstance(2, info); - args[0] = NanNew(NanNull()); - args[1] = NanNew(js_result); + info[0] = Nan::New(Nan::Null()); + info[1] = Nan::New(js_result); - data->cb->Call(2, args); + data->cb->Call(2, info); } if (try_catch.HasCaught()) { @@ -247,9 +247,9 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { NAN_METHOD(ODBC::CreateConnectionSync) { DEBUG_PRINTF("ODBC::CreateConnectionSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBC* dbo = ObjectWrap::Unwrap(args.Holder()); + ODBC* dbo = Nan::ObjectWrap::Unwrap(info.Holder()); HDBC hDBC; @@ -265,12 +265,12 @@ NAN_METHOD(ODBC::CreateConnectionSync) { uv_mutex_unlock(&ODBC::g_odbcMutex); Local params[2]; - params[0] = NanNew(dbo->m_hEnv); - params[1] = NanNew(hDBC); + params[0] = Nan::New(dbo->m_hEnv); + params[1] = Nan::New(hDBC); - Local js_result = NanNew(ODBCConnection::constructor)->NewInstance(2, params); + Local js_result = Nan::New(ODBCConnection::constructor)->NewInstance(2, params); - NanReturnValue(js_result); + info.GetReturnValue().Set(js_result); } /* @@ -351,7 +351,7 @@ void ODBC::FreeColumns(Column* columns, short* colCount) { Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength) { - NanEscapableScope(); + Nan::EscapableHandleScope scope; SQLLEN len = 0; //reset the buffer @@ -379,10 +379,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret, value); if (len == SQL_NULL_DATA) { - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); } else { - return NanEscapeScope(NanNew(value)); + return scope.Escape(Nan::New(value)); } } break; @@ -406,11 +406,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len, ret, value); if (len == SQL_NULL_DATA) { - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); //return Null(); } else { - return NanEscapeScope(NanNew(value)); + return scope.Escape(Nan::New(value)); //return Number::New(value); } } @@ -434,7 +434,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); //return Null(); } else { @@ -444,11 +444,11 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //at the specified time. timeInfo.tm_isdst = -1; - //return NanEscapeScope(Date::New(Isolate::GetCurrent(), (double(mktime(&timeInfo)) * 1000)); - return NanEscapeScope(NanNew(double(mktime(&timeInfo)) * 1000)); + //return scope.Escape(Date::New(Isolate::GetCurrent(), (double(mktime(&timeInfo)) * 1000)); + return scope.Escape(Nan::New(double(mktime(&timeInfo)) * 1000)); } else { - return NanEscapeScope(NanNew((char *)buffer)); + return scope.Escape(Nan::New((char *)buffer)); } } #else @@ -480,7 +480,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); //return Null(); } else { @@ -496,10 +496,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //at the specified time. timeInfo.tm_isdst = -1; #ifdef TIMEGM - return NanEscapeScope(NanNew((double(timegm(&timeInfo)) * 1000) + return scope.Escape(Nan::New((double(timegm(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000))); #else - return NanEscapeScope(NanNew((double(timelocal(&timeInfo)) * 1000) + return scope.Escape(Nan::New((double(timelocal(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000))); #endif //return Date::New((double(timegm(&timeInfo)) * 1000) @@ -522,10 +522,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); } else { - return NanEscapeScope(NanNew((*buffer == '0') ? false : true)); + return scope.Escape(Nan::New((*buffer == '0') ? false : true)); } default : Local str; @@ -544,7 +544,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); if (len == SQL_NULL_DATA && str.IsEmpty()) { - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); //return Null(); } @@ -552,7 +552,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //we have captured all of the data //double check that we have some data else return null if (str.IsEmpty()){ - return NanEscapeScope(NanNull()); + return scope.Escape(Nan::Null()); } break; @@ -563,17 +563,17 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, if (count == 0) { //no concatenation required, this is our first pass #ifdef UNICODE - str = NanNew((uint16_t*) buffer); + str = Nan::New((uint16_t*) buffer); #else - str = NanNew((char *) buffer); + str = Nan::New((char *) buffer); #endif } else { //we need to concatenate #ifdef UNICODE - str = String::Concat(str, NanNew((uint16_t*) buffer)); + str = String::Concat(str, Nan::New((uint16_t*) buffer)); #else - str = String::Concat(str, NanNew((char *) buffer)); + str = String::Concat(str, Nan::New((char *) buffer)); #endif } @@ -599,17 +599,17 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //Not sure if throwing here will work out well for us but we can try //since we should have a valid handle and the error is something we //can look into - NanThrowError(ODBC::GetSQLError( + Nan::ThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, hStmt, (char *) "[node-odbc] Error in ODBC::GetColumnValue" )); - return NanEscapeScope(NanUndefined()); + return scope.Escape(Nan::Undefined()); break; } } while (true); - return NanEscapeScope(str); + return scope.Escape(str); } } @@ -620,21 +620,21 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { - NanEscapableScope(); + Nan::EscapableHandleScope scope; - Local tuple = NanNew(); + Local tuple = Nan::New(); for(int i = 0; i < *colCount; i++) { #ifdef UNICODE - tuple->Set( NanNew((uint16_t *) columns[i].name), + tuple->Set( Nan::New((uint16_t *) columns[i].name), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); #else - tuple->Set( NanNew((const char *) columns[i].name), + tuple->Set( Nan::New((const char *) columns[i].name), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); #endif } - return NanEscapeScope(tuple); + return scope.Escape(tuple); } /* @@ -644,16 +644,16 @@ Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { - NanEscapableScope(); + Nan::EscapableHandleScope scope; - Local array = NanNew(); + Local array = Nan::New(); for(int i = 0; i < *colCount; i++) { - array->Set( NanNew(i), + array->Set( Nan::New(i), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); } - return NanEscapeScope(array); + return scope.Escape(array); } /* @@ -767,10 +767,10 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, - NanCallback* cb) { - NanEscapableScope(); + Nan::Callback* cb) { + Nan::EscapableHandleScope scope; - return NanEscapeScope(CallbackSQLError( + return scope.Escape(CallbackSQLError( handleType, handle, (char *) "[node-odbc] SQL_ERROR", @@ -780,8 +780,8 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, - NanCallback* cb) { - NanEscapableScope(); + Nan::Callback* cb) { + Nan::EscapableHandleScope scope; Local objError = ODBC::GetSQLError( handleType, @@ -789,11 +789,11 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, message ); - Local args[1]; - args[0] = objError; - cb->Call(1, args); + Local info[1]; + info[0] = objError; + cb->Call(1, info); - return NanEscapeScope(NanUndefined()); + return scope.Escape(Nan::Undefined()); } /* @@ -801,20 +801,20 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, */ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle) { - NanEscapableScope(); + Nan::EscapableHandleScope scope; - return NanEscapeScope(GetSQLError( + return scope.Escape(GetSQLError( handleType, handle, (char *) "[node-odbc] SQL_ERROR")); } Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message) { - NanEscapableScope(); + Nan::EscapableHandleScope scope; DEBUG_PRINTF("ODBC::GetSQLError : handleType=%i, handle=%p\n", handleType, handle); - Local objError = NanNew(); + Local objError = Nan::New(); SQLINTEGER i = 0; SQLINTEGER native; @@ -837,8 +837,8 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* // Windows seems to define SQLINTEGER as long int, unixodbc as just int... %i should cover both DEBUG_PRINTF("ODBC::GetSQLError : called SQLGetDiagField; ret=%i, statusRecCount=%i\n", ret, statusRecCount); - Local errors = NanNew(); - objError->Set(NanNew("errors"), errors); + Local errors = Nan::New(); + objError->Set(Nan::New("errors").ToLocalChecked(), errors); for (i = 0; i < statusRecCount; i++){ DEBUG_PRINTF("ODBC::GetSQLError : calling SQLGetDiagRec; i=%i, statusRecCount=%i\n", i, statusRecCount); @@ -860,28 +860,28 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* if (i == 0) { // First error is assumed the primary error - objError->Set(NanNew("error"), NanNew(message)); + objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message)); #ifdef UNICODE - objError->SetPrototype(Exception::Error(NanNew((uint16_t *)errorMessage))); - objError->Set(NanNew("message"), NanNew((uint16_t *)errorMessage)); - objError->Set(NanNew("state"), NanNew((uint16_t *)errorSQLState)); + objError->SetPrototype(Exception::Error(Nan::New((uint16_t *)errorMessage))); + objError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage)); + objError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState)); #else - objError->SetPrototype(Exception::Error(NanNew(errorMessage))); - objError->Set(NanNew("message"), NanNew(errorMessage)); - objError->Set(NanNew("state"), NanNew(errorSQLState)); + objError->SetPrototype(Exception::Error(Nan::New(errorMessage))); + objError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage)); + objError->Set(Nan::New("state").ToLocalChecked(), Nan::New(errorSQLState)); #endif } - Local subError = NanNew(); + Local subError = Nan::New(); #ifdef UNICODE - subError->Set(NanNew("message"), NanNew((uint16_t *)errorMessage)); - subError->Set(NanNew("state"), NanNew((uint16_t *)errorSQLState)); + subError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage)); + subError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState)); #else - subError->Set(NanNew("message"), NanNew(errorMessage)); - subError->Set(NanNew("state"), NanNew(errorSQLState)); + subError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage)); + subError->Set(Nan::New("state").ToLocalChecked(), Nan::New(errorSQLState)); #endif - errors->Set(NanNew(i), subError); + errors->Set(Nan::New(i), subError); } else if (ret == SQL_NO_DATA) { break; @@ -890,13 +890,13 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* if (statusRecCount == 0) { //Create a default error object if there were no diag records - objError->Set(NanNew("error"), NanNew(message)); - objError->SetPrototype(Exception::Error(NanNew(message))); - objError->Set(NanNew("message"), NanNew( + objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message)); + objError->SetPrototype(Exception::Error(Nan::New(message))); + objError->Set(Nan::New("message").ToLocalChecked(), Nan::New( (const char *) "[node-odbc] An error occurred but no diagnostic information was available.")); } - return NanEscapeScope(objError); + return scope.Escape(objError); } /* @@ -910,9 +910,9 @@ Local ODBC::GetAllRecordsSync (HENV hENV, int bufferLength) { DEBUG_PRINTF("ODBC::GetAllRecordsSync\n"); - NanEscapableScope(); + Nan::EscapableHandleScope scope; - Local objError = NanNew(); + Local objError = Nan::New(); int count = 0; int errorCount = 0; @@ -920,7 +920,7 @@ Local ODBC::GetAllRecordsSync (HENV hENV, Column* columns = GetColumns(hSTMT, &colCount); - Local rows = NanNew(); + Local rows = Nan::New(); //loop through all records while (true) { @@ -950,7 +950,7 @@ Local ODBC::GetAllRecordsSync (HENV hENV, } rows->Set( - NanNew(count), + Nan::New(count), ODBC::GetRecordTuple( hSTMT, columns, @@ -963,25 +963,25 @@ Local ODBC::GetAllRecordsSync (HENV hENV, } //TODO: what do we do about errors!?! //we throw them - return NanEscapeScope(rows); + return scope.Escape(rows); } #ifdef dynodbc NAN_METHOD(ODBC::LoadODBCLibrary) { - NanScope(); + Nan::HandleScope scope; REQ_STR_ARG(0, js_library); bool result = DynLoadODBC(*js_library); - NanReturnValue((result) ? NanTrue() : NanFalse()); + info.GetReturnValue().Set((result) ? Nan::True() : Nan::False()); } #endif extern "C" void init(v8::Handle exports) { #ifdef dynodbc - exports->Set(NanNew("loadODBCLibrary"), - NanNew(ODBC::LoadODBCLibrary)->GetFunction()); + exports->Set(Nan::New("loadODBCLibrary").ToLocalChecked(), + Nan::New(ODBC::LoadODBCLibrary)->GetFunction()); #endif ODBC::Init(exports); diff --git a/src/odbc.h b/src/odbc.h index 90014381..b631e82f 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -71,9 +71,9 @@ typedef struct { SQLLEN StrLen_or_IndPtr; } Parameter; -class ODBC : public node::ObjectWrap { +class ODBC : public Nan::ObjectWrap { public: - static Persistent constructor; + static Nan::Persistent constructor; static uv_mutex_t g_odbcMutex; static uv_async_t g_async; @@ -83,8 +83,8 @@ class ODBC : public node::ObjectWrap { static Handle GetColumnValue(SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength); static Local GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); - static Handle CallbackSQLError(SQLSMALLINT handleType, SQLHANDLE handle, NanCallback* cb); - static Handle CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, NanCallback* cb); + static Handle CallbackSQLError(SQLSMALLINT handleType, SQLHANDLE handle, Nan::Callback* cb); + static Handle CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, Nan::Callback* cb); static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle); static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message); static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); @@ -119,27 +119,27 @@ class ODBC : public node::ObjectWrap { }; struct create_connection_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBC *dbo; HDBC hDBC; int result; }; struct open_request { - Persistent cb; + Nan::Persistent cb; ODBC *dbo; int result; char connection[1]; }; struct close_request { - Persistent cb; + Nan::Persistent cb; ODBC *dbo; int result; }; struct query_request { - Persistent cb; + Nan::Persistent cb; ODBC *dbo; HSTMT hSTMT; int affectedRows; @@ -174,77 +174,77 @@ struct query_request { #endif #define REQ_ARGS(N) \ - if (args.Length() < (N)) \ - return NanThrowTypeError("Expected " #N "arguments"); + if (info.Length() < (N)) \ + return Nan::ThrowTypeError("Expected " #N "arguments"); //Require String Argument; Save String as Utf8 #define REQ_STR_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsString()) \ - return NanThrowTypeError("Argument " #I " must be a string"); \ - String::Utf8Value VAR(args[I]->ToString()); + if (info.Length() <= (I) || !info[I]->IsString()) \ + return Nan::ThrowTypeError("Argument " #I " must be a string"); \ + String::Utf8Value VAR(info[I]->ToString()); //Require String Argument; Save String as Wide String (UCS2) #define REQ_WSTR_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsString()) \ - return NanThrowTypeError("Argument " #I " must be a string"); \ - String::Value VAR(args[I]->ToString()); + if (info.Length() <= (I) || !info[I]->IsString()) \ + return Nan::ThrowTypeError("Argument " #I " must be a string"); \ + String::Value VAR(info[I]->ToString()); //Require String Argument; Save String as Object #define REQ_STRO_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsString()) \ - return NanThrowTypeError("Argument " #I " must be a string"); \ - Local VAR(args[I]->ToString()); + if (info.Length() <= (I) || !info[I]->IsString()) \ + return Nan::ThrowTypeError("Argument " #I " must be a string"); \ + Local VAR(info[I]->ToString()); //Require String or Null Argument; Save String as Utf8 #define REQ_STR_OR_NULL_ARG(I, VAR) \ - if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ - return NanThrowTypeError("Argument " #I " must be a string or null"); \ - String::Utf8Value VAR(args[I]->ToString()); + if ( info.Length() <= (I) || (!info[I]->IsString() && !info[I]->IsNull()) ) \ + return Nan::ThrowTypeError("Argument " #I " must be a string or null"); \ + String::Utf8Value VAR(info[I]->ToString()); //Require String or Null Argument; Save String as Wide String (UCS2) #define REQ_WSTR_OR_NULL_ARG(I, VAR) \ - if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) \ - return NanThrowTypeError("Argument " #I " must be a string or null"); \ - String::Value VAR(args[I]->ToString()); + if ( info.Length() <= (I) || (!info[I]->IsString() && !info[I]->IsNull()) ) \ + return Nan::ThrowTypeError("Argument " #I " must be a string or null"); \ + String::Value VAR(info[I]->ToString()); //Require String or Null Argument; save String as String Object #define REQ_STRO_OR_NULL_ARG(I, VAR) \ - if ( args.Length() <= (I) || (!args[I]->IsString() && !args[I]->IsNull()) ) { \ - NanThrowTypeError("Argument " #I " must be a string or null"); \ - NanReturnUndefined(); \ + if ( info.Length() <= (I) || (!info[I]->IsString() && !info[I]->IsNull()) ) { \ + Nan::ThrowTypeError("Argument " #I " must be a string or null"); \ + return; \ } \ - Local VAR(args[I]->ToString()); + Local VAR(info[I]->ToString()); #define REQ_FUN_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsFunction()) \ - return NanThrowTypeError("Argument " #I " must be a function"); \ - Local VAR = Local::Cast(args[I]); + if (info.Length() <= (I) || !info[I]->IsFunction()) \ + return Nan::ThrowTypeError("Argument " #I " must be a function"); \ + Local VAR = Local::Cast(info[I]); #define REQ_BOOL_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsBoolean()) \ - return NanThrowTypeError("Argument " #I " must be a boolean"); \ - Local VAR = (args[I]->ToBoolean()); + if (info.Length() <= (I) || !info[I]->IsBoolean()) \ + return Nan::ThrowTypeError("Argument " #I " must be a boolean"); \ + Local VAR = (info[I]->ToBoolean()); #define REQ_EXT_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsExternal()) \ - return NanThrowTypeError("Argument " #I " invalid"); \ - Local VAR = Local::Cast(args[I]); + if (info.Length() <= (I) || !info[I]->IsExternal()) \ + return Nan::ThrowTypeError("Argument " #I " invalid"); \ + Local VAR = Local::Cast(info[I]); #define OPT_INT_ARG(I, VAR, DEFAULT) \ int VAR; \ - if (args.Length() <= (I)) { \ + if (info.Length() <= (I)) { \ VAR = (DEFAULT); \ - } else if (args[I]->IsInt32()) { \ - VAR = args[I]->Int32Value(); \ + } else if (info[I]->IsInt32()) { \ + VAR = info[I]->Int32Value(); \ } else { \ - return NanThrowTypeError("Argument " #I " must be an integer"); \ + return Nan::ThrowTypeError("Argument " #I " must be an integer"); \ } // From node v10 NODE_DEFINE_CONSTANT #define NODE_ODBC_DEFINE_CONSTANT(constructor_template, constant) \ - (constructor_template)->Set(NanNew(#constant), \ - NanNew(constant), \ + (constructor_template)->Set(Nan::New(#constant), \ + Nan::New(constant), \ static_cast(v8::ReadOnly|v8::DontDelete)) #endif diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index f93456f5..8ab8fe3c 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -30,55 +30,55 @@ using namespace v8; using namespace node; -Persistent ODBCConnection::constructor; -Persistent ODBCConnection::OPTION_SQL; -Persistent ODBCConnection::OPTION_PARAMS; -Persistent ODBCConnection::OPTION_NORESULTS; +Nan::Persistent ODBCConnection::constructor; +Nan::Persistent ODBCConnection::OPTION_SQL; +Nan::Persistent ODBCConnection::OPTION_PARAMS; +Nan::Persistent ODBCConnection::OPTION_NORESULTS; void ODBCConnection::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCConnection::Init\n"); - NanScope(); + Nan::HandleScope scope; - NanAssignPersistent(OPTION_SQL, NanNew("sql")); - NanAssignPersistent(OPTION_PARAMS, NanNew("params")); - NanAssignPersistent(OPTION_NORESULTS, NanNew("noResults")); + OPTION_SQL.Reset(Nan::New("sql").ToLocalChecked()); + OPTION_PARAMS.Reset(Nan::New("params").ToLocalChecked()); + OPTION_NORESULTS.Reset(Nan::New("noResults").ToLocalChecked()); - Local constructor_template = NanNew(New); + Local constructor_template = Nan::New(New); // Constructor Template - constructor_template->SetClassName(NanNew("ODBCConnection")); + constructor_template->SetClassName(Nan::New("ODBCConnection").ToLocalChecked()); // Reserve space for one Handle Local instance_template = constructor_template->InstanceTemplate(); instance_template->SetInternalFieldCount(1); // Properties - //instance_template->SetAccessor(NanNew("mode"), ModeGetter, ModeSetter); - instance_template->SetAccessor(NanNew("connected"), ConnectedGetter); - instance_template->SetAccessor(NanNew("connectTimeout"), ConnectTimeoutGetter, ConnectTimeoutSetter); - instance_template->SetAccessor(NanNew("loginTimeout"), LoginTimeoutGetter, LoginTimeoutSetter); + //Nan::SetAccessor(instance_template, Nan::New("mode").ToLocalChecked(), ModeGetter, ModeSetter); + Nan::SetAccessor(instance_template, Nan::New("connected").ToLocalChecked(), ConnectedGetter); + Nan::SetAccessor(instance_template, Nan::New("connectTimeout").ToLocalChecked(), ConnectTimeoutGetter, ConnectTimeoutSetter); + Nan::SetAccessor(instance_template, Nan::New("loginTimeout").ToLocalChecked(), LoginTimeoutGetter, LoginTimeoutSetter); // Prototype Methods - NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "openSync", OpenSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatement", CreateStatement); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "createStatementSync", CreateStatementSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", Query); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "querySync", QuerySync); - - NODE_SET_PROTOTYPE_METHOD(constructor_template, "beginTransaction", BeginTransaction); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "beginTransactionSync", BeginTransactionSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "endTransaction", EndTransaction); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "endTransactionSync", EndTransactionSync); - - NODE_SET_PROTOTYPE_METHOD(constructor_template, "columns", Columns); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "tables", Tables); + Nan::SetPrototypeMethod(constructor_template, "open", Open); + Nan::SetPrototypeMethod(constructor_template, "openSync", OpenSync); + Nan::SetPrototypeMethod(constructor_template, "close", Close); + Nan::SetPrototypeMethod(constructor_template, "closeSync", CloseSync); + Nan::SetPrototypeMethod(constructor_template, "createStatement", CreateStatement); + Nan::SetPrototypeMethod(constructor_template, "createStatementSync", CreateStatementSync); + Nan::SetPrototypeMethod(constructor_template, "query", Query); + Nan::SetPrototypeMethod(constructor_template, "querySync", QuerySync); + + Nan::SetPrototypeMethod(constructor_template, "beginTransaction", BeginTransaction); + Nan::SetPrototypeMethod(constructor_template, "beginTransactionSync", BeginTransactionSync); + Nan::SetPrototypeMethod(constructor_template, "endTransaction", EndTransaction); + Nan::SetPrototypeMethod(constructor_template, "endTransactionSync", EndTransactionSync); + + Nan::SetPrototypeMethod(constructor_template, "columns", Columns); + Nan::SetPrototypeMethod(constructor_template, "tables", Tables); // Attach the Database Constructor to the target object - NanAssignPersistent(constructor, constructor_template->GetFunction()); - exports->Set( NanNew("ODBCConnection"), constructor_template->GetFunction()); + constructor.Reset(constructor_template->GetFunction()); + exports->Set( Nan::New("ODBCConnection").ToLocalChecked(), constructor_template->GetFunction()); } ODBCConnection::~ODBCConnection() { @@ -107,7 +107,7 @@ void ODBCConnection::Free() { NAN_METHOD(ODBCConnection::New) { DEBUG_PRINTF("ODBCConnection::New\n"); - NanScope(); + Nan::HandleScope scope; REQ_EXT_ARG(0, js_henv); REQ_EXT_ARG(1, js_hdbc); @@ -117,36 +117,36 @@ NAN_METHOD(ODBCConnection::New) { ODBCConnection* conn = new ODBCConnection(hENV, hDBC); - conn->Wrap(args.Holder()); + conn->Wrap(info.Holder()); //set default connectTimeout to 0 seconds conn->connectTimeout = 0; //set default loginTimeout to 5 seconds conn->loginTimeout = 5; - NanReturnValue(args.Holder()); + info.GetReturnValue().Set(info.Holder()); } NAN_GETTER(ODBCConnection::ConnectedGetter) { - NanScope(); + Nan::HandleScope scope; - ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection *obj = Nan::ObjectWrap::Unwrap(info.Holder()); - NanReturnValue(obj->connected ? NanTrue() : NanFalse()); + info.GetReturnValue().Set(obj->connected ? Nan::True() : Nan::False()); } NAN_GETTER(ODBCConnection::ConnectTimeoutGetter) { - NanScope(); + Nan::HandleScope scope; - ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection *obj = Nan::ObjectWrap::Unwrap(info.Holder()); - NanReturnValue(NanNew(obj->connectTimeout)); + info.GetReturnValue().Set(Nan::New(obj->connectTimeout)); } NAN_SETTER(ODBCConnection::ConnectTimeoutSetter) { - NanScope(); + Nan::HandleScope scope; - ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection *obj = Nan::ObjectWrap::Unwrap(info.Holder()); if (value->IsNumber()) { obj->connectTimeout = value->Uint32Value(); @@ -154,17 +154,17 @@ NAN_SETTER(ODBCConnection::ConnectTimeoutSetter) { } NAN_GETTER(ODBCConnection::LoginTimeoutGetter) { - NanScope(); + Nan::HandleScope scope; - ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection *obj = Nan::ObjectWrap::Unwrap(info.Holder()); - NanReturnValue(NanNew(obj->loginTimeout)); + info.GetReturnValue().Set(Nan::New(obj->loginTimeout)); } NAN_SETTER(ODBCConnection::LoginTimeoutSetter) { - NanScope(); + Nan::HandleScope scope; - ODBCConnection *obj = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection *obj = Nan::ObjectWrap::Unwrap(info.Holder()); if (value->IsNumber()) { obj->loginTimeout = value->Uint32Value(); @@ -176,16 +176,16 @@ NAN_SETTER(ODBCConnection::LoginTimeoutSetter) { * */ -//Handle ODBCConnection::Open(const Arguments& args) { +//Handle ODBCConnection::Open(const Arguments& info) { NAN_METHOD(ODBCConnection::Open) { DEBUG_PRINTF("ODBCConnection::Open\n"); - NanScope(); + Nan::HandleScope scope; REQ_STRO_ARG(0, connection); REQ_FUN_ARG(1, cb); //get reference to the connection object - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); //create a uv work request uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -205,7 +205,7 @@ NAN_METHOD(ODBCConnection::Open) { connection->WriteUtf8((char*) data->connection); #endif - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->conn = conn; work_req->data = data; @@ -218,7 +218,7 @@ NAN_METHOD(ODBCConnection::Open) { conn->Ref(); - NanReturnValue(args.Holder()); + info.GetReturnValue().Set(info.Holder()); } void ODBCConnection::UV_Open(uv_work_t* req) { @@ -289,7 +289,7 @@ void ODBCConnection::UV_Open(uv_work_t* req) { void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterOpen\n"); - NanScope(); + Nan::HandleScope scope; open_connection_work_data* data = (open_connection_work_data *)(req->data); @@ -338,12 +338,12 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { NAN_METHOD(ODBCConnection::OpenSync) { DEBUG_PRINTF("ODBCConnection::OpenSync\n"); - NanScope(); + Nan::HandleScope scope; REQ_STRO_ARG(0, connection); //get reference to the connection object - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); DEBUG_PRINTF("ODBCConnection::OpenSync : connectTimeout=%i, loginTimeout = %i\n", *&(conn->connectTimeout), *&(conn->loginTimeout)); @@ -433,10 +433,10 @@ NAN_METHOD(ODBCConnection::OpenSync) { free(connectionString); if (err) { - return NanThrowError(objError); + return Nan::ThrowError(objError); } else { - NanReturnValue(NanTrue()); + info.GetReturnValue().Set(Nan::True()); } } @@ -447,18 +447,18 @@ NAN_METHOD(ODBCConnection::OpenSync) { NAN_METHOD(ODBCConnection::Close) { DEBUG_PRINTF("ODBCConnection::Close\n"); - NanScope(); + Nan::HandleScope scope; REQ_FUN_ARG(0, cb); - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); close_connection_work_data* data = (close_connection_work_data *) (calloc(1, sizeof(close_connection_work_data))); - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->conn = conn; work_req->data = data; @@ -471,7 +471,7 @@ NAN_METHOD(ODBCConnection::Close) { conn->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCConnection::UV_Close(uv_work_t* req) { @@ -489,7 +489,7 @@ void ODBCConnection::UV_Close(uv_work_t* req) { void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterClose\n"); - NanScope(); + Nan::HandleScope scope; close_connection_work_data* data = (close_connection_work_data *)(req->data); @@ -500,7 +500,7 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { if (data->result) { err = true; - argv[0] = Exception::Error(NanNew("Error closing database")); + argv[0] = Exception::Error(Nan::New("Error closing database").ToLocalChecked()); } else { conn->connected = false; @@ -534,9 +534,9 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { NAN_METHOD(ODBCConnection::CloseSync) { DEBUG_PRINTF("ODBCConnection::CloseSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); //TODO: check to see if there are any open statements //on this connection @@ -551,7 +551,7 @@ NAN_METHOD(ODBCConnection::CloseSync) { uv_unref(uv_default_loop()); #endif - NanReturnValue(NanTrue()); + info.GetReturnValue().Set(Nan::True()); } /* @@ -561,9 +561,9 @@ NAN_METHOD(ODBCConnection::CloseSync) { NAN_METHOD(ODBCConnection::CreateStatementSync) { DEBUG_PRINTF("ODBCConnection::CreateStatementSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); HSTMT hSTMT; @@ -577,13 +577,13 @@ NAN_METHOD(ODBCConnection::CreateStatementSync) { uv_mutex_unlock(&ODBC::g_odbcMutex); Local params[3]; - params[0] = NanNew(conn->m_hENV); - params[1] = NanNew(conn->m_hDBC); - params[2] = NanNew(hSTMT); + params[0] = Nan::New(conn->m_hENV); + params[1] = Nan::New(conn->m_hDBC); + params[2] = Nan::New(hSTMT); - Local js_result(NanNew(ODBCStatement::constructor)->NewInstance(3, params)); + Local js_result(Nan::New(ODBCStatement::constructor)->NewInstance(3, params)); - NanReturnValue(js_result); + info.GetReturnValue().Set(js_result); } /* @@ -593,11 +593,11 @@ NAN_METHOD(ODBCConnection::CreateStatementSync) { NAN_METHOD(ODBCConnection::CreateStatement) { DEBUG_PRINTF("ODBCConnection::CreateStatement\n"); - NanScope(); + Nan::HandleScope scope; REQ_FUN_ARG(0, cb); - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); //initialize work request uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -606,7 +606,7 @@ NAN_METHOD(ODBCConnection::CreateStatement) { create_statement_work_data* data = (create_statement_work_data *) (calloc(1, sizeof(create_statement_work_data))); - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->conn = conn; work_req->data = data; @@ -619,7 +619,7 @@ NAN_METHOD(ODBCConnection::CreateStatement) { conn->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCConnection::UV_CreateStatement(uv_work_t* req) { @@ -652,7 +652,7 @@ void ODBCConnection::UV_CreateStatement(uv_work_t* req) { void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterCreateStatement\n"); - NanScope(); + Nan::HandleScope scope; create_statement_work_data* data = (create_statement_work_data *)(req->data); @@ -662,20 +662,20 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { data->hSTMT ); - Local args[3]; - args[0] = NanNew(data->conn->m_hENV); - args[1] = NanNew(data->conn->m_hDBC); - args[2] = NanNew(data->hSTMT); + Local info[3]; + info[0] = Nan::New(data->conn->m_hENV); + info[1] = Nan::New(data->conn->m_hDBC); + info[2] = Nan::New(data->hSTMT); - Local js_result = NanNew(ODBCStatement::constructor)->NewInstance(3, args); + Local js_result = Nan::New(ODBCStatement::constructor)->NewInstance(3, info); - args[0] = NanNew(NanNull()); - args[1] = NanNew(js_result); + info[0] = Nan::New(Nan::Null()); + info[1] = Nan::New(js_result); TryCatch try_catch; - data->cb->Call( 2, args); + data->cb->Call( 2, info); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -694,73 +694,73 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { NAN_METHOD(ODBCConnection::Query) { DEBUG_PRINTF("ODBCConnection::Query\n"); - NanScope(); + Nan::HandleScope scope; Local cb; Local sql; - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); query_work_data* data = (query_work_data *) calloc(1, sizeof(query_work_data)); //Check arguments for different variations of calling this function - if (args.Length() == 3) { + if (info.Length() == 3) { //handle Query("sql string", [params], function cb () {}); - if ( !args[0]->IsString() ) { - return NanThrowTypeError("Argument 0 must be an String."); + if ( !info[0]->IsString() ) { + return Nan::ThrowTypeError("Argument 0 must be an String."); } - else if ( !args[1]->IsArray() ) { - return NanThrowTypeError("Argument 1 must be an Array."); + else if ( !info[1]->IsArray() ) { + return Nan::ThrowTypeError("Argument 1 must be an Array."); } - else if ( !args[2]->IsFunction() ) { - return NanThrowTypeError("Argument 2 must be a Function."); + else if ( !info[2]->IsFunction() ) { + return Nan::ThrowTypeError("Argument 2 must be a Function."); } - sql = args[0]->ToString(); + sql = info[0]->ToString(); data->params = ODBC::GetParametersFromArray( - Local::Cast(args[1]), + Local::Cast(info[1]), &data->paramCount); - cb = Local::Cast(args[2]); + cb = Local::Cast(info[2]); } - else if (args.Length() == 2 ) { + else if (info.Length() == 2 ) { //handle either Query("sql", cb) or Query({ settings }, cb) - if (!args[1]->IsFunction()) { - return NanThrowTypeError("ODBCConnection::Query(): Argument 1 must be a Function."); + if (!info[1]->IsFunction()) { + return Nan::ThrowTypeError("ODBCConnection::Query(): Argument 1 must be a Function."); } - cb = Local::Cast(args[1]); + cb = Local::Cast(info[1]); - if (args[0]->IsString()) { + if (info[0]->IsString()) { //handle Query("sql", function cb () {}) - sql = args[0]->ToString(); + sql = info[0]->ToString(); data->paramCount = 0; } - else if (args[0]->IsObject()) { + else if (info[0]->IsObject()) { //NOTE: going forward this is the way we should expand options //rather than adding more arguments to the function signature. //specify options on an options object. //handle Query({}, function cb () {}); - Local obj = args[0]->ToObject(); + Local obj = info[0]->ToObject(); - Local optionSqlKey = NanNew(OPTION_SQL); + Local optionSqlKey = Nan::New(OPTION_SQL); if (obj->Has(optionSqlKey) && obj->Get(optionSqlKey)->IsString()) { sql = obj->Get(optionSqlKey)->ToString(); } else { - sql = NanNew(""); + sql = Nan::New("").ToLocalChecked(); } - Local optionParamsKey = NanNew(OPTION_PARAMS); + Local optionParamsKey = Nan::New(OPTION_PARAMS); if (obj->Has(optionParamsKey) && obj->Get(optionParamsKey)->IsArray()) { data->params = ODBC::GetParametersFromArray( Local::Cast(obj->Get(optionParamsKey)), @@ -770,7 +770,7 @@ NAN_METHOD(ODBCConnection::Query) { data->paramCount = 0; } - Local optionNoResultsKey = NanNew(OPTION_NORESULTS); + Local optionNoResultsKey = Nan::New(OPTION_NORESULTS); if (obj->Has(optionNoResultsKey) && obj->Get(optionNoResultsKey)->IsBoolean()) { data->noResultObject = obj->Get(optionNoResultsKey)->ToBoolean()->Value(); } @@ -779,15 +779,15 @@ NAN_METHOD(ODBCConnection::Query) { } } else { - return NanThrowTypeError("ODBCConnection::Query(): Argument 0 must be a String or an Object."); + return Nan::ThrowTypeError("ODBCConnection::Query(): Argument 0 must be a String or an Object."); } } else { - return NanThrowTypeError("ODBCConnection::Query(): Requires either 2 or 3 Arguments. "); + return Nan::ThrowTypeError("ODBCConnection::Query(): Requires either 2 or 3 Arguments. "); } //Done checking arguments - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->sqlLen = sql->Length(); #ifdef UNICODE @@ -814,7 +814,7 @@ NAN_METHOD(ODBCConnection::Query) { conn->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCConnection::UV_Query(uv_work_t* req) { @@ -877,7 +877,7 @@ void ODBCConnection::UV_Query(uv_work_t* req) { void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterQuery\n"); - NanScope(); + Nan::HandleScope scope; query_work_data* data = (query_work_data *)(req->data); @@ -888,7 +888,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { if (data->result != SQL_ERROR && data->noResultObject) { //We have been requested to not create a result object //this means we should release the handle now and call back - //with NanTrue() + //with Nan::True() uv_mutex_lock(&ODBC::g_odbcMutex); @@ -896,32 +896,32 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { uv_mutex_unlock(&ODBC::g_odbcMutex); - Local args[2]; - args[0] = NanNew(NanNull()); - args[1] = NanNew(NanTrue()); + Local info[2]; + info[0] = Nan::New(Nan::Null()); + info[1] = Nan::New(Nan::True()); - data->cb->Call(2, args); + data->cb->Call(2, info); } else { - Local args[4]; + Local info[4]; bool* canFreeHandle = new bool(true); - args[0] = NanNew(data->conn->m_hENV); - args[1] = NanNew(data->conn->m_hDBC); - args[2] = NanNew(data->hSTMT); - args[3] = NanNew(canFreeHandle); + info[0] = Nan::New(data->conn->m_hENV); + info[1] = Nan::New(data->conn->m_hDBC); + info[2] = Nan::New(data->hSTMT); + info[3] = Nan::New(canFreeHandle); - Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, args); + Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); // Check now to see if there was an error (as there may be further result sets) if (data->result == SQL_ERROR) { - args[0] = ODBC::GetSQLError(SQL_HANDLE_STMT, data->hSTMT, (char *) "[node-odbc] SQL_ERROR"); + info[0] = ODBC::GetSQLError(SQL_HANDLE_STMT, data->hSTMT, (char *) "[node-odbc] SQL_ERROR"); } else { - args[0] = NanNew(NanNull()); + info[0] = Nan::New(Nan::Null()); } - args[1] = NanNew(js_result); + info[1] = Nan::New(js_result); - data->cb->Call(2, args); + data->cb->Call(2, info); } data->conn->Unref(); @@ -967,7 +967,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { NAN_METHOD(ODBCConnection::QuerySync) { DEBUG_PRINTF("ODBCConnection::QuerySync\n"); - NanScope(); + Nan::HandleScope scope; #ifdef UNICODE String::Value* sql; @@ -975,7 +975,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { String::Utf8Value* sql; #endif - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); Parameter* params = new Parameter[0]; Parameter prm; @@ -985,49 +985,49 @@ NAN_METHOD(ODBCConnection::QuerySync) { bool noResultObject = false; //Check arguments for different variations of calling this function - if (args.Length() == 2) { + if (info.Length() == 2) { //handle QuerySync("sql string", [params]); - if ( !args[0]->IsString() ) { - return NanThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be an String."); + if ( !info[0]->IsString() ) { + return Nan::ThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be an String."); } - else if (!args[1]->IsArray()) { - return NanThrowTypeError("ODBCConnection::QuerySync(): Argument 1 must be an Array."); + else if (!info[1]->IsArray()) { + return Nan::ThrowTypeError("ODBCConnection::QuerySync(): Argument 1 must be an Array."); } #ifdef UNICODE - sql = new String::Value(args[0]->ToString()); + sql = new String::Value(info[0]->ToString()); #else - sql = new String::Utf8Value(args[0]->ToString()); + sql = new String::Utf8Value(info[0]->ToString()); #endif params = ODBC::GetParametersFromArray( - Local::Cast(args[1]), + Local::Cast(info[1]), ¶mCount); } - else if (args.Length() == 1 ) { + else if (info.Length() == 1 ) { //handle either QuerySync("sql") or QuerySync({ settings }) - if (args[0]->IsString()) { + if (info[0]->IsString()) { //handle Query("sql") #ifdef UNICODE - sql = new String::Value(args[0]->ToString()); + sql = new String::Value(info[0]->ToString()); #else - sql = new String::Utf8Value(args[0]->ToString()); + sql = new String::Utf8Value(info[0]->ToString()); #endif paramCount = 0; } - else if (args[0]->IsObject()) { + else if (info[0]->IsObject()) { //NOTE: going forward this is the way we should expand options //rather than adding more arguments to the function signature. //specify options on an options object. //handle Query({}, function cb () {}); - Local obj = args[0]->ToObject(); + Local obj = info[0]->ToObject(); - Local optionSqlKey = NanNew(OPTION_SQL); + Local optionSqlKey = Nan::New(OPTION_SQL).ToLocalChecked(); if (obj->Has(optionSqlKey) && obj->Get(optionSqlKey)->IsString()) { #ifdef UNICODE sql = new String::Value(obj->Get(optionSqlKey)->ToString()); @@ -1037,13 +1037,13 @@ NAN_METHOD(ODBCConnection::QuerySync) { } else { #ifdef UNICODE - sql = new String::Value(NanNew("")); + sql = new String::Value(Nan::New("").ToLocalChecked()); #else - sql = new String::Utf8Value(NanNew("")); + sql = new String::Utf8Value(Nan::New("").ToLocalChecked()); #endif } - Local optionParamsKey = NanNew(OPTION_PARAMS); + Local optionParamsKey = Nan::New(OPTION_PARAMS); if (obj->Has(optionParamsKey) && obj->Get(optionParamsKey)->IsArray()) { params = ODBC::GetParametersFromArray( Local::Cast(obj->Get(optionParamsKey)), @@ -1053,17 +1053,17 @@ NAN_METHOD(ODBCConnection::QuerySync) { paramCount = 0; } - Local optionNoResultsKey = NanNew(OPTION_NORESULTS); + Local optionNoResultsKey = Nan::New(OPTION_NORESULTS); if (obj->Has(optionNoResultsKey) && obj->Get(optionNoResultsKey)->IsBoolean()) { noResultObject = obj->Get(optionNoResultsKey)->ToBoolean()->Value(); } } else { - return NanThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be a String or an Object."); + return Nan::ThrowTypeError("ODBCConnection::QuerySync(): Argument 0 must be a String or an Object."); } } else { - return NanThrowTypeError("ODBCConnection::QuerySync(): Requires either 1 or 2 Arguments."); + return Nan::ThrowTypeError("ODBCConnection::QuerySync(): Requires either 1 or 2 Arguments."); } //Done checking arguments @@ -1130,13 +1130,13 @@ NAN_METHOD(ODBCConnection::QuerySync) { //check to see if there was an error during execution if (ret == SQL_ERROR) { - NanThrowError(ODBC::GetSQLError( + Nan::ThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, hSTMT, (char *) "[node-odbc] Error in ODBCConnection::QuerySync" )); - NanReturnUndefined(); + return; } else if (noResultObject) { //if there is not result object requested then @@ -1147,20 +1147,20 @@ NAN_METHOD(ODBCConnection::QuerySync) { uv_mutex_unlock(&ODBC::g_odbcMutex); - NanReturnValue(NanTrue()); + info.GetReturnValue().Set(Nan::True()); } else { Local result[4]; bool* canFreeHandle = new bool(true); - result[0] = NanNew(conn->m_hENV); - result[1] = NanNew(conn->m_hDBC); - result[2] = NanNew(hSTMT); - result[3] = NanNew(canFreeHandle); + result[0] = Nan::New(conn->m_hENV); + result[1] = Nan::New(conn->m_hDBC); + result[2] = Nan::New(hSTMT); + result[3] = Nan::New(canFreeHandle); - Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, result); + Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, result); - NanReturnValue(js_result); + info.GetReturnValue().Set(js_result); } } @@ -1169,15 +1169,15 @@ NAN_METHOD(ODBCConnection::QuerySync) { */ NAN_METHOD(ODBCConnection::Tables) { - NanScope(); + Nan::HandleScope scope; REQ_STRO_OR_NULL_ARG(0, catalog); REQ_STRO_OR_NULL_ARG(1, schema); REQ_STRO_OR_NULL_ARG(2, table); REQ_STRO_OR_NULL_ARG(3, type); - Local cb = Local::Cast(args[4]); + Local cb = Local::Cast(info[4]); - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -1186,8 +1186,8 @@ NAN_METHOD(ODBCConnection::Tables) { if (!data) { NanLowMemoryNotification(); - NanThrowError("Could not allocate enough memory"); - NanReturnUndefined(); + Nan::ThrowError("Could not allocate enough memory"); + return; } data->sql = NULL; @@ -1196,9 +1196,9 @@ NAN_METHOD(ODBCConnection::Tables) { data->table = NULL; data->type = NULL; data->column = NULL; - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); - if (!catalog->Equals(NanNew("null"))) { + if (!catalog->Equals(Nan::New("null").ToLocalChecked())) { #ifdef UNICODE data->catalog = (uint16_t *) malloc((catalog->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); catalog->Write((uint16_t *) data->catalog); @@ -1208,7 +1208,7 @@ NAN_METHOD(ODBCConnection::Tables) { #endif } - if (!schema->Equals(NanNew("null"))) { + if (!schema->Equals(Nan::New("null").ToLocalChecked())) { #ifdef UNICODE data->schema = (uint16_t *) malloc((schema->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); schema->Write((uint16_t *) data->schema); @@ -1218,7 +1218,7 @@ NAN_METHOD(ODBCConnection::Tables) { #endif } - if (!table->Equals(NanNew("null"))) { + if (!table->Equals(Nan::New("null").ToLocalChecked())) { #ifdef UNICODE data->table = (uint16_t *) malloc((table->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); table->Write((uint16_t *) data->table); @@ -1228,7 +1228,7 @@ NAN_METHOD(ODBCConnection::Tables) { #endif } - if (!type->Equals(NanNew("null"))) { + if (!type->Equals(Nan::New("null").ToLocalChecked())) { #ifdef UNICODE data->type = (uint16_t *) malloc((type->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); type->Write((uint16_t *) data->type); @@ -1249,7 +1249,7 @@ NAN_METHOD(ODBCConnection::Tables) { conn->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCConnection::UV_Tables(uv_work_t* req) { @@ -1280,16 +1280,16 @@ void ODBCConnection::UV_Tables(uv_work_t* req) { */ NAN_METHOD(ODBCConnection::Columns) { - NanScope(); + Nan::HandleScope scope; REQ_STRO_OR_NULL_ARG(0, catalog); REQ_STRO_OR_NULL_ARG(1, schema); REQ_STRO_OR_NULL_ARG(2, table); REQ_STRO_OR_NULL_ARG(3, column); - Local cb = Local::Cast(args[4]); + Local cb = Local::Cast(info[4]); - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -1297,8 +1297,8 @@ NAN_METHOD(ODBCConnection::Columns) { if (!data) { NanLowMemoryNotification(); - NanThrowError("Could not allocate enough memory"); - NanReturnUndefined(); + Nan::ThrowError("Could not allocate enough memory"); + return; } data->sql = NULL; @@ -1307,9 +1307,9 @@ NAN_METHOD(ODBCConnection::Columns) { data->table = NULL; data->type = NULL; data->column = NULL; - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); - if (!catalog->Equals(NanNew("null"))) { + if (!catalog->Equals(Nan::New("null").ToLocalChecked())) { #ifdef UNICODE data->catalog = (uint16_t *) malloc((catalog->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); catalog->Write((uint16_t *) data->catalog); @@ -1319,7 +1319,7 @@ NAN_METHOD(ODBCConnection::Columns) { #endif } - if (!schema->Equals(NanNew("null"))) { + if (!schema->Equals(Nan::New("null").ToLocalChecked())) { #ifdef UNICODE data->schema = (uint16_t *) malloc((schema->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); schema->Write((uint16_t *) data->schema); @@ -1329,7 +1329,7 @@ NAN_METHOD(ODBCConnection::Columns) { #endif } - if (!table->Equals(NanNew("null"))) { + if (!table->Equals(Nan::New("null").ToLocalChecked())) { #ifdef UNICODE data->table = (uint16_t *) malloc((table->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); table->Write((uint16_t *) data->table); @@ -1339,7 +1339,7 @@ NAN_METHOD(ODBCConnection::Columns) { #endif } - if (!column->Equals(NanNew("null"))) { + if (!column->Equals(Nan::New("null").ToLocalChecked())) { #ifdef UNICODE data->column = (uint16_t *) malloc((column->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); column->Write((uint16_t *) data->column); @@ -1360,7 +1360,7 @@ NAN_METHOD(ODBCConnection::Columns) { conn->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCConnection::UV_Columns(uv_work_t* req) { @@ -1391,9 +1391,9 @@ void ODBCConnection::UV_Columns(uv_work_t* req) { NAN_METHOD(ODBCConnection::BeginTransactionSync) { DEBUG_PRINTF("ODBCConnection::BeginTransactionSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); SQLRETURN ret; @@ -1407,12 +1407,12 @@ NAN_METHOD(ODBCConnection::BeginTransactionSync) { if (!SQL_SUCCEEDED(ret)) { Local objError = ODBC::GetSQLError(SQL_HANDLE_DBC, conn->m_hDBC); - NanThrowError(objError); + Nan::ThrowError(objError); - NanReturnValue(NanFalse()); + info.GetReturnValue().Set(Nan::False()); } - NanReturnValue(NanTrue()); + info.GetReturnValue().Set(Nan::True()); } /* @@ -1422,11 +1422,11 @@ NAN_METHOD(ODBCConnection::BeginTransactionSync) { NAN_METHOD(ODBCConnection::BeginTransaction) { DEBUG_PRINTF("ODBCConnection::BeginTransaction\n"); - NanScope(); + Nan::HandleScope scope; REQ_FUN_ARG(0, cb); - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -1435,10 +1435,10 @@ NAN_METHOD(ODBCConnection::BeginTransaction) { if (!data) { NanLowMemoryNotification(); - return NanThrowError("Could not allocate enough memory"); + return Nan::ThrowError("Could not allocate enough memory"); } - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->conn = conn; work_req->data = data; @@ -1448,7 +1448,7 @@ NAN_METHOD(ODBCConnection::BeginTransaction) { UV_BeginTransaction, (uv_after_work_cb)UV_AfterBeginTransaction); - NanReturnUndefined(); + return; } /* @@ -1476,7 +1476,7 @@ void ODBCConnection::UV_BeginTransaction(uv_work_t* req) { void ODBCConnection::UV_AfterBeginTransaction(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterBeginTransaction\n"); - NanScope(); + Nan::HandleScope scope; //TODO: Is this supposed to be of type query_work_data? open_connection_work_data* data = (open_connection_work_data *)(req->data); @@ -1514,9 +1514,9 @@ void ODBCConnection::UV_AfterBeginTransaction(uv_work_t* req, int status) { NAN_METHOD(ODBCConnection::EndTransactionSync) { DEBUG_PRINTF("ODBCConnection::EndTransactionSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); REQ_BOOL_ARG(0, rollback); @@ -1561,12 +1561,12 @@ NAN_METHOD(ODBCConnection::EndTransactionSync) { } if (error) { - NanThrowError(objError); + Nan::ThrowError(objError); - NanReturnValue(NanFalse()); + info.GetReturnValue().Set(Nan::False()); } else { - NanReturnValue(NanTrue()); + info.GetReturnValue().Set(Nan::True()); } } @@ -1577,12 +1577,12 @@ NAN_METHOD(ODBCConnection::EndTransactionSync) { NAN_METHOD(ODBCConnection::EndTransaction) { DEBUG_PRINTF("ODBCConnection::EndTransaction\n"); - NanScope(); + Nan::HandleScope scope; REQ_BOOL_ARG(0, rollback); REQ_FUN_ARG(1, cb); - ODBCConnection* conn = ObjectWrap::Unwrap(args.Holder()); + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -1591,14 +1591,14 @@ NAN_METHOD(ODBCConnection::EndTransaction) { if (!data) { NanLowMemoryNotification(); - return NanThrowError("Could not allocate enough memory"); + return Nan::ThrowError("Could not allocate enough memory"); } data->completionType = (rollback->Value()) ? SQL_ROLLBACK : SQL_COMMIT ; - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->conn = conn; work_req->data = data; @@ -1608,7 +1608,7 @@ NAN_METHOD(ODBCConnection::EndTransaction) { UV_EndTransaction, (uv_after_work_cb)UV_AfterEndTransaction); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } /* @@ -1657,7 +1657,7 @@ void ODBCConnection::UV_EndTransaction(uv_work_t* req) { void ODBCConnection::UV_AfterEndTransaction(uv_work_t* req, int status) { DEBUG_PRINTF("ODBCConnection::UV_AfterEndTransaction\n"); - NanScope(); + Nan::HandleScope scope; open_connection_work_data* data = (open_connection_work_data *)(req->data); diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 76aa67d4..1fdd6ce6 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -20,12 +20,12 @@ #include -class ODBCConnection : public node::ObjectWrap { +class ODBCConnection : public Nan::ObjectWrap { public: - static Persistent OPTION_SQL; - static Persistent OPTION_PARAMS; - static Persistent OPTION_NORESULTS; - static Persistent constructor; + static Nan::Persistent OPTION_SQL; + static Nan::Persistent OPTION_PARAMS; + static Nan::Persistent OPTION_NORESULTS; + static Nan::Persistent constructor; static void Init(v8::Handle exports); @@ -35,7 +35,7 @@ class ODBCConnection : public node::ObjectWrap { ODBCConnection() {}; explicit ODBCConnection(HENV hENV, HDBC hDBC): - ObjectWrap(), + Nan::ObjectWrap(), m_hENV(hENV), m_hDBC(hDBC) {}; @@ -92,7 +92,7 @@ class ODBCConnection : public node::ObjectWrap { static NAN_METHOD(EndTransactionSync); struct Fetch_Request { - NanCallback* callback; + Nan::Callback* callback; ODBCConnection *objResult; SQLRETURN result; }; @@ -110,14 +110,14 @@ class ODBCConnection : public node::ObjectWrap { }; struct create_statement_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBCConnection *conn; HSTMT hSTMT; int result; }; struct query_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBCConnection *conn; HSTMT hSTMT; @@ -140,7 +140,7 @@ struct query_work_data { }; struct open_connection_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBCConnection *conn; int result; int connectionLength; @@ -148,7 +148,7 @@ struct open_connection_work_data { }; struct close_connection_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBCConnection *conn; int result; }; diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 21ed9bc6..224fc887 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -29,39 +29,39 @@ using namespace v8; using namespace node; -Persistent ODBCResult::constructor; -Persistent ODBCResult::OPTION_FETCH_MODE; +Nan::Persistent ODBCResult::constructor; +Nan::Persistent ODBCResult::OPTION_FETCH_MODE; void ODBCResult::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCResult::Init\n"); - NanScope(); + Nan::HandleScope scope; - Local constructor_template = NanNew(New); + Local constructor_template = Nan::New(New); // Constructor Template - constructor_template->SetClassName(NanNew("ODBCResult")); + constructor_template->SetClassName(Nan::New("ODBCResult").ToLocalChecked()); // Reserve space for one Handle Local instance_template = constructor_template->InstanceTemplate(); instance_template->SetInternalFieldCount(1); // Prototype Methods - NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAll", FetchAll); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetch", Fetch); + Nan::SetPrototypeMethod(constructor_template, "fetchAll", FetchAll); + Nan::SetPrototypeMethod(constructor_template, "fetch", Fetch); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "moreResultsSync", MoreResultsSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchSync", FetchSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAllSync", FetchAllSync); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "getColumnNamesSync", GetColumnNamesSync); + Nan::SetPrototypeMethod(constructor_template, "moreResultsSync", MoreResultsSync); + Nan::SetPrototypeMethod(constructor_template, "closeSync", CloseSync); + Nan::SetPrototypeMethod(constructor_template, "fetchSync", FetchSync); + Nan::SetPrototypeMethod(constructor_template, "fetchAllSync", FetchAllSync); + Nan::SetPrototypeMethod(constructor_template, "getColumnNamesSync", GetColumnNamesSync); // Properties - NanAssignPersistent(OPTION_FETCH_MODE, NanNew("fetchMode")); - instance_template->SetAccessor(NanNew("fetchMode"), FetchModeGetter, FetchModeSetter); + OPTION_FETCH_MODE.Reset(Nan::New("fetchMode").ToLocalChecked()); + Nan::SetAccessor(instance_template, Nan::New("fetchMode").ToLocalChecked(), FetchModeGetter, FetchModeSetter); // Attach the Database Constructor to the target object - NanAssignPersistent(constructor, constructor_template->GetFunction()); - exports->Set(NanNew("ODBCResult"), + constructor.Reset(constructor_template->GetFunction()); + exports->Set(Nan::New("ODBCResult").ToLocalChecked(), constructor_template->GetFunction()); } @@ -91,7 +91,7 @@ void ODBCResult::Free() { NAN_METHOD(ODBCResult::New) { DEBUG_PRINTF("ODBCResult::New\n"); - NanScope(); + Nan::HandleScope scope; REQ_EXT_ARG(0, js_henv); REQ_EXT_ARG(1, js_hdbc); @@ -129,23 +129,23 @@ NAN_METHOD(ODBCResult::New) { //default fetchMode to FETCH_OBJECT objODBCResult->m_fetchMode = FETCH_OBJECT; - objODBCResult->Wrap(args.Holder()); + objODBCResult->Wrap(info.Holder()); - NanReturnValue(args.Holder()); + info.GetReturnValue().Set(info.Holder()); } NAN_GETTER(ODBCResult::FetchModeGetter) { - NanScope(); + Nan::HandleScope scope; - ODBCResult *obj = ObjectWrap::Unwrap(args.Holder()); + ODBCResult *obj = Nan::ObjectWrap::Unwrap(info.Holder()); - NanReturnValue(NanNew(obj->m_fetchMode)); + info.GetReturnValue().Set(Nan::New(obj->m_fetchMode)); } NAN_SETTER(ODBCResult::FetchModeSetter) { - NanScope(); + Nan::HandleScope scope; - ODBCResult *obj = ObjectWrap::Unwrap(args.Holder()); + ODBCResult *obj = Nan::ObjectWrap::Unwrap(info.Holder()); if (value->IsNumber()) { obj->m_fetchMode = value->Int32Value(); @@ -158,9 +158,9 @@ NAN_SETTER(ODBCResult::FetchModeSetter) { NAN_METHOD(ODBCResult::Fetch) { DEBUG_PRINTF("ODBCResult::Fetch\n"); - NanScope(); + Nan::HandleScope scope; - ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); + ODBCResult* objODBCResult = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -171,24 +171,24 @@ NAN_METHOD(ODBCResult::Fetch) { //set the fetch mode to the default of this instance data->fetchMode = objODBCResult->m_fetchMode; - if (args.Length() == 1 && args[0]->IsFunction()) { - cb = Local::Cast(args[0]); + if (info.Length() == 1 && info[0]->IsFunction()) { + cb = Local::Cast(info[0]); } - else if (args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction()) { - cb = Local::Cast(args[1]); + else if (info.Length() == 2 && info[0]->IsObject() && info[1]->IsFunction()) { + cb = Local::Cast(info[1]); - Local obj = args[0]->ToObject(); + Local obj = info[0]->ToObject(); - Local fetchModeKey = NanNew(OPTION_FETCH_MODE); + Local fetchModeKey = Nan::New(OPTION_FETCH_MODE).ToLocalChecked(); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } } else { - return NanThrowTypeError("ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function."); + return Nan::ThrowTypeError("ODBCResult::Fetch(): 1 or 2 arguments are required. The last argument must be a callback function."); } - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->objResult = objODBCResult; work_req->data = data; @@ -201,7 +201,7 @@ NAN_METHOD(ODBCResult::Fetch) { objODBCResult->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCResult::UV_Fetch(uv_work_t* work_req) { @@ -214,7 +214,7 @@ void ODBCResult::UV_Fetch(uv_work_t* work_req) { void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { DEBUG_PRINTF("ODBCResult::UV_AfterFetch\n"); - NanScope(); + Nan::HandleScope scope; fetch_work_data* data = (fetch_work_data *)(work_req->data); @@ -252,11 +252,11 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { } if (moreWork) { - Handle args[2]; + Handle info[2]; - args[0] = NanNull(); + info[0] = Nan::Null(); if (data->fetchMode == FETCH_ARRAY) { - args[1] = ODBC::GetRecordArray( + info[1] = ODBC::GetRecordArray( data->objResult->m_hSTMT, data->objResult->columns, &data->objResult->colCount, @@ -264,7 +264,7 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { data->objResult->bufferLength); } else { - args[1] = ODBC::GetRecordTuple( + info[1] = ODBC::GetRecordTuple( data->objResult->m_hSTMT, data->objResult->columns, &data->objResult->colCount, @@ -274,7 +274,7 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { TryCatch try_catch; - data->cb->Call(2, args); + data->cb->Call(2, info); delete data->cb; if (try_catch.HasCaught()) { @@ -284,21 +284,21 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { else { ODBC::FreeColumns(data->objResult->columns, &data->objResult->colCount); - Handle args[2]; + Handle info[2]; //if there was an error, pass that as arg[0] otherwise Null if (error) { - args[0] = objError; + info[0] = objError; } else { - args[0] = NanNull(); + info[0] = Nan::Null(); } - args[1] = NanNull(); + info[1] = Nan::Null(); TryCatch try_catch; - data->cb->Call(2, args); + data->cb->Call(2, info); delete data->cb; if (try_catch.HasCaught()) { @@ -320,19 +320,19 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { NAN_METHOD(ODBCResult::FetchSync) { DEBUG_PRINTF("ODBCResult::FetchSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCResult* objResult = ObjectWrap::Unwrap(args.Holder()); + ODBCResult* objResult = Nan::ObjectWrap::Unwrap(info.Holder()); Local objError; bool moreWork = true; bool error = false; int fetchMode = objResult->m_fetchMode; - if (args.Length() == 1 && args[0]->IsObject()) { - Local obj = args[0]->ToObject(); + if (info.Length() == 1 && info[0]->IsObject()) { + Local obj = info[0]->ToObject(); - Local fetchModeKey = NanNew(OPTION_FETCH_MODE); + Local fetchModeKey = Nan::New(OPTION_FETCH_MODE).ToLocalChecked(); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } @@ -385,19 +385,19 @@ NAN_METHOD(ODBCResult::FetchSync) { objResult->bufferLength); } - NanReturnValue(data); + info.GetReturnValue().Set(data); } else { ODBC::FreeColumns(objResult->columns, &objResult->colCount); //if there was an error, pass that as arg[0] otherwise Null if (error) { - NanThrowError(objError); + Nan::ThrowError(objError); - NanReturnValue(NanNull()); + info.GetReturnValue().Set(Nan::Null()); } else { - NanReturnValue(NanNull()); + info.GetReturnValue().Set(Nan::Null()); } } } @@ -408,9 +408,9 @@ NAN_METHOD(ODBCResult::FetchSync) { NAN_METHOD(ODBCResult::FetchAll) { DEBUG_PRINTF("ODBCResult::FetchAll\n"); - NanScope(); + Nan::HandleScope scope; - ODBCResult* objODBCResult = ObjectWrap::Unwrap(args.Holder()); + ODBCResult* objODBCResult = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -420,29 +420,29 @@ NAN_METHOD(ODBCResult::FetchAll) { data->fetchMode = objODBCResult->m_fetchMode; - if (args.Length() == 1 && args[0]->IsFunction()) { - cb = Local::Cast(args[0]); + if (info.Length() == 1 && info[0]->IsFunction()) { + cb = Local::Cast(info[0]); } - else if (args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction()) { - cb = Local::Cast(args[1]); + else if (info.Length() == 2 && info[0]->IsObject() && info[1]->IsFunction()) { + cb = Local::Cast(info[1]); - Local obj = args[0]->ToObject(); + Local obj = info[0]->ToObject(); - Local fetchModeKey = NanNew(OPTION_FETCH_MODE); + Local fetchModeKey = Nan::New(OPTION_FETCH_MODE).ToLocalChecked(); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } } else { - NanThrowTypeError("ODBCResult::FetchAll(): 1 or 2 arguments are required. The last argument must be a callback function."); + Nan::ThrowTypeError("ODBCResult::FetchAll(): 1 or 2 arguments are required. The last argument must be a callback function."); } - NanAssignPersistent(data->rows, NanNew()); + data->rows.Reset(Nan::New()); data->errorCount = 0; data->count = 0; - NanAssignPersistent(data->objError, NanNew()); + data->objError.Reset(Nan::New()); - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->objResult = objODBCResult; work_req->data = data; @@ -454,7 +454,7 @@ NAN_METHOD(ODBCResult::FetchAll) { data->objResult->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCResult::UV_FetchAll(uv_work_t* work_req) { @@ -467,7 +467,7 @@ void ODBCResult::UV_FetchAll(uv_work_t* work_req) { void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { DEBUG_PRINTF("ODBCResult::UV_AfterFetchAll\n"); - NanScope(); + Nan::HandleScope scope; fetch_work_data* data = (fetch_work_data *)(work_req->data); @@ -502,10 +502,10 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { doMoreWork = false; } else { - Local rows = NanNew(data->rows); + Local rows = Nan::New(data->rows); if (data->fetchMode == FETCH_ARRAY) { rows->Set( - NanNew(data->count), + Nan::New(data->count), ODBC::GetRecordArray( self->m_hSTMT, self->columns, @@ -516,7 +516,7 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { } else { rows->Set( - NanNew(data->count), + Nan::New(data->count), ODBC::GetRecordTuple( self->m_hSTMT, self->columns, @@ -539,23 +539,23 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { else { ODBC::FreeColumns(self->columns, &self->colCount); - Handle args[2]; + Handle info[2]; if (data->errorCount > 0) { - args[0] = NanNew(data->objError); + info[0] = Nan::New(data->objError); } else { - args[0] = NanNull(); + info[0] = Nan::Null(); } - args[1] = NanNew(data->rows); + info[1] = Nan::New(data->rows); TryCatch try_catch; - data->cb->Call(2, args); + data->cb->Call(2, info); delete data->cb; - NanDisposePersistent(data->rows); - NanDisposePersistent(data->objError); + data->rows.Reset(); + data->objError.Reset(); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -574,21 +574,21 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { NAN_METHOD(ODBCResult::FetchAllSync) { DEBUG_PRINTF("ODBCResult::FetchAllSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCResult* self = ObjectWrap::Unwrap(args.Holder()); + ODBCResult* self = Nan::ObjectWrap::Unwrap(info.Holder()); - Local objError = NanNew(); + Local objError = Nan::New(); SQLRETURN ret; int count = 0; int errorCount = 0; int fetchMode = self->m_fetchMode; - if (args.Length() == 1 && args[0]->IsObject()) { - Local obj = args[0]->ToObject(); + if (info.Length() == 1 && info[0]->IsObject()) { + Local obj = info[0]->ToObject(); - Local fetchModeKey = NanNew(OPTION_FETCH_MODE); + Local fetchModeKey = Nan::New(OPTION_FETCH_MODE).ToLocalChecked(); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } @@ -598,7 +598,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); } - Local rows = NanNew(); + Local rows = Nan::New(); //Only loop through the recordset if there are columns if (self->colCount > 0) { @@ -629,7 +629,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { if (fetchMode == FETCH_ARRAY) { rows->Set( - NanNew(count), + Nan::New(count), ODBC::GetRecordArray( self->m_hSTMT, self->columns, @@ -640,7 +640,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { } else { rows->Set( - NanNew(count), + Nan::New(count), ODBC::GetRecordTuple( self->m_hSTMT, self->columns, @@ -658,10 +658,10 @@ NAN_METHOD(ODBCResult::FetchAllSync) { //throw the error object if there were errors if (errorCount > 0) { - NanThrowError(objError); + Nan::ThrowError(objError); } - NanReturnValue(rows); + info.GetReturnValue().Set(rows); } /* @@ -671,11 +671,11 @@ NAN_METHOD(ODBCResult::FetchAllSync) { NAN_METHOD(ODBCResult::CloseSync) { DEBUG_PRINTF("ODBCResult::CloseSync\n"); - NanScope(); + Nan::HandleScope scope; OPT_INT_ARG(0, closeOption, SQL_DESTROY); - ODBCResult* result = ObjectWrap::Unwrap(args.Holder()); + ODBCResult* result = Nan::ObjectWrap::Unwrap(info.Holder()); DEBUG_PRINTF("ODBCResult::CloseSync closeOption=%i m_canFreeHandle=%i\n", closeOption, result->m_canFreeHandle); @@ -699,22 +699,22 @@ NAN_METHOD(ODBCResult::CloseSync) { uv_mutex_unlock(&ODBC::g_odbcMutex); } - NanReturnValue(NanTrue()); + info.GetReturnValue().Set(Nan::True()); } NAN_METHOD(ODBCResult::MoreResultsSync) { DEBUG_PRINTF("ODBCResult::MoreResultsSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCResult* result = ObjectWrap::Unwrap(args.Holder()); + ODBCResult* result = Nan::ObjectWrap::Unwrap(info.Holder()); SQLRETURN ret = SQLMoreResults(result->m_hSTMT); if (ret == SQL_ERROR) { - NanThrowError(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync")); + Nan::ThrowError(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync")); } - NanReturnValue(SQL_SUCCEEDED(ret) || ret == SQL_ERROR ? NanTrue() : NanFalse()); + info.GetReturnValue().Set(SQL_SUCCEEDED(ret) || ret == SQL_ERROR ? Nan::True() : Nan::False()); } /* @@ -723,20 +723,20 @@ NAN_METHOD(ODBCResult::MoreResultsSync) { NAN_METHOD(ODBCResult::GetColumnNamesSync) { DEBUG_PRINTF("ODBCResult::GetColumnNamesSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCResult* self = ObjectWrap::Unwrap(args.Holder()); + ODBCResult* self = Nan::ObjectWrap::Unwrap(info.Holder()); - Local cols = NanNew(); + Local cols = Nan::New(); if (self->colCount == 0) { self->columns = ODBC::GetColumns(self->m_hSTMT, &self->colCount); } for (int i = 0; i < self->colCount; i++) { - cols->Set(NanNew(i), - NanNew((const char *) self->columns[i].name)); + cols->Set(Nan::New(i), + Nan::New((const char *) self->columns[i].name)); } - NanReturnValue(cols); + info.GetReturnValue().Set(cols); } diff --git a/src/odbc_result.h b/src/odbc_result.h index 87c489c5..e9bc1052 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -19,10 +19,10 @@ #include -class ODBCResult : public node::ObjectWrap { +class ODBCResult : public Nan::ObjectWrap { public: - static Persistent OPTION_FETCH_MODE; - static Persistent constructor; + static Nan::Persistent OPTION_FETCH_MODE; + static Nan::Persistent constructor; static void Init(v8::Handle exports); void Free(); @@ -31,7 +31,7 @@ class ODBCResult : public node::ObjectWrap { ODBCResult() {}; explicit ODBCResult(HENV hENV, HDBC hDBC, HSTMT hSTMT, bool canFreeHandle): - ObjectWrap(), + Nan::ObjectWrap(), m_hENV(hENV), m_hDBC(hDBC), m_hSTMT(hSTMT), @@ -63,15 +63,15 @@ class ODBCResult : public node::ObjectWrap { static NAN_SETTER(FetchModeSetter); struct fetch_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBCResult *objResult; SQLRETURN result; int fetchMode; int count; int errorCount; - Persistent rows; - Persistent objError; + Nan::Persistent rows; + Nan::Persistent objError; }; ODBCResult *self(void) { return this; } diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 56c0a74a..37395b0f 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -29,43 +29,43 @@ using namespace v8; using namespace node; -Persistent ODBCStatement::constructor; +Nan::Persistent ODBCStatement::constructor; void ODBCStatement::Init(v8::Handle exports) { DEBUG_PRINTF("ODBCStatement::Init\n"); - NanScope(); + Nan::HandleScope scope; - Local t = NanNew(New); + Local t = Nan::New(New); // Constructor Template - t->SetClassName(NanNew("ODBCStatement")); + t->SetClassName(Nan::New("ODBCStatement").ToLocalChecked()); // Reserve space for one Handle Local instance_template = t->InstanceTemplate(); instance_template->SetInternalFieldCount(1); // Prototype Methods - NODE_SET_PROTOTYPE_METHOD(t, "execute", Execute); - NODE_SET_PROTOTYPE_METHOD(t, "executeSync", ExecuteSync); + Nan::SetPrototypeMethod(t, "execute", Execute); + Nan::SetPrototypeMethod(t, "executeSync", ExecuteSync); - NODE_SET_PROTOTYPE_METHOD(t, "executeDirect", ExecuteDirect); - NODE_SET_PROTOTYPE_METHOD(t, "executeDirectSync", ExecuteDirectSync); + Nan::SetPrototypeMethod(t, "executeDirect", ExecuteDirect); + Nan::SetPrototypeMethod(t, "executeDirectSync", ExecuteDirectSync); - NODE_SET_PROTOTYPE_METHOD(t, "executeNonQuery", ExecuteNonQuery); - NODE_SET_PROTOTYPE_METHOD(t, "executeNonQuerySync", ExecuteNonQuerySync); + Nan::SetPrototypeMethod(t, "executeNonQuery", ExecuteNonQuery); + Nan::SetPrototypeMethod(t, "executeNonQuerySync", ExecuteNonQuerySync); - NODE_SET_PROTOTYPE_METHOD(t, "prepare", Prepare); - NODE_SET_PROTOTYPE_METHOD(t, "prepareSync", PrepareSync); + Nan::SetPrototypeMethod(t, "prepare", Prepare); + Nan::SetPrototypeMethod(t, "prepareSync", PrepareSync); - NODE_SET_PROTOTYPE_METHOD(t, "bind", Bind); - NODE_SET_PROTOTYPE_METHOD(t, "bindSync", BindSync); + Nan::SetPrototypeMethod(t, "bind", Bind); + Nan::SetPrototypeMethod(t, "bindSync", BindSync); - NODE_SET_PROTOTYPE_METHOD(t, "closeSync", CloseSync); + Nan::SetPrototypeMethod(t, "closeSync", CloseSync); // Attach the Database Constructor to the target object - NanAssignPersistent(constructor, t->GetFunction()); - exports->Set(NanNew("ODBCStatement"), t->GetFunction()); + constructor.Reset(t->GetFunction()); + exports->Set(Nan::New("ODBCStatement").ToLocalChecked(), t->GetFunction()); } ODBCStatement::~ODBCStatement() { @@ -113,7 +113,7 @@ void ODBCStatement::Free() { NAN_METHOD(ODBCStatement::New) { DEBUG_PRINTF("ODBCStatement::New\n"); - NanScope(); + Nan::HandleScope scope; REQ_EXT_ARG(0, js_henv); REQ_EXT_ARG(1, js_hdbc); @@ -139,9 +139,9 @@ NAN_METHOD(ODBCStatement::New) { //initialize the paramCount stmt->paramCount = 0; - stmt->Wrap(args.Holder()); + stmt->Wrap(info.Holder()); - NanReturnValue(args.Holder()); + info.GetReturnValue().Set(info.Holder()); } /* @@ -151,18 +151,18 @@ NAN_METHOD(ODBCStatement::New) { NAN_METHOD(ODBCStatement::Execute) { DEBUG_PRINTF("ODBCStatement::Execute\n"); - NanScope(); + Nan::HandleScope scope; REQ_FUN_ARG(0, cb); - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); execute_work_data* data = (execute_work_data *) calloc(1, sizeof(execute_work_data)); - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->stmt = stmt; work_req->data = data; @@ -175,7 +175,7 @@ NAN_METHOD(ODBCStatement::Execute) { stmt->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCStatement::UV_Execute(uv_work_t* req) { @@ -195,7 +195,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { execute_work_data* data = (execute_work_data *)(req->data); - NanScope(); + Nan::HandleScope scope; //an easy reference to the statment object ODBCStatement* self = data->stmt->self(); @@ -208,22 +208,22 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { data->cb); } else { - Local args[4]; + Local info[4]; bool* canFreeHandle = new bool(false); - args[0] = NanNew(self->m_hENV); - args[1] = NanNew(self->m_hDBC); - args[2] = NanNew(self->m_hSTMT); - args[3] = NanNew(canFreeHandle); + info[0] = Nan::New(self->m_hENV); + info[1] = Nan::New(self->m_hDBC); + info[2] = Nan::New(self->m_hSTMT); + info[3] = Nan::New(canFreeHandle); - Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, args); + Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); - args[0] = NanNew(NanNull()); - args[1] = NanNew(js_result); + info[0] = Nan::New(Nan::Null()); + info[1] = Nan::New(js_result); TryCatch try_catch; - data->cb->Call(2, args); + data->cb->Call(2, info); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -245,33 +245,33 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { NAN_METHOD(ODBCStatement::ExecuteSync) { DEBUG_PRINTF("ODBCStatement::ExecuteSync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); SQLRETURN ret = SQLExecute(stmt->m_hSTMT); if(ret == SQL_ERROR) { - NanThrowError(ODBC::GetSQLError( + Nan::ThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); - NanReturnValue(NanNull()); + info.GetReturnValue().Set(Nan::Null()); } else { Local result[4]; bool* canFreeHandle = new bool(false); - result[0] = NanNew(stmt->m_hENV); - result[1] = NanNew(stmt->m_hDBC); - result[2] = NanNew(stmt->m_hSTMT); - result[3] = NanNew(canFreeHandle); + result[0] = Nan::New(stmt->m_hENV); + result[1] = Nan::New(stmt->m_hDBC); + result[2] = Nan::New(stmt->m_hSTMT); + result[3] = Nan::New(canFreeHandle); - Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, result); + Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, result); - NanReturnValue(js_result); + info.GetReturnValue().Set(js_result); } } @@ -282,18 +282,18 @@ NAN_METHOD(ODBCStatement::ExecuteSync) { NAN_METHOD(ODBCStatement::ExecuteNonQuery) { DEBUG_PRINTF("ODBCStatement::ExecuteNonQuery\n"); - NanScope(); + Nan::HandleScope scope; REQ_FUN_ARG(0, cb); - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); execute_work_data* data = (execute_work_data *) calloc(1, sizeof(execute_work_data)); - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->stmt = stmt; work_req->data = data; @@ -306,7 +306,7 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuery) { stmt->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCStatement::UV_ExecuteNonQuery(uv_work_t* req) { @@ -326,7 +326,7 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { execute_work_data* data = (execute_work_data *)(req->data); - NanScope(); + Nan::HandleScope scope; //an easy reference to the statment object ODBCStatement* self = data->stmt->self(); @@ -351,15 +351,15 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { SQLFreeStmt(self->m_hSTMT, SQL_CLOSE); uv_mutex_unlock(&ODBC::g_odbcMutex); - Local args[2]; + Local info[2]; - args[0] = NanNew(NanNull()); + info[0] = Nan::New(Nan::Null()); // We get a potential loss of precision here. Number isn't as big as int64. Probably fine though. - args[1] = NanNew(NanNew(rowCount)); + info[1] = Nan::New(Nan::New(rowCount)); TryCatch try_catch; - data->cb->Call(2, args); + data->cb->Call(2, info); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -381,20 +381,20 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { NAN_METHOD(ODBCStatement::ExecuteNonQuerySync) { DEBUG_PRINTF("ODBCStatement::ExecuteNonQuerySync\n"); - NanScope(); + Nan::HandleScope scope; - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); SQLRETURN ret = SQLExecute(stmt->m_hSTMT); if(ret == SQL_ERROR) { - NanThrowError(ODBC::GetSQLError( + Nan::ThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" )); - NanReturnValue(NanNull()); + info.GetReturnValue().Set(Nan::Null()); } else { SQLLEN rowCount = 0; @@ -409,7 +409,7 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuerySync) { SQLFreeStmt(stmt->m_hSTMT, SQL_CLOSE); uv_mutex_unlock(&ODBC::g_odbcMutex); - NanReturnValue(NanNew(rowCount)); + info.GetReturnValue().Set(Nan::New(rowCount)); } } @@ -421,19 +421,19 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuerySync) { NAN_METHOD(ODBCStatement::ExecuteDirect) { DEBUG_PRINTF("ODBCStatement::ExecuteDirect\n"); - NanScope(); + Nan::HandleScope scope; REQ_STRO_ARG(0, sql); REQ_FUN_ARG(1, cb); - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); execute_direct_work_data* data = (execute_direct_work_data *) calloc(1, sizeof(execute_direct_work_data)); - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->sqlLen = sql->Length(); @@ -456,7 +456,7 @@ NAN_METHOD(ODBCStatement::ExecuteDirect) { stmt->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCStatement::UV_ExecuteDirect(uv_work_t* req) { @@ -479,7 +479,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { execute_direct_work_data* data = (execute_direct_work_data *)(req->data); - NanScope(); + Nan::HandleScope scope; //an easy reference to the statment object ODBCStatement* self = data->stmt->self(); @@ -492,22 +492,22 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { data->cb); } else { - Local args[4]; + Local info[4]; bool* canFreeHandle = new bool(false); - args[0] = NanNew(self->m_hENV); - args[1] = NanNew(self->m_hDBC); - args[2] = NanNew(self->m_hSTMT); - args[3] = NanNew(canFreeHandle); + info[0] = Nan::New(self->m_hENV); + info[1] = Nan::New(self->m_hDBC); + info[2] = Nan::New(self->m_hSTMT); + info[3] = Nan::New(canFreeHandle); - Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, args); + Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); - args[0] = NanNew(NanNull()); - args[1] = NanNew(js_result); + info[0] = Nan::New(Nan::Null()); + info[1] = Nan::New(js_result); TryCatch try_catch; - data->cb->Call(2, args); + data->cb->Call(2, info); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -530,7 +530,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { NAN_METHOD(ODBCStatement::ExecuteDirectSync) { DEBUG_PRINTF("ODBCStatement::ExecuteDirectSync\n"); - NanScope(); + Nan::HandleScope scope; #ifdef UNICODE REQ_WSTR_ARG(0, sql); @@ -538,7 +538,7 @@ NAN_METHOD(ODBCStatement::ExecuteDirectSync) { REQ_STR_ARG(0, sql); #endif - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); SQLRETURN ret = SQLExecDirect( stmt->m_hSTMT, @@ -546,26 +546,26 @@ NAN_METHOD(ODBCStatement::ExecuteDirectSync) { sql.length()); if(ret == SQL_ERROR) { - NanThrowError(ODBC::GetSQLError( + Nan::ThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteDirectSync" )); - NanReturnValue(NanNull()); + info.GetReturnValue().Set(Nan::Null()); } else { Local result[4]; bool* canFreeHandle = new bool(false); - result[0] = NanNew(stmt->m_hENV); - result[1] = NanNew(stmt->m_hDBC); - result[2] = NanNew(stmt->m_hSTMT); - result[3] = NanNew(canFreeHandle); + result[0] = Nan::New(stmt->m_hENV); + result[1] = Nan::New(stmt->m_hDBC); + result[2] = Nan::New(stmt->m_hSTMT); + result[3] = Nan::New(canFreeHandle); - Local js_result = NanNew(ODBCResult::constructor)->NewInstance(4, result); + Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, result); - NanReturnValue(js_result); + info.GetReturnValue().Set(js_result); } } @@ -577,11 +577,11 @@ NAN_METHOD(ODBCStatement::ExecuteDirectSync) { NAN_METHOD(ODBCStatement::PrepareSync) { DEBUG_PRINTF("ODBCStatement::PrepareSync\n"); - NanScope(); + Nan::HandleScope scope; REQ_STRO_ARG(0, sql); - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); SQLRETURN ret; @@ -603,16 +603,16 @@ NAN_METHOD(ODBCStatement::PrepareSync) { sqlLen); if (SQL_SUCCEEDED(ret)) { - NanReturnValue(NanTrue()); + info.GetReturnValue().Set(Nan::True()); } else { - NanThrowError(ODBC::GetSQLError( + Nan::ThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::PrepareSync" )); - NanReturnValue(NanFalse()); + info.GetReturnValue().Set(Nan::False()); } } @@ -624,19 +624,19 @@ NAN_METHOD(ODBCStatement::PrepareSync) { NAN_METHOD(ODBCStatement::Prepare) { DEBUG_PRINTF("ODBCStatement::Prepare\n"); - NanScope(); + Nan::HandleScope scope; REQ_STRO_ARG(0, sql); REQ_FUN_ARG(1, cb); - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); prepare_work_data* data = (prepare_work_data *) calloc(1, sizeof(prepare_work_data)); - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->sqlLen = sql->Length(); @@ -660,7 +660,7 @@ NAN_METHOD(ODBCStatement::Prepare) { stmt->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCStatement::UV_Prepare(uv_work_t* req) { @@ -695,7 +695,7 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { data->stmt->m_hSTMT ); - NanScope(); + Nan::HandleScope scope; //First thing, let's check if the execution of the query returned any errors if(data->result == SQL_ERROR) { @@ -705,14 +705,14 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { data->cb); } else { - Local args[2]; + Local info[2]; - args[0] = NanNew(NanNull()); - args[1] = NanNew(NanTrue()); + info[0] = Nan::New(Nan::Null()); + info[1] = Nan::New(Nan::True()); TryCatch try_catch; - data->cb->Call( 2, args); + data->cb->Call( 2, info); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -735,13 +735,13 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { NAN_METHOD(ODBCStatement::BindSync) { DEBUG_PRINTF("ODBCStatement::BindSync\n"); - NanScope(); + Nan::HandleScope scope; - if ( !args[0]->IsArray() ) { - return NanThrowTypeError("Argument 1 must be an Array"); + if ( !info[0]->IsArray() ) { + return Nan::ThrowTypeError("Argument 1 must be an Array"); } - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); DEBUG_PRINTF("ODBCStatement::BindSync m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", stmt->m_hENV, @@ -774,7 +774,7 @@ NAN_METHOD(ODBCStatement::BindSync) { } stmt->params = ODBC::GetParametersFromArray( - Local::Cast(args[0]), + Local::Cast(info[0]), &stmt->paramCount); SQLRETURN ret = SQL_SUCCESS; @@ -808,19 +808,19 @@ NAN_METHOD(ODBCStatement::BindSync) { } if (SQL_SUCCEEDED(ret)) { - NanReturnValue(NanTrue()); + info.GetReturnValue().Set(Nan::True()); } else { - NanThrowError(ODBC::GetSQLError( + Nan::ThrowError(ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::BindSync" )); - NanReturnValue(NanFalse()); + info.GetReturnValue().Set(Nan::False()); } - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } /* @@ -831,15 +831,15 @@ NAN_METHOD(ODBCStatement::BindSync) { NAN_METHOD(ODBCStatement::Bind) { DEBUG_PRINTF("ODBCStatement::Bind\n"); - NanScope(); + Nan::HandleScope scope; - if ( !args[0]->IsArray() ) { - return NanThrowError("Argument 1 must be an Array"); + if ( !info[0]->IsArray() ) { + return Nan::ThrowError("Argument 1 must be an Array"); } REQ_FUN_ARG(1, cb); - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); uv_work_t* work_req = (uv_work_t *) (calloc(1, sizeof(uv_work_t))); @@ -878,10 +878,10 @@ NAN_METHOD(ODBCStatement::Bind) { data->stmt->m_hSTMT ); - data->cb = new NanCallback(cb); + data->cb = new Nan::Callback(cb); data->stmt->params = ODBC::GetParametersFromArray( - Local::Cast(args[0]), + Local::Cast(info[0]), &data->stmt->paramCount); work_req->data = data; @@ -894,7 +894,7 @@ NAN_METHOD(ODBCStatement::Bind) { stmt->Ref(); - NanReturnValue(NanUndefined()); + info.GetReturnValue().Set(Nan::Undefined()); } void ODBCStatement::UV_Bind(uv_work_t* req) { @@ -946,7 +946,7 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { bind_work_data* data = (bind_work_data *)(req->data); - NanScope(); + Nan::HandleScope scope; //an easy reference to the statment object ODBCStatement* self = data->stmt->self(); @@ -959,14 +959,14 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { data->cb); } else { - Local args[2]; + Local info[2]; - args[0] = NanNew(NanNull()); - args[1] = NanNew(NanTrue()); + info[0] = Nan::New(Nan::Null()); + info[1] = Nan::New(Nan::True()); TryCatch try_catch; - data->cb->Call( 2, args); + data->cb->Call( 2, info); if (try_catch.HasCaught()) { FatalException(try_catch); @@ -987,11 +987,11 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { NAN_METHOD(ODBCStatement::CloseSync) { DEBUG_PRINTF("ODBCStatement::CloseSync\n"); - NanScope(); + Nan::HandleScope scope; OPT_INT_ARG(0, closeOption, SQL_DESTROY); - ODBCStatement* stmt = ObjectWrap::Unwrap(args.Holder()); + ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); DEBUG_PRINTF("ODBCStatement::CloseSync closeOption=%i\n", closeOption); @@ -1007,5 +1007,5 @@ NAN_METHOD(ODBCStatement::CloseSync) { uv_mutex_unlock(&ODBC::g_odbcMutex); } - NanReturnValue(NanTrue()); + info.GetReturnValue().Set(Nan::True()); } diff --git a/src/odbc_statement.h b/src/odbc_statement.h index 925dc35f..2cdb01df 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -19,9 +19,9 @@ #include -class ODBCStatement : public node::ObjectWrap { +class ODBCStatement : public Nan::ObjectWrap { public: - static Persistent constructor; + static Nan::Persistent constructor; static void Init(v8::Handle exports); void Free(); @@ -30,7 +30,7 @@ class ODBCStatement : public node::ObjectWrap { ODBCStatement() {}; explicit ODBCStatement(HENV hENV, HDBC hDBC, HSTMT hSTMT): - ObjectWrap(), + Nan::ObjectWrap(), m_hENV(hENV), m_hDBC(hDBC), m_hSTMT(hSTMT) {}; @@ -70,7 +70,7 @@ class ODBCStatement : public node::ObjectWrap { static NAN_METHOD(BindSync); struct Fetch_Request { - NanCallback* callback; + Nan::Callback* callback; ODBCStatement *objResult; SQLRETURN result; }; @@ -92,7 +92,7 @@ class ODBCStatement : public node::ObjectWrap { }; struct execute_direct_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBCStatement *stmt; int result; void *sql; @@ -100,13 +100,13 @@ struct execute_direct_work_data { }; struct execute_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBCStatement *stmt; int result; }; struct prepare_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBCStatement *stmt; int result; void *sql; @@ -114,7 +114,7 @@ struct prepare_work_data { }; struct bind_work_data { - NanCallback* cb; + Nan::Callback* cb; ODBCStatement *stmt; int result; }; From 9804520869804173c3f1594c96387f0f69428042 Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Wed, 23 Sep 2015 13:57:10 -0400 Subject: [PATCH 460/511] Updating node dependencies and binding.gyp settings. --- binding.gyp | 3 ++- package.json | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/binding.gyp b/binding.gyp index 77c59a02..3bc47dcf 100644 --- a/binding.gyp +++ b/binding.gyp @@ -9,7 +9,8 @@ 'src/odbc_result.cpp', 'src/dynodbc.cpp' ], - 'include_dirs': [ + 'cflags' : ['-Wall', '-Wextra', '-Wno-unused-parameter'], + 'include_dirs': [ "=0.8.0 <4.0.0" + "node": ">=4.0.0" }, "scripts": { "install": "node-gyp configure build", "test": "cd test && node run-tests.js" }, "dependencies": { - "bindings": "~1.0.0", - "nan": "*1.8.4" + "bindings": "^1.2.1", + "nan": "^2.0.9" }, "gypfile": true } From c2e147ca936f800ae2b44b67be8af44ad3c116cf Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Wed, 23 Sep 2015 15:00:11 -0400 Subject: [PATCH 461/511] Updating odbc.cpp/.h --- src/odbc.cpp | 64 ++++++++++++++++++++++++++-------------------------- src/odbc.h | 12 ++++------ 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index dee2ab8d..8d6b94e5 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -140,7 +140,7 @@ NAN_METHOD(ODBC::New) { Local objError = ODBC::GetSQLError(SQL_HANDLE_ENV, dbo->m_hEnv); - return Nan::ThrowError(objError); + return Nan::ThrowError(objError.As()); } // Use ODBC 3.x behavior @@ -207,7 +207,7 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { create_connection_work_data* data = (create_connection_work_data *)(req->data); - TryCatch try_catch; + Nan::TryCatch try_catch; if (!SQL_SUCCEEDED(data->result)) { Local info[1]; @@ -223,14 +223,14 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { Local js_result = Nan::New(ODBCConnection::constructor)->NewInstance(2, info); - info[0] = Nan::New(Nan::Null()); - info[1] = Nan::New(js_result); + info[0] = Nan::Null().As(); + info[1] = js_result.As(); data->cb->Call(2, info); } if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } @@ -375,7 +375,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, sizeof(value), &len); - DEBUG_PRINTF("ODBC::GetColumnValue - Integer: index=%i name=%s type=%i len=%i ret=%i val=%li\n", + DEBUG_PRINTF("ODBC::GetColumnValue - Integer: index=%i name=%s type=%lli len=%lli ret=%i val=%li\n", column.index, column.name, column.type, len, ret, value); if (len == SQL_NULL_DATA) { @@ -402,7 +402,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, sizeof(value), &len); - DEBUG_PRINTF("ODBC::GetColumnValue - Number: index=%i name=%s type=%i len=%i ret=%i val=%f\n", + DEBUG_PRINTF("ODBC::GetColumnValue - Number: index=%i name=%s type=%lli len=%lli ret=%i val=%f\n", column.index, column.name, column.type, len, ret, value); if (len == SQL_NULL_DATA) { @@ -430,7 +430,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, bufferLength, &len); - DEBUG_PRINTF("ODBC::GetColumnValue - W32 Timestamp: index=%i name=%s type=%i len=%i\n", + DEBUG_PRINTF("ODBC::GetColumnValue - W32 Timestamp: index=%i name=%s type=%lli len=%lli\n", column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { @@ -445,10 +445,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, timeInfo.tm_isdst = -1; //return scope.Escape(Date::New(Isolate::GetCurrent(), (double(mktime(&timeInfo)) * 1000)); - return scope.Escape(Nan::New(double(mktime(&timeInfo)) * 1000)); + return scope.Escape(Nan::New(double(mktime(&timeInfo)) * 1000).ToLocalChecked()); } else { - return scope.Escape(Nan::New((char *)buffer)); + return scope.Escape(Nan::New((char *)buffer).ToLocalChecked()); } } #else @@ -518,7 +518,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, bufferLength, &len); - DEBUG_PRINTF("ODBC::GetColumnValue - Bit: index=%i name=%s type=%i len=%i\n", + DEBUG_PRINTF("ODBC::GetColumnValue - Bit: index=%i name=%s type=%lli len=%lli\n", column.index, column.name, column.type, len); if (len == SQL_NULL_DATA) { @@ -540,7 +540,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, bufferLength, &len); - DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%i len=%i value=%s ret=%i bufferLength=%i\n", + DEBUG_PRINTF("ODBC::GetColumnValue - String: index=%i name=%s type=%lli len=%lli value=%s ret=%i bufferLength=%i\n", column.index, column.name, column.type, len,(char *) buffer, ret, bufferLength); if (len == SQL_NULL_DATA && str.IsEmpty()) { @@ -563,7 +563,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, if (count == 0) { //no concatenation required, this is our first pass #ifdef UNICODE - str = Nan::New((uint16_t*) buffer); + str = Nan::New((uint16_t*) buffer).ToLocalChecked(); #else str = Nan::New((char *) buffer); #endif @@ -571,7 +571,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, else { //we need to concatenate #ifdef UNICODE - str = String::Concat(str, Nan::New((uint16_t*) buffer)); + str = String::Concat(str, Nan::New((uint16_t*) buffer).ToLocalChecked()); #else str = String::Concat(str, Nan::New((char *) buffer)); #endif @@ -603,7 +603,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, SQL_HANDLE_STMT, hStmt, (char *) "[node-odbc] Error in ODBC::GetColumnValue" - )); + ).As()); return scope.Escape(Nan::Undefined()); break; } @@ -626,7 +626,7 @@ Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, for(int i = 0; i < *colCount; i++) { #ifdef UNICODE - tuple->Set( Nan::New((uint16_t *) columns[i].name), + tuple->Set( Nan::New((uint16_t *) columns[i].name).ToLocalChecked(), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); #else tuple->Set( Nan::New((const char *) columns[i].name), @@ -678,8 +678,8 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].BufferLength = 0; params[i].DecimalDigits = 0; - DEBUG_PRINTF("ODBC::GetParametersFromArray - ¶m[%i].length = %X\n", - i, ¶ms[i].StrLen_or_IndPtr); + DEBUG_PRINTF("ODBC::GetParametersFromArray - param[%i].length = %lli\n", + i, params[i].StrLen_or_IndPtr); if (value->IsString()) { Local string = value->ToString(); @@ -703,7 +703,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { string->WriteUtf8((char *) params[i].ParameterValuePtr); #endif - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsString(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i value=%s\n", + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsString(): params[%i] c_type=%i type=%i buffer_length=%lli size=%lli length=%lli value=%s\n", i, params[i].ValueType, params[i].ParameterType, params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, (char*) params[i].ParameterValuePtr); @@ -713,7 +713,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].ParameterType = SQL_VARCHAR; params[i].StrLen_or_IndPtr = SQL_NULL_DATA; - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNull(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNull(): params[%i] c_type=%i type=%i buffer_length=%lli size=%lli length=%lli\n", i, params[i].ValueType, params[i].ParameterType, params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr); } @@ -724,7 +724,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].ParameterValuePtr = number; params[i].StrLen_or_IndPtr = 0; - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsInt32(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i value=%lld\n", + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsInt32(): params[%i] c_type=%i type=%i buffer_length=%lli size=%lli length=%lli value=%lld\n", i, params[i].ValueType, params[i].ParameterType, params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, *number); @@ -740,7 +740,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].DecimalDigits = 7; params[i].ColumnSize = sizeof(double); - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i value=%f\n", + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsNumber(): params[%i] c_type=%i type=%i buffer_length=%lli size=%lli length=%lli value=%f\n", i, params[i].ValueType, params[i].ParameterType, params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr, *number); @@ -752,7 +752,7 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { params[i].ParameterValuePtr = boolean; params[i].StrLen_or_IndPtr = 0; - DEBUG_PRINTF("ODBC::GetParametersFromArray - IsBoolean(): params[%i] c_type=%i type=%i buffer_length=%i size=%i length=%i\n", + DEBUG_PRINTF("ODBC::GetParametersFromArray - IsBoolean(): params[%i] c_type=%i type=%i buffer_length=%lli size=%lli length=%lli\n", i, params[i].ValueType, params[i].ParameterType, params[i].BufferLength, params[i].ColumnSize, params[i].StrLen_or_IndPtr); } @@ -860,11 +860,11 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* if (i == 0) { // First error is assumed the primary error - objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message)); + objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message).ToLocalChecked()); #ifdef UNICODE - objError->SetPrototype(Exception::Error(Nan::New((uint16_t *)errorMessage))); - objError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage)); - objError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState)); + objError->SetPrototype(Exception::Error(Nan::New((uint16_t *)errorMessage).ToLocalChecked())); + objError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage).ToLocalChecked()); + objError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState).ToLocalChecked()); #else objError->SetPrototype(Exception::Error(Nan::New(errorMessage))); objError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage)); @@ -875,8 +875,8 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* Local subError = Nan::New(); #ifdef UNICODE - subError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage)); - subError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState)); + subError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage).ToLocalChecked()); + subError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState).ToLocalChecked()); #else subError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage)); subError->Set(Nan::New("state").ToLocalChecked(), Nan::New(errorSQLState)); @@ -890,10 +890,10 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* if (statusRecCount == 0) { //Create a default error object if there were no diag records - objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message)); - objError->SetPrototype(Exception::Error(Nan::New(message))); + objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message).ToLocalChecked()); + objError->SetPrototype(Exception::Error(Nan::New(message).ToLocalChecked())); objError->Set(Nan::New("message").ToLocalChecked(), Nan::New( - (const char *) "[node-odbc] An error occurred but no diagnostic information was available.")); + (const char *) "[node-odbc] An error occurred but no diagnostic information was available.").ToLocalChecked()); } return scope.Escape(objError); diff --git a/src/odbc.h b/src/odbc.h index b631e82f..0200f95b 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -100,21 +100,24 @@ class ODBC : public Nan::ObjectWrap { ~ODBC(); + public: static NAN_METHOD(New); //async methods static NAN_METHOD(CreateConnection); + protected: static void UV_CreateConnection(uv_work_t* work_req); static void UV_AfterCreateConnection(uv_work_t* work_req, int status); static void WatcherCallback(uv_async_t* w, int revents); //sync methods + public: static NAN_METHOD(CreateConnectionSync); + protected: ODBC *self(void) { return this; } - protected: HENV m_hEnv; }; @@ -161,13 +164,8 @@ struct query_request { #endif #ifdef DEBUG -#ifdef UNICODE -#define DEBUG_PRINTF(...) fwprintf(stdout, L##__VA_ARGS__) -#define DEBUG_TPRINTF(...) fwprintf(stdout, __VA_ARGS__) -#else #define DEBUG_TPRINTF(...) fprintf(stdout, __VA_ARGS__) #define DEBUG_PRINTF(...) fprintf(stdout, __VA_ARGS__) -#endif #else #define DEBUG_PRINTF(...) (void)0 #define DEBUG_TPRINTF(...) (void)0 @@ -243,7 +241,7 @@ struct query_request { // From node v10 NODE_DEFINE_CONSTANT #define NODE_ODBC_DEFINE_CONSTANT(constructor_template, constant) \ - (constructor_template)->Set(Nan::New(#constant), \ + (constructor_template)->Set(Nan::New(#constant).ToLocalChecked(), \ Nan::New(constant), \ static_cast(v8::ReadOnly|v8::DontDelete)) From 10245b2bc0d70f3f826722b083c54811a41b893b Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Wed, 23 Sep 2015 15:18:53 -0400 Subject: [PATCH 462/511] Updating odbc_connection.cpp/.h --- src/odbc_connection.cpp | 30 +++++++++++++++--------------- src/odbc_connection.h | 25 +++++++++++++++++++++---- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 8ab8fe3c..69846be9 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -433,7 +433,7 @@ NAN_METHOD(ODBCConnection::OpenSync) { free(connectionString); if (err) { - return Nan::ThrowError(objError); + return Nan::ThrowError(objError.As()); } else { info.GetReturnValue().Set(Nan::True()); @@ -669,8 +669,8 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { Local js_result = Nan::New(ODBCStatement::constructor)->NewInstance(3, info); - info[0] = Nan::New(Nan::Null()); - info[1] = Nan::New(js_result); + info[0] = Nan::Null().As(); + info[1] = js_result.As(); TryCatch try_catch; @@ -897,8 +897,8 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { uv_mutex_unlock(&ODBC::g_odbcMutex); Local info[2]; - info[0] = Nan::New(Nan::Null()); - info[1] = Nan::New(Nan::True()); + info[0] = Nan::Null().As(); + info[1] = Nan::True().As(); data->cb->Call(2, info); } @@ -917,9 +917,9 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { if (data->result == SQL_ERROR) { info[0] = ODBC::GetSQLError(SQL_HANDLE_STMT, data->hSTMT, (char *) "[node-odbc] SQL_ERROR"); } else { - info[0] = Nan::New(Nan::Null()); + info[0] = Nan::Null().As(); } - info[1] = Nan::New(js_result); + info[1] = js_result.As(); data->cb->Call(2, info); } @@ -1027,7 +1027,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { Local obj = info[0]->ToObject(); - Local optionSqlKey = Nan::New(OPTION_SQL).ToLocalChecked(); + Local optionSqlKey = Nan::New(OPTION_SQL); if (obj->Has(optionSqlKey) && obj->Get(optionSqlKey)->IsString()) { #ifdef UNICODE sql = new String::Value(obj->Get(optionSqlKey)->ToString()); @@ -1134,7 +1134,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { SQL_HANDLE_STMT, hSTMT, (char *) "[node-odbc] Error in ODBCConnection::QuerySync" - )); + ).As()); return; } @@ -1185,7 +1185,7 @@ NAN_METHOD(ODBCConnection::Tables) { (query_work_data *) calloc(1, sizeof(query_work_data)); if (!data) { - NanLowMemoryNotification(); + Nan::LowMemoryNotification(); Nan::ThrowError("Could not allocate enough memory"); return; } @@ -1296,7 +1296,7 @@ NAN_METHOD(ODBCConnection::Columns) { query_work_data* data = (query_work_data *) calloc(1, sizeof(query_work_data)); if (!data) { - NanLowMemoryNotification(); + Nan::LowMemoryNotification(); Nan::ThrowError("Could not allocate enough memory"); return; } @@ -1407,7 +1407,7 @@ NAN_METHOD(ODBCConnection::BeginTransactionSync) { if (!SQL_SUCCEEDED(ret)) { Local objError = ODBC::GetSQLError(SQL_HANDLE_DBC, conn->m_hDBC); - Nan::ThrowError(objError); + Nan::ThrowError(objError.As()); info.GetReturnValue().Set(Nan::False()); } @@ -1434,7 +1434,7 @@ NAN_METHOD(ODBCConnection::BeginTransaction) { (query_work_data *) calloc(1, sizeof(query_work_data)); if (!data) { - NanLowMemoryNotification(); + Nan::LowMemoryNotification(); return Nan::ThrowError("Could not allocate enough memory"); } @@ -1561,7 +1561,7 @@ NAN_METHOD(ODBCConnection::EndTransactionSync) { } if (error) { - Nan::ThrowError(objError); + Nan::ThrowError(objError.As()); info.GetReturnValue().Set(Nan::False()); } @@ -1590,7 +1590,7 @@ NAN_METHOD(ODBCConnection::EndTransaction) { (query_work_data *) calloc(1, sizeof(query_work_data)); if (!data) { - NanLowMemoryNotification(); + Nan::LowMemoryNotification(); return Nan::ThrowError("Could not allocate enough memory"); } diff --git a/src/odbc_connection.h b/src/odbc_connection.h index 1fdd6ce6..a5131df0 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -41,6 +41,7 @@ class ODBCConnection : public Nan::ObjectWrap { ~ODBCConnection(); +public: //constructor static NAN_METHOD(New); @@ -53,44 +54,60 @@ class ODBCConnection : public Nan::ObjectWrap { //async methods static NAN_METHOD(BeginTransaction); +protected: static void UV_BeginTransaction(uv_work_t* work_req); static void UV_AfterBeginTransaction(uv_work_t* work_req, int status); - + +public: static NAN_METHOD(EndTransaction); +protected: static void UV_EndTransaction(uv_work_t* work_req); static void UV_AfterEndTransaction(uv_work_t* work_req, int status); - +public: static NAN_METHOD(Open); +protected: static void UV_Open(uv_work_t* work_req); static void UV_AfterOpen(uv_work_t* work_req, int status); +public: static NAN_METHOD(Close); +protected: static void UV_Close(uv_work_t* work_req); static void UV_AfterClose(uv_work_t* work_req, int status); +public: static NAN_METHOD(CreateStatement); +protected: static void UV_CreateStatement(uv_work_t* work_req); static void UV_AfterCreateStatement(uv_work_t* work_req, int status); +public: static NAN_METHOD(Query); +protected: static void UV_Query(uv_work_t* req); static void UV_AfterQuery(uv_work_t* req, int status); +public: static NAN_METHOD(Columns); +protected: static void UV_Columns(uv_work_t* req); - + +public: static NAN_METHOD(Tables); +protected: static void UV_Tables(uv_work_t* req); //sync methods +public: static NAN_METHOD(CloseSync); static NAN_METHOD(CreateStatementSync); static NAN_METHOD(OpenSync); static NAN_METHOD(QuerySync); static NAN_METHOD(BeginTransactionSync); static NAN_METHOD(EndTransactionSync); - +protected: + struct Fetch_Request { Nan::Callback* callback; ODBCConnection *objResult; From 96f80af709f4a80508a097ee8ef2180bb69cc359 Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Wed, 23 Sep 2015 15:30:49 -0400 Subject: [PATCH 463/511] Updating odbc_result.cpp/.h --- src/odbc_result.cpp | 27 ++++++++++++++------------- src/odbc_result.h | 8 +++++++- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 224fc887..9ff5c605 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -179,7 +179,7 @@ NAN_METHOD(ODBCResult::Fetch) { Local obj = info[0]->ToObject(); - Local fetchModeKey = Nan::New(OPTION_FETCH_MODE).ToLocalChecked(); + Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } @@ -332,7 +332,7 @@ NAN_METHOD(ODBCResult::FetchSync) { if (info.Length() == 1 && info[0]->IsObject()) { Local obj = info[0]->ToObject(); - Local fetchModeKey = Nan::New(OPTION_FETCH_MODE).ToLocalChecked(); + Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } @@ -392,7 +392,7 @@ NAN_METHOD(ODBCResult::FetchSync) { //if there was an error, pass that as arg[0] otherwise Null if (error) { - Nan::ThrowError(objError); + Nan::ThrowError(objError.As()); info.GetReturnValue().Set(Nan::Null()); } @@ -428,7 +428,7 @@ NAN_METHOD(ODBCResult::FetchAll) { Local obj = info[0]->ToObject(); - Local fetchModeKey = Nan::New(OPTION_FETCH_MODE).ToLocalChecked(); + Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } @@ -488,11 +488,12 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { //check to see if there was an error else if (data->result == SQL_ERROR) { data->errorCount++; - - NanAssignPersistent(data->objError, ODBC::GetSQLError( - SQL_HANDLE_STMT, - self->m_hSTMT, - (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll" + + //NanAssignPersistent(data->objError, ODBC::GetSQLError( + data->objError.Reset(ODBC::GetSQLError( + SQL_HANDLE_STMT, + self->m_hSTMT, + (char *) "[node-odbc] Error in ODBCResult::UV_AfterFetchAll" )); doMoreWork = false; @@ -588,7 +589,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { if (info.Length() == 1 && info[0]->IsObject()) { Local obj = info[0]->ToObject(); - Local fetchModeKey = Nan::New(OPTION_FETCH_MODE).ToLocalChecked(); + Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); } @@ -658,7 +659,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { //throw the error object if there were errors if (errorCount > 0) { - Nan::ThrowError(objError); + Nan::ThrowError(objError.As()); } info.GetReturnValue().Set(rows); @@ -711,7 +712,7 @@ NAN_METHOD(ODBCResult::MoreResultsSync) { SQLRETURN ret = SQLMoreResults(result->m_hSTMT); if (ret == SQL_ERROR) { - Nan::ThrowError(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync")); + Nan::ThrowError(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync").As()); } info.GetReturnValue().Set(SQL_SUCCEEDED(ret) || ret == SQL_ERROR ? Nan::True() : Nan::False()); @@ -735,7 +736,7 @@ NAN_METHOD(ODBCResult::GetColumnNamesSync) { for (int i = 0; i < self->colCount; i++) { cols->Set(Nan::New(i), - Nan::New((const char *) self->columns[i].name)); + Nan::New((const char *) self->columns[i].name).ToLocalChecked()); } info.GetReturnValue().Set(cols); diff --git a/src/odbc_result.h b/src/odbc_result.h index e9bc1052..b75778ae 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -40,18 +40,23 @@ class ODBCResult : public Nan::ObjectWrap { ~ODBCResult(); //constructor +public: static NAN_METHOD(New); //async methods static NAN_METHOD(Fetch); +protected: static void UV_Fetch(uv_work_t* work_req); static void UV_AfterFetch(uv_work_t* work_req, int status); +public: static NAN_METHOD(FetchAll); +protected: static void UV_FetchAll(uv_work_t* work_req); static void UV_AfterFetchAll(uv_work_t* work_req, int status); //sync methods +public: static NAN_METHOD(CloseSync); static NAN_METHOD(MoreResultsSync); static NAN_METHOD(FetchSync); @@ -61,7 +66,8 @@ class ODBCResult : public Nan::ObjectWrap { //property getter/setters static NAN_GETTER(FetchModeGetter); static NAN_SETTER(FetchModeSetter); - + +protected: struct fetch_work_data { Nan::Callback* cb; ODBCResult *objResult; From bf2313207ee6d34d034a227fb94ff42c8e972fc4 Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Wed, 23 Sep 2015 15:37:02 -0400 Subject: [PATCH 464/511] Updating odbc_statement.cpp/.h --- src/odbc_statement.cpp | 30 +++++++++++++++--------------- src/odbc_statement.h | 16 ++++++++++++++-- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 37395b0f..3874b44e 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -218,8 +218,8 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); - info[0] = Nan::New(Nan::Null()); - info[1] = Nan::New(js_result); + info[0] = Nan::Null().As(); + info[1] = js_result.As(); TryCatch try_catch; @@ -256,7 +256,7 @@ NAN_METHOD(ODBCStatement::ExecuteSync) { SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" - )); + ).As()); info.GetReturnValue().Set(Nan::Null()); } @@ -353,9 +353,9 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { Local info[2]; - info[0] = Nan::New(Nan::Null()); + info[0] = Nan::Null().As(); // We get a potential loss of precision here. Number isn't as big as int64. Probably fine though. - info[1] = Nan::New(Nan::New(rowCount)); + info[1] = Nan::New(rowCount).As(); TryCatch try_catch; @@ -392,7 +392,7 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuerySync) { SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" - )); + ).As()); info.GetReturnValue().Set(Nan::Null()); } @@ -502,8 +502,8 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); - info[0] = Nan::New(Nan::Null()); - info[1] = Nan::New(js_result); + info[0] = Nan::Null().As(); + info[1] = js_result.As(); TryCatch try_catch; @@ -550,7 +550,7 @@ NAN_METHOD(ODBCStatement::ExecuteDirectSync) { SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteDirectSync" - )); + ).As()); info.GetReturnValue().Set(Nan::Null()); } @@ -610,7 +610,7 @@ NAN_METHOD(ODBCStatement::PrepareSync) { SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::PrepareSync" - )); + ).As()); info.GetReturnValue().Set(Nan::False()); } @@ -707,8 +707,8 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { else { Local info[2]; - info[0] = Nan::New(Nan::Null()); - info[1] = Nan::New(Nan::True()); + info[0] = Nan::Null().As(); + info[1] = Nan::True().As(); TryCatch try_catch; @@ -815,7 +815,7 @@ NAN_METHOD(ODBCStatement::BindSync) { SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::BindSync" - )); + ).As()); info.GetReturnValue().Set(Nan::False()); } @@ -961,8 +961,8 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { else { Local info[2]; - info[0] = Nan::New(Nan::Null()); - info[1] = Nan::New(Nan::True()); + info[0] = Nan::Null().As(); + info[1] = Nan::True().As(); TryCatch try_catch; diff --git a/src/odbc_statement.h b/src/odbc_statement.h index 2cdb01df..0aa5afa3 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -38,37 +38,49 @@ class ODBCStatement : public Nan::ObjectWrap { ~ODBCStatement(); //constructor +public: static NAN_METHOD(New); //async methods static NAN_METHOD(Execute); +protected: static void UV_Execute(uv_work_t* work_req); static void UV_AfterExecute(uv_work_t* work_req, int status); +public: static NAN_METHOD(ExecuteDirect); +protected: static void UV_ExecuteDirect(uv_work_t* work_req); static void UV_AfterExecuteDirect(uv_work_t* work_req, int status); +public: static NAN_METHOD(ExecuteNonQuery); +protected: static void UV_ExecuteNonQuery(uv_work_t* work_req); static void UV_AfterExecuteNonQuery(uv_work_t* work_req, int status); +public: static NAN_METHOD(Prepare); +protected: static void UV_Prepare(uv_work_t* work_req); static void UV_AfterPrepare(uv_work_t* work_req, int status); - + +public: static NAN_METHOD(Bind); +protected: static void UV_Bind(uv_work_t* work_req); static void UV_AfterBind(uv_work_t* work_req, int status); //sync methods +public: static NAN_METHOD(CloseSync); static NAN_METHOD(ExecuteSync); static NAN_METHOD(ExecuteDirectSync); static NAN_METHOD(ExecuteNonQuerySync); static NAN_METHOD(PrepareSync); static NAN_METHOD(BindSync); - +protected: + struct Fetch_Request { Nan::Callback* callback; ODBCStatement *objResult; From 58b6a2de32a6ce6332654537f4086a2b7178f38b Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Wed, 23 Sep 2015 16:28:12 -0400 Subject: [PATCH 465/511] Addressing other compiler issues. --- src/odbc.cpp | 2 +- src/odbc_connection.cpp | 61 +++++++++++++++++---------------- src/odbc_result.cpp | 33 +++++++++--------- src/odbc_statement.cpp | 75 ++++++++++++++++++++++------------------- 4 files changed, 91 insertions(+), 80 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 8d6b94e5..e5e56c6b 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -846,7 +846,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* ret = SQLGetDiagRec( handleType, handle, - i + 1, + (SQLSMALLINT)(i + 1), (SQLTCHAR *) errorSQLState, &native, (SQLTCHAR *) errorMessage, diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 69846be9..17a4e9ff 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -316,13 +316,13 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { //#endif } - TryCatch try_catch; + Nan::TryCatch try_catch; data->conn->Unref(); data->cb->Call(err ? 1 : 0, argv); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } delete data->cb; @@ -513,13 +513,13 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { //#endif } - TryCatch try_catch; + Nan::TryCatch try_catch; data->conn->Unref(); data->cb->Call(err ? 1 : 0, argv); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } delete data->cb; @@ -628,11 +628,12 @@ void ODBCConnection::UV_CreateStatement(uv_work_t* req) { //get our work data create_statement_work_data* data = (create_statement_work_data *)(req->data); - DEBUG_PRINTF("ODBCConnection::UV_CreateStatement m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", - data->conn->m_hENV, - data->conn->m_hDBC, - data->hSTMT - ); + DEBUG_PRINTF("ODBCConnection::UV_CreateStatement\n"); + //DEBUG_PRINTF("ODBCConnection::UV_CreateStatement m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + // data->conn->m_hENV, + // data->conn->m_hDBC, + // data->hSTMT + //); uv_mutex_lock(&ODBC::g_odbcMutex); @@ -643,11 +644,12 @@ void ODBCConnection::UV_CreateStatement(uv_work_t* req) { uv_mutex_unlock(&ODBC::g_odbcMutex); - DEBUG_PRINTF("ODBCConnection::UV_CreateStatement m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", - data->conn->m_hENV, - data->conn->m_hDBC, - data->hSTMT - ); + DEBUG_PRINTF("ODBCConnection::UV_CreateStatement\n"); + //DEBUG_PRINTF("ODBCConnection::UV_CreateStatement m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + // data->conn->m_hENV, + // data->conn->m_hDBC, + // data->hSTMT + //); } void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { @@ -656,11 +658,12 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { create_statement_work_data* data = (create_statement_work_data *)(req->data); - DEBUG_PRINTF("ODBCConnection::UV_AfterCreateStatement m_hDBC=%X m_hDBC=%X hSTMT=%X\n", - data->conn->m_hENV, - data->conn->m_hDBC, - data->hSTMT - ); + DEBUG_PRINTF("ODBCConnection::UV_AfterCreateStatement\n"); + //DEBUG_PRINTF("ODBCConnection::UV_AfterCreateStatement m_hDBC=%X m_hDBC=%X hSTMT=%X\n", + // data->conn->m_hENV, + // data->conn->m_hDBC, + // data->hSTMT + //); Local info[3]; info[0] = Nan::New(data->conn->m_hENV); @@ -673,12 +676,12 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { info[1] = js_result.As(); - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call( 2, info); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } data->conn->Unref(); @@ -881,7 +884,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { query_work_data* data = (query_work_data *)(req->data); - TryCatch try_catch; + Nan::TryCatch try_catch; DEBUG_PRINTF("ODBCConnection::UV_AfterQuery : data->result=%i, data->noResultObject=%i\n", data->result, data->noResultObject); @@ -927,7 +930,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { data->conn->Unref(); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } delete data->cb; @@ -1084,8 +1087,8 @@ NAN_METHOD(ODBCConnection::QuerySync) { prm = params[i]; DEBUG_PRINTF( - "ODBCConnection::UV_Query - param[%i]: ValueType=%i type=%i BufferLength=%i size=%i length=%i &length=%X\n", i, prm.ValueType, prm.ParameterType, - prm.BufferLength, prm.ColumnSize, prm.StrLen_or_IndPtr, ¶ms[i].StrLen_or_IndPtr); + "ODBCConnection::UV_Query - param[%i]: ValueType=%i type=%i BufferLength=%lli size=%lli length=%lli &length=%lli\n", i, prm.ValueType, prm.ParameterType, + prm.BufferLength, prm.ColumnSize, prm.StrLen_or_IndPtr, params[i].StrLen_or_IndPtr); ret = SQLBindParameter( hSTMT, //StatementHandle @@ -1493,12 +1496,12 @@ void ODBCConnection::UV_AfterBeginTransaction(uv_work_t* req, int status) { argv[0] = objError; } - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call( err ? 1 : 0, argv); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } delete data->cb; @@ -1673,12 +1676,12 @@ void ODBCConnection::UV_AfterEndTransaction(uv_work_t* req, int status) { argv[0] = objError; } - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call(err ? 1 : 0, argv); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } delete data->cb; diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 9ff5c605..a6ae1d33 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -66,13 +66,15 @@ void ODBCResult::Init(v8::Handle exports) { } ODBCResult::~ODBCResult() { - DEBUG_PRINTF("ODBCResult::~ODBCResult m_hSTMT=%x\n", m_hSTMT); + DEBUG_PRINTF("ODBCResult::~ODBCResult\n"); + //DEBUG_PRINTF("ODBCResult::~ODBCResult m_hSTMT=%x\n", m_hSTMT); this->Free(); } void ODBCResult::Free() { - DEBUG_PRINTF("ODBCResult::Free m_hSTMT=%X m_canFreeHandle=%X\n", m_hSTMT, m_canFreeHandle); - + DEBUG_PRINTF("ODBCResult::Free\n"); + //DEBUG_PRINTF("ODBCResult::Free m_hSTMT=%X m_canFreeHandle=%X\n", m_hSTMT, m_canFreeHandle); + if (m_hSTMT && m_canFreeHandle) { uv_mutex_lock(&ODBC::g_odbcMutex); @@ -106,12 +108,13 @@ NAN_METHOD(ODBCResult::New) { //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 - ); + DEBUG_PRINTF("ODBCResult::New\n"); + //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; @@ -272,13 +275,13 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { data->objResult->bufferLength); } - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call(2, info); delete data->cb; if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } } else { @@ -296,13 +299,13 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { info[1] = Nan::Null(); - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call(2, info); delete data->cb; if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } } @@ -551,7 +554,7 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { info[1] = Nan::New(data->rows); - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call(2, info); delete data->cb; @@ -559,7 +562,7 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { data->objError.Reset(); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } free(data); diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 3874b44e..7e2a8a16 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -221,12 +221,12 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { info[0] = Nan::Null().As(); info[1] = js_result.As(); - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call(2, info); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } } @@ -357,12 +357,12 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { // We get a potential loss of precision here. Number isn't as big as int64. Probably fine though. info[1] = Nan::New(rowCount).As(); - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call(2, info); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } } @@ -505,12 +505,12 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { info[0] = Nan::Null().As(); info[1] = js_result.As(); - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call(2, info); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } } @@ -668,11 +668,12 @@ void ODBCStatement::UV_Prepare(uv_work_t* req) { prepare_work_data* data = (prepare_work_data *)(req->data); - DEBUG_PRINTF("ODBCStatement::UV_Prepare m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", - data->stmt->m_hENV, - data->stmt->m_hDBC, - data->stmt->m_hSTMT - ); + DEBUG_PRINTF("ODBCStatement::UV_Prepare\n"); + //DEBUG_PRINTF("ODBCStatement::UV_Prepare m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + // data->stmt->m_hENV, + // data->stmt->m_hDBC, + // data->stmt->m_hSTMT + //); SQLRETURN ret; @@ -689,11 +690,12 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { prepare_work_data* data = (prepare_work_data *)(req->data); - DEBUG_PRINTF("ODBCStatement::UV_AfterPrepare m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", - data->stmt->m_hENV, - data->stmt->m_hDBC, - data->stmt->m_hSTMT - ); + DEBUG_PRINTF("ODBCStatement::UV_AfterPrepare\n"); + //DEBUG_PRINTF("ODBCStatement::UV_AfterPrepare m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + // data->stmt->m_hENV, + // data->stmt->m_hDBC, + // data->stmt->m_hSTMT + //); Nan::HandleScope scope; @@ -710,12 +712,12 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { info[0] = Nan::Null().As(); info[1] = Nan::True().As(); - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call( 2, info); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } } @@ -743,11 +745,12 @@ NAN_METHOD(ODBCStatement::BindSync) { ODBCStatement* stmt = Nan::ObjectWrap::Unwrap(info.Holder()); - DEBUG_PRINTF("ODBCStatement::BindSync m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", - stmt->m_hENV, - stmt->m_hDBC, - stmt->m_hSTMT - ); + DEBUG_PRINTF("ODBCStatement::BindSync\n"); + //DEBUG_PRINTF("ODBCStatement::BindSync m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + // stmt->m_hENV, + // stmt->m_hDBC, + // stmt->m_hSTMT + //); //if we previously had parameters, then be sure to free them //before allocating more @@ -872,11 +875,12 @@ NAN_METHOD(ODBCStatement::Bind) { data->stmt = stmt; - DEBUG_PRINTF("ODBCStatement::Bind m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", - data->stmt->m_hENV, - data->stmt->m_hDBC, - data->stmt->m_hSTMT - ); + DEBUG_PRINTF("ODBCStatement::Bind\n"); + //DEBUG_PRINTF("ODBCStatement::Bind m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + // data->stmt->m_hENV, + // data->stmt->m_hDBC, + // data->stmt->m_hSTMT + //); data->cb = new Nan::Callback(cb); @@ -902,11 +906,12 @@ void ODBCStatement::UV_Bind(uv_work_t* req) { bind_work_data* data = (bind_work_data *)(req->data); - DEBUG_PRINTF("ODBCStatement::UV_Bind m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", - data->stmt->m_hENV, - data->stmt->m_hDBC, - data->stmt->m_hSTMT - ); + DEBUG_PRINTF("ODBCStatement::UV_Bind\n"); + //DEBUG_PRINTF("ODBCStatement::UV_Bind m_hDBC=%X m_hDBC=%X m_hSTMT=%X\n", + // data->stmt->m_hENV, + // data->stmt->m_hDBC, + // data->stmt->m_hSTMT + //); SQLRETURN ret = SQL_SUCCESS; Parameter prm; @@ -964,12 +969,12 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { info[0] = Nan::Null().As(); info[1] = Nan::True().As(); - TryCatch try_catch; + Nan::TryCatch try_catch; data->cb->Call( 2, info); if (try_catch.HasCaught()) { - FatalException(try_catch); + Nan::FatalException(try_catch); } } From f25be7f2d263de6b3d9e1e65a47df9182392dedc Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 23 Sep 2015 22:02:34 -0400 Subject: [PATCH 466/511] Fixes for node@0.12.7 and nan@2.0.9 --- src/odbc.cpp | 6 +++--- src/odbc.h | 2 +- src/odbc_result.cpp | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index e5e56c6b..f360c670 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -497,10 +497,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, timeInfo.tm_isdst = -1; #ifdef TIMEGM return scope.Escape(Nan::New((double(timegm(&timeInfo)) * 1000) - + (odbcTime.fraction / 1000000))); + + (odbcTime.fraction / 1000000)).ToLocalChecked()); #else return scope.Escape(Nan::New((double(timelocal(&timeInfo)) * 1000) - + (odbcTime.fraction / 1000000))); + + (odbcTime.fraction / 1000000)).ToLocalChecked()); #endif //return Date::New((double(timegm(&timeInfo)) * 1000) // + (odbcTime.fraction / 1000000)); @@ -777,7 +777,7 @@ Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, cb)); } -Handle ODBC::CallbackSQLError (SQLSMALLINT handleType, +Local ODBC::CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, Nan::Callback* cb) { diff --git a/src/odbc.h b/src/odbc.h index 0200f95b..82bd1be9 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -84,7 +84,7 @@ class ODBC : public Nan::ObjectWrap { static Local GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle CallbackSQLError(SQLSMALLINT handleType, SQLHANDLE handle, Nan::Callback* cb); - static Handle CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, Nan::Callback* cb); + static Local CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, Nan::Callback* cb); static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle); static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message); static Local GetAllRecordsSync (HENV hENV, HDBC hDBC, HSTMT hSTMT, uint16_t* buffer, int bufferLength); diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index a6ae1d33..4011cbed 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -255,7 +255,7 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { } if (moreWork) { - Handle info[2]; + Local info[2]; info[0] = Nan::Null(); if (data->fetchMode == FETCH_ARRAY) { @@ -287,7 +287,7 @@ void ODBCResult::UV_AfterFetch(uv_work_t* work_req, int status) { else { ODBC::FreeColumns(data->objResult->columns, &data->objResult->colCount); - Handle info[2]; + Local info[2]; //if there was an error, pass that as arg[0] otherwise Null if (error) { @@ -369,7 +369,7 @@ NAN_METHOD(ODBCResult::FetchSync) { } if (moreWork) { - Handle data; + Local data; if (fetchMode == FETCH_ARRAY) { data = ODBC::GetRecordArray( @@ -543,7 +543,7 @@ void ODBCResult::UV_AfterFetchAll(uv_work_t* work_req, int status) { else { ODBC::FreeColumns(self->columns, &self->colCount); - Handle info[2]; + Local info[2]; if (data->errorCount > 0) { info[0] = Nan::New(data->objError); From 76015bff4a85001ede1944766268e5ee621c8ec9 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 23 Sep 2015 22:45:39 -0400 Subject: [PATCH 467/511] Fixes for node v0.10.40 and v0.8.28 --- src/odbc.cpp | 27 +++++++++++---------- src/odbc.h | 4 ++-- src/odbc_connection.cpp | 32 +++++++++++++------------ src/odbc_result.cpp | 16 +++++++++---- src/odbc_statement.cpp | 52 ++++++++++++++++++++++++----------------- 5 files changed, 76 insertions(+), 55 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index f360c670..e9f883a2 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -138,9 +138,9 @@ NAN_METHOD(ODBC::New) { if (!SQL_SUCCEEDED(ret)) { DEBUG_PRINTF("ODBC::New - ERROR ALLOCATING ENV HANDLE!!\n"); - Local objError = ODBC::GetSQLError(SQL_HANDLE_ENV, dbo->m_hEnv); + Local objError = ODBC::GetSQLError(SQL_HANDLE_ENV, dbo->m_hEnv); - return Nan::ThrowError(objError.As()); + return Nan::ThrowError(objError); } // Use ODBC 3.x behavior @@ -221,10 +221,10 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { info[0] = Nan::New(data->dbo->m_hEnv); info[1] = Nan::New(data->hDBC); - Local js_result = Nan::New(ODBCConnection::constructor)->NewInstance(2, info); + Local js_result = Nan::New(ODBCConnection::constructor)->NewInstance(2, info); - info[0] = Nan::Null().As(); - info[1] = js_result.As(); + info[0] = Nan::Null(); + info[1] = js_result; data->cb->Call(2, info); } @@ -599,11 +599,14 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, //Not sure if throwing here will work out well for us but we can try //since we should have a valid handle and the error is something we //can look into - Nan::ThrowError(ODBC::GetSQLError( - SQL_HANDLE_STMT, - hStmt, - (char *) "[node-odbc] Error in ODBC::GetColumnValue" - ).As()); + + Local objError = ODBC::GetSQLError( + SQL_HANDLE_STMT, + hStmt, + (char *) "[node-odbc] Error in ODBC::GetColumnValue" + ); + + Nan::ThrowError(objError); return scope.Escape(Nan::Undefined()); break; } @@ -617,7 +620,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, * GetRecordTuple */ -Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, +Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { Nan::EscapableHandleScope scope; @@ -641,7 +644,7 @@ Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, * GetRecordArray */ -Handle ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, +Local ODBC::GetRecordArray ( SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength) { Nan::EscapableHandleScope scope; diff --git a/src/odbc.h b/src/odbc.h index 82bd1be9..52663057 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -81,8 +81,8 @@ class ODBC : public Nan::ObjectWrap { static Column* GetColumns(SQLHSTMT hStmt, short* colCount); static void FreeColumns(Column* columns, short* colCount); static Handle GetColumnValue(SQLHSTMT hStmt, Column column, uint16_t* buffer, int bufferLength); - static Local GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); - static Handle GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); + static Local GetRecordTuple (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); + static Local GetRecordArray (SQLHSTMT hStmt, Column* columns, short* colCount, uint16_t* buffer, int bufferLength); static Handle CallbackSQLError(SQLSMALLINT handleType, SQLHANDLE handle, Nan::Callback* cb); static Local CallbackSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* message, Nan::Callback* cb); static Local GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 17a4e9ff..5947c94b 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -347,7 +347,7 @@ NAN_METHOD(ODBCConnection::OpenSync) { DEBUG_PRINTF("ODBCConnection::OpenSync : connectTimeout=%i, loginTimeout = %i\n", *&(conn->connectTimeout), *&(conn->loginTimeout)); - Local objError; + Local objError; SQLRETURN ret; bool err = false; @@ -433,7 +433,7 @@ NAN_METHOD(ODBCConnection::OpenSync) { free(connectionString); if (err) { - return Nan::ThrowError(objError.As()); + return Nan::ThrowError(objError); } else { info.GetReturnValue().Set(Nan::True()); @@ -670,10 +670,10 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { info[1] = Nan::New(data->conn->m_hDBC); info[2] = Nan::New(data->hSTMT); - Local js_result = Nan::New(ODBCStatement::constructor)->NewInstance(3, info); + Local js_result = Nan::New(ODBCStatement::constructor)->NewInstance(3, info); - info[0] = Nan::Null().As(); - info[1] = js_result.As(); + info[0] = Nan::Null(); + info[1] = js_result; Nan::TryCatch try_catch; @@ -900,8 +900,8 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { uv_mutex_unlock(&ODBC::g_odbcMutex); Local info[2]; - info[0] = Nan::Null().As(); - info[1] = Nan::True().As(); + info[0] = Nan::Null(); + info[1] = Nan::True(); data->cb->Call(2, info); } @@ -920,9 +920,9 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { if (data->result == SQL_ERROR) { info[0] = ODBC::GetSQLError(SQL_HANDLE_STMT, data->hSTMT, (char *) "[node-odbc] SQL_ERROR"); } else { - info[0] = Nan::Null().As(); + info[0] = Nan::Null(); } - info[1] = js_result.As(); + info[1] = js_result; data->cb->Call(2, info); } @@ -1133,11 +1133,13 @@ NAN_METHOD(ODBCConnection::QuerySync) { //check to see if there was an error during execution if (ret == SQL_ERROR) { - Nan::ThrowError(ODBC::GetSQLError( + Local objError = ODBC::GetSQLError( SQL_HANDLE_STMT, hSTMT, (char *) "[node-odbc] Error in ODBCConnection::QuerySync" - ).As()); + ); + + Nan::ThrowError(objError); return; } @@ -1408,9 +1410,9 @@ NAN_METHOD(ODBCConnection::BeginTransactionSync) { SQL_NTS); if (!SQL_SUCCEEDED(ret)) { - Local objError = ODBC::GetSQLError(SQL_HANDLE_DBC, conn->m_hDBC); + Local objError = ODBC::GetSQLError(SQL_HANDLE_DBC, conn->m_hDBC); - Nan::ThrowError(objError.As()); + Nan::ThrowError(objError); info.GetReturnValue().Set(Nan::False()); } @@ -1523,7 +1525,7 @@ NAN_METHOD(ODBCConnection::EndTransactionSync) { REQ_BOOL_ARG(0, rollback); - Local objError; + Local objError; SQLRETURN ret; bool error = false; SQLSMALLINT completionType = (rollback->Value()) @@ -1564,7 +1566,7 @@ NAN_METHOD(ODBCConnection::EndTransactionSync) { } if (error) { - Nan::ThrowError(objError.As()); + Nan::ThrowError(objError); info.GetReturnValue().Set(Nan::False()); } diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 4011cbed..c71c7a24 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -327,7 +327,7 @@ NAN_METHOD(ODBCResult::FetchSync) { ODBCResult* objResult = Nan::ObjectWrap::Unwrap(info.Holder()); - Local objError; + Local objError; bool moreWork = true; bool error = false; int fetchMode = objResult->m_fetchMode; @@ -395,7 +395,7 @@ NAN_METHOD(ODBCResult::FetchSync) { //if there was an error, pass that as arg[0] otherwise Null if (error) { - Nan::ThrowError(objError.As()); + Nan::ThrowError(objError); info.GetReturnValue().Set(Nan::Null()); } @@ -582,7 +582,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { ODBCResult* self = Nan::ObjectWrap::Unwrap(info.Holder()); - Local objError = Nan::New(); + Local objError = Nan::New(); SQLRETURN ret; int count = 0; @@ -662,7 +662,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { //throw the error object if there were errors if (errorCount > 0) { - Nan::ThrowError(objError.As()); + Nan::ThrowError(objError); } info.GetReturnValue().Set(rows); @@ -715,7 +715,13 @@ NAN_METHOD(ODBCResult::MoreResultsSync) { SQLRETURN ret = SQLMoreResults(result->m_hSTMT); if (ret == SQL_ERROR) { - Nan::ThrowError(ODBC::GetSQLError(SQL_HANDLE_STMT, result->m_hSTMT, (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync").As()); + Local objError = ODBC::GetSQLError( + SQL_HANDLE_STMT, + result->m_hSTMT, + (char *)"[node-odbc] Error in ODBCResult::MoreResultsSync" + ); + + Nan::ThrowError(objError); } info.GetReturnValue().Set(SQL_SUCCEEDED(ret) || ret == SQL_ERROR ? Nan::True() : Nan::False()); diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 7e2a8a16..19623184 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -216,10 +216,10 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { info[2] = Nan::New(self->m_hSTMT); info[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); + Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); - info[0] = Nan::Null().As(); - info[1] = js_result.As(); + info[0] = Nan::Null(); + info[1] = js_result; Nan::TryCatch try_catch; @@ -252,11 +252,13 @@ NAN_METHOD(ODBCStatement::ExecuteSync) { SQLRETURN ret = SQLExecute(stmt->m_hSTMT); if(ret == SQL_ERROR) { - Nan::ThrowError(ODBC::GetSQLError( + Local objError = ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" - ).As()); + ); + + Nan::ThrowError(objError); info.GetReturnValue().Set(Nan::Null()); } @@ -353,9 +355,9 @@ void ODBCStatement::UV_AfterExecuteNonQuery(uv_work_t* req, int status) { Local info[2]; - info[0] = Nan::Null().As(); + info[0] = Nan::Null(); // We get a potential loss of precision here. Number isn't as big as int64. Probably fine though. - info[1] = Nan::New(rowCount).As(); + info[1] = Nan::New(rowCount); Nan::TryCatch try_catch; @@ -388,11 +390,13 @@ NAN_METHOD(ODBCStatement::ExecuteNonQuerySync) { SQLRETURN ret = SQLExecute(stmt->m_hSTMT); if(ret == SQL_ERROR) { - Nan::ThrowError(ODBC::GetSQLError( + Local objError = ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteSync" - ).As()); + ); + + Nan::ThrowError(objError); info.GetReturnValue().Set(Nan::Null()); } @@ -502,8 +506,8 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); - info[0] = Nan::Null().As(); - info[1] = js_result.As(); + info[0] = Nan::Null(); + info[1] = js_result; Nan::TryCatch try_catch; @@ -546,11 +550,13 @@ NAN_METHOD(ODBCStatement::ExecuteDirectSync) { sql.length()); if(ret == SQL_ERROR) { - Nan::ThrowError(ODBC::GetSQLError( + Local objError = ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::ExecuteDirectSync" - ).As()); + ); + + Nan::ThrowError(objError); info.GetReturnValue().Set(Nan::Null()); } @@ -606,11 +612,13 @@ NAN_METHOD(ODBCStatement::PrepareSync) { info.GetReturnValue().Set(Nan::True()); } else { - Nan::ThrowError(ODBC::GetSQLError( + Local objError = ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::PrepareSync" - ).As()); + ); + + Nan::ThrowError(objError); info.GetReturnValue().Set(Nan::False()); } @@ -709,8 +717,8 @@ void ODBCStatement::UV_AfterPrepare(uv_work_t* req, int status) { else { Local info[2]; - info[0] = Nan::Null().As(); - info[1] = Nan::True().As(); + info[0] = Nan::Null(); + info[1] = Nan::True(); Nan::TryCatch try_catch; @@ -814,11 +822,13 @@ NAN_METHOD(ODBCStatement::BindSync) { info.GetReturnValue().Set(Nan::True()); } else { - Nan::ThrowError(ODBC::GetSQLError( + Local objError = ODBC::GetSQLError( SQL_HANDLE_STMT, stmt->m_hSTMT, (char *) "[node-odbc] Error in ODBCStatement::BindSync" - ).As()); + ); + + Nan::ThrowError(objError); info.GetReturnValue().Set(Nan::False()); } @@ -966,8 +976,8 @@ void ODBCStatement::UV_AfterBind(uv_work_t* req, int status) { else { Local info[2]; - info[0] = Nan::Null().As(); - info[1] = Nan::True().As(); + info[0] = Nan::Null(); + info[1] = Nan::True(); Nan::TryCatch try_catch; From 03a1ca5605cea417241ed6bb07cbdbe62486312d Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Thu, 24 Sep 2015 09:55:02 -0400 Subject: [PATCH 468/511] Updating node engine requirement. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a62307cc..277540d1 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "lib": "." }, "engines": { - "node": ">=4.0.0" + "node": ">=0.8.0" }, "scripts": { "install": "node-gyp configure build", From e26825e64dd543059f387a6ec976c7d32c01c4eb Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Thu, 24 Sep 2015 10:01:48 -0400 Subject: [PATCH 469/511] Fixing issues for UNICODE preprocessor definition. --- src/odbc.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index e9f883a2..8a1cfa1a 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -565,7 +565,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, #ifdef UNICODE str = Nan::New((uint16_t*) buffer).ToLocalChecked(); #else - str = Nan::New((char *) buffer); + str = Nan::New((char *) buffer).ToLocalChecked(); #endif } else { @@ -573,7 +573,7 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, #ifdef UNICODE str = String::Concat(str, Nan::New((uint16_t*) buffer).ToLocalChecked()); #else - str = String::Concat(str, Nan::New((char *) buffer)); + str = String::Concat(str, Nan::New((char *) buffer).ToLocalChecked()); #endif } @@ -632,7 +632,7 @@ Local ODBC::GetRecordTuple ( SQLHSTMT hStmt, Column* columns, tuple->Set( Nan::New((uint16_t *) columns[i].name).ToLocalChecked(), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); #else - tuple->Set( Nan::New((const char *) columns[i].name), + tuple->Set( Nan::New((const char *) columns[i].name).ToLocalChecked(), GetColumnValue( hStmt, columns[i], buffer, bufferLength)); #endif } @@ -869,9 +869,9 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* objError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage).ToLocalChecked()); objError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState).ToLocalChecked()); #else - objError->SetPrototype(Exception::Error(Nan::New(errorMessage))); - objError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage)); - objError->Set(Nan::New("state").ToLocalChecked(), Nan::New(errorSQLState)); + objError->SetPrototype(Exception::Error(Nan::New(errorMessage).ToLocalChecked())); + objError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage).ToLocalChecked()); + objError->Set(Nan::New("state").ToLocalChecked(), Nan::New(errorSQLState).ToLocalChecked()); #endif } @@ -881,8 +881,8 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* subError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage).ToLocalChecked()); subError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState).ToLocalChecked()); #else - subError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage)); - subError->Set(Nan::New("state").ToLocalChecked(), Nan::New(errorSQLState)); + subError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage).ToLocalChecked()); + subError->Set(Nan::New("state").ToLocalChecked(), Nan::New(errorSQLState).ToLocalChecked()); #endif errors->Set(Nan::New(i), subError); From 40421ec8d863d87a7435e91413c0a5859730811f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 24 Sep 2015 12:03:32 -0400 Subject: [PATCH 470/511] 1.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 277540d1..ed92d8a1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.1.8", + "version": "1.2.0", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From bd90bdbf0a527d98f5ac4b9e81d97011324f4cae Mon Sep 17 00:00:00 2001 From: Santiago Gimeno Date: Mon, 2 Nov 2015 15:52:02 +0100 Subject: [PATCH 471/511] src: fix compilation error on 32-bit linux - As SQLINTEGER is defined as long int, store the number of errors in a int32_t variable instead of a SQLINTEGER. This is probably safe as it's highly unlikely to have so many errors as to produce an overflow. --- src/odbc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 8a1cfa1a..6f36e79e 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -819,7 +819,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* Local objError = Nan::New(); - SQLINTEGER i = 0; + int32_t i = 0; SQLINTEGER native; SQLSMALLINT len; From bc80233c8dd9d3ace14bc135907fd34e373ef121 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 2 Nov 2015 10:27:05 -0500 Subject: [PATCH 472/511] 1.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ed92d8a1..0ba5ee60 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.2.0", + "version": "1.2.1", "main": "lib/odbc.js", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { From d09fd4d6dd8dfde2794ccf70a8b2335cdefb5179 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 6 Mar 2017 13:32:37 +0100 Subject: [PATCH 473/511] Fix OS X build, search in /usr/local/include. The OS X build already looked for the odbc library in /usr/local/lib so it makes the sense to look for the headers in /usr/local/include. --- binding.gyp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/binding.gyp b/binding.gyp index 3bc47dcf..a142ff13 100644 --- a/binding.gyp +++ b/binding.gyp @@ -26,6 +26,9 @@ ] }], [ 'OS == "mac"', { + 'include_dirs': [ + '/usr/local/include' + ], 'libraries' : [ '-L/usr/local/lib', '-lodbc' From e7c20b69dcd2824425f316261041fc35fc691a59 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 6 Mar 2017 13:58:07 +0100 Subject: [PATCH 474/511] Make failing tests print stack traces. --- test/test-binding-statement-executeSync.js | 2 +- test/test-binding-statement-rebinding.js | 2 +- test/test-binding-transaction-commitSync.js | 4 ++-- test/test-multi-openSync-closeSync.js | 2 +- test/test-openSync.js | 4 ++-- test/test-query-create-table-fetchSync.js | 2 +- test/test-querySync-select-unicode.js | 2 +- test/test-querySync-select-with-execption.js | 2 +- test/test-transaction-commit-sync.js | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/test-binding-statement-executeSync.js b/test/test-binding-statement-executeSync.js index e546b587..8c5dba00 100644 --- a/test/test-binding-statement-executeSync.js +++ b/test/test-binding-statement-executeSync.js @@ -62,7 +62,7 @@ db.createConnection(function (err, conn) { console.log(r); } catch (e) { - console.log(e); + console.log(e.stack); exitCode = 1; } diff --git a/test/test-binding-statement-rebinding.js b/test/test-binding-statement-rebinding.js index 73789f3b..14444b5e 100644 --- a/test/test-binding-statement-rebinding.js +++ b/test/test-binding-statement-rebinding.js @@ -39,7 +39,7 @@ db.createConnection(function (err, conn) { assert.deepEqual(r, [ { col1: 'goodbye', col2: 'steven' } ]); } catch (e) { - console.log(e); + console.log(e.stack); exitCode = 1; } diff --git a/test/test-binding-transaction-commitSync.js b/test/test-binding-transaction-commitSync.js index dd7409b3..3e004d86 100644 --- a/test/test-binding-transaction-commitSync.js +++ b/test/test-binding-transaction-commitSync.js @@ -22,7 +22,7 @@ db.createConnection(function (err, conn) { } catch (e) { console.log("Failed when rolling back"); - console.log(e); + console.log(e.stack); exitCode = 1 } @@ -40,7 +40,7 @@ db.createConnection(function (err, conn) { } catch (e) { console.log("Failed when committing"); - console.log(e); + console.log(e.stack); exitCode = 2; } diff --git a/test/test-multi-openSync-closeSync.js b/test/test-multi-openSync-closeSync.js index 3e89d5f0..d948d8df 100644 --- a/test/test-multi-openSync-closeSync.js +++ b/test/test-multi-openSync-closeSync.js @@ -16,7 +16,7 @@ for (var x = 0; x < openCount; x++ ) { } catch (e) { console.log(common.connectionString); - console.log(e); + console.log(e.stack); errorCount += 1; break; } diff --git a/test/test-openSync.js b/test/test-openSync.js index 9e03d593..88f6eeb2 100644 --- a/test/test-openSync.js +++ b/test/test-openSync.js @@ -18,7 +18,7 @@ try { db.openSync(common.connectionString); } catch(e) { - console.log(e); + console.log(e.stack); assert.deepEqual(e, null); } @@ -26,6 +26,6 @@ try { db.closeSync(); } catch(e) { - console.log(e); + console.log(e.stack); assert.deepEqual(e, null); } diff --git a/test/test-query-create-table-fetchSync.js b/test/test-query-create-table-fetchSync.js index 61d0d226..7fa2ed98 100644 --- a/test/test-query-create-table-fetchSync.js +++ b/test/test-query-create-table-fetchSync.js @@ -15,7 +15,7 @@ db.queryResult("create table " + common.tableName + " (COLINT INTEGER, COLDATETI console.log(data); } catch (e) { - console.log(e); + console.log(e.stack); } db.closeSync(); diff --git a/test/test-querySync-select-unicode.js b/test/test-querySync-select-unicode.js index bcd4166f..21c6b663 100644 --- a/test/test-querySync-select-unicode.js +++ b/test/test-querySync-select-unicode.js @@ -11,7 +11,7 @@ try { data = db.querySync("select 'ꜨꜢ' as UNICODETEXT"); } catch (e) { - console.log(e); + console.log(e.stack); } db.closeSync(); diff --git a/test/test-querySync-select-with-execption.js b/test/test-querySync-select-with-execption.js index 2bd2015b..b53cbe42 100644 --- a/test/test-querySync-select-with-execption.js +++ b/test/test-querySync-select-with-execption.js @@ -12,7 +12,7 @@ try { var data = db.querySync("select invalid query"); } catch (e) { - console.log(e); + console.log(e.stack); err = e; } diff --git a/test/test-transaction-commit-sync.js b/test/test-transaction-commit-sync.js index 3d992382..1880956b 100644 --- a/test/test-transaction-commit-sync.js +++ b/test/test-transaction-commit-sync.js @@ -22,7 +22,7 @@ common.createTables(db, function (err, data) { } catch (e) { console.log("Failed when rolling back"); - console.log(e); + console.log(e.stack); exitCode = 1 } @@ -40,7 +40,7 @@ common.createTables(db, function (err, data) { } catch (e) { console.log("Failed when committing"); - console.log(e); + console.log(e.stack); exitCode = 2; } From 2bc41284b32ec9f319597461c3d5fdcb931d2b61 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 6 Mar 2017 14:03:30 +0100 Subject: [PATCH 475/511] Fix test-binding-statement-executeSync failure. The `info.GetReturnValue().Set(Nan::Undefined())` at the end was subsuming the preceding `info.GetReturnValue().Set(Nan::True())`, making the test fail. --- src/odbc_statement.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 19623184..966d1710 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -832,8 +832,6 @@ NAN_METHOD(ODBCStatement::BindSync) { info.GetReturnValue().Set(Nan::False()); } - - info.GetReturnValue().Set(Nan::Undefined()); } /* From 0d50fb1b509e9c04bf486084c7530b49770e70d7 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 6 Mar 2017 14:09:21 +0100 Subject: [PATCH 476/511] Fix test-binding-statement-rebinding failure. Call `stmt.bindSync()` again after reassigning the array elements because I don't see how this test could pass otherwise, given the implementation of `ODBC::GetParametersFromArray()`. --- test/test-binding-statement-rebinding.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/test-binding-statement-rebinding.js b/test/test-binding-statement-rebinding.js index 14444b5e..51d4df86 100644 --- a/test/test-binding-statement-rebinding.js +++ b/test/test-binding-statement-rebinding.js @@ -5,12 +5,6 @@ var common = require("./common") , exitCode = 0 ; -/* - * The purpose of this test is to test binding an array and then - * changing the values of the array before an execute[Sync] - * call and have the new array values be used. - */ - db.createConnection(function (err, conn) { conn.openSync(common.connectionString); @@ -30,6 +24,7 @@ db.createConnection(function (err, conn) { a[0] = 'goodbye'; a[1] = 'steven'; + stmt.bindSync(a); result = stmt.executeSync(); From 024f61d7edc8ff738d98ba9eddbc67d9ce0244d6 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 6 Mar 2017 15:07:43 +0100 Subject: [PATCH 477/511] Fix test-date failure. SQLite has no distinct DATETIME or TIMESTAMP type. Add a special case to the test. --- test/test-date.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/test/test-date.js b/test/test-date.js index aafb9095..224efcb5 100644 --- a/test/test-date.js +++ b/test/test-date.js @@ -4,13 +4,18 @@ var common = require("./common") , assert = require("assert") ; +var sqlite = /sqlite/i.test(common.connectionString); + db.open(common.connectionString, function(err) { assert.equal(err, null); assert.equal(db.connected, true); var dt = new Date(); - var sql = "SELECT cast('" + dt.toISOString().replace('Z','') + "' as datetime) as DT1"; - + var ds = dt.toISOString().replace('Z',''); + var sql = "SELECT cast('" + ds + "' as datetime) as DT1"; + // XXX(bnoordhuis) sqlite3 has no distinct DATETIME or TIMESTAMP type. + // 'datetime' in this expression is a function, not a type. + if (sqlite) sql = "SELECT datetime('" + ds + "') as DT1"; console.log(sql); db.query(sql, function (err, data) { @@ -24,8 +29,13 @@ db.open(common.connectionString, function(err) { //test selected data after the connection //is closed, in case the assertion fails - assert.equal(data[0].DT1.constructor.name, "Date", "DT1 is not an instance of a Date object"); - assert.equal(data[0].DT1.getTime(), dt.getTime()); + if (sqlite) { + assert.equal(data[0].DT1.constructor.name, "String", "DT1 is not an instance of a String object"); + assert.equal(data[0].DT1, ds.replace('T', ' ').replace(/\.\d+$/, '')); + } else { + assert.equal(data[0].DT1.constructor.name, "Date", "DT1 is not an instance of a Date object"); + assert.equal(data[0].DT1.getTime(), dt.getTime()); + } }); }); }); From cf4cfeaf70399a6ece52b1c54457dd2285c2cece Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 6 Mar 2017 15:41:33 +0100 Subject: [PATCH 478/511] Fix test-domains-open failure. Store the active domain on the database and connection objects and use the `Nan::Callback::Call()` overload that enters the domain before making the callback. This does the bare minimum to make the test pass. Better domains support would need a complete audit of the code base, but as domains are a deprecated feature anyway, I don't think it's worth the effort. --- lib/odbc.js | 2 ++ src/odbc.cpp | 2 +- src/odbc_connection.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/odbc.js b/lib/odbc.js index dd9dc4c6..23a2de42 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -66,6 +66,7 @@ function Database(options) { } self.odbc = (options.odbc) ? options.odbc : new odbc.ODBC(); + self.odbc.domain = process.domain; self.queue = new SimpleQueue(); self.fetchMode = options.fetchMode || null; self.connected = false; @@ -106,6 +107,7 @@ Database.prototype.open = function (connectionString, cb) { if (err) return cb(err); self.conn = conn; + self.conn.domain = process.domain; if (self.connectTimeout || self.connectTimeout === 0) { self.conn.connectTimeout = self.connectTimeout; diff --git a/src/odbc.cpp b/src/odbc.cpp index 6f36e79e..18063fba 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -226,7 +226,7 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { info[0] = Nan::Null(); info[1] = js_result; - data->cb->Call(2, info); + data->cb->Call(data->dbo->handle(), 2, info); } if (try_catch.HasCaught()) { diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 5947c94b..f5294062 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -319,7 +319,7 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { Nan::TryCatch try_catch; data->conn->Unref(); - data->cb->Call(err ? 1 : 0, argv); + data->cb->Call(data->conn->handle(), err ? 1 : 0, argv); if (try_catch.HasCaught()) { Nan::FatalException(try_catch); From b2fe3c179c54263dc379c73485267e6d81f4b790 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 6 Mar 2017 20:09:23 +0100 Subject: [PATCH 479/511] Fix test-date failure. Retrieved dates are in local time but we insert a UTC date so adjust it before comparing. --- test/test-date.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test-date.js b/test/test-date.js index 224efcb5..d0309a28 100644 --- a/test/test-date.js +++ b/test/test-date.js @@ -11,6 +11,7 @@ db.open(common.connectionString, function(err) { assert.equal(db.connected, true); var dt = new Date(); + dt.setMilliseconds(0); // MySQL truncates them. var ds = dt.toISOString().replace('Z',''); var sql = "SELECT cast('" + ds + "' as datetime) as DT1"; // XXX(bnoordhuis) sqlite3 has no distinct DATETIME or TIMESTAMP type. @@ -34,7 +35,10 @@ db.open(common.connectionString, function(err) { assert.equal(data[0].DT1, ds.replace('T', ' ').replace(/\.\d+$/, '')); } else { assert.equal(data[0].DT1.constructor.name, "Date", "DT1 is not an instance of a Date object"); - assert.equal(data[0].DT1.getTime(), dt.getTime()); + // XXX(bnoordhuis) DT1 is in local time but we inserted + // a UTC date so we need to adjust it before comparing. + dt = new Date(dt.getTime() + 6e4 * dt.getTimezoneOffset()); + assert.equal(data[0].DT1.toISOString(), dt.toISOString()); } }); }); From 26f0c0598e1824ffb6ac5a3c5c2f3f7d371f4dbc Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 6 Mar 2017 22:24:22 +0100 Subject: [PATCH 480/511] Fix buffer overflows in UTF-8 mode. Update the C++ code to use `string->Utf8Length()` instead of `string->Length()` because the latter returns the size in _characters_, not bytes. Fixes several buffer overruns in one fell swoop. --- src/odbc.cpp | 3 +-- src/odbc_connection.cpp | 29 +++++++++++++++-------------- src/odbc_statement.cpp | 18 ++++++++---------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 18063fba..3c3f22e6 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -686,13 +686,12 @@ Parameter* ODBC::GetParametersFromArray (Local values, int *paramCount) { if (value->IsString()) { Local string = value->ToString(); - int length = string->Length(); params[i].ValueType = SQL_C_TCHAR; params[i].ColumnSize = 0; //SQL_SS_LENGTH_UNLIMITED #ifdef UNICODE params[i].ParameterType = SQL_WVARCHAR; - params[i].BufferLength = (length * sizeof(uint16_t)) + sizeof(uint16_t); + params[i].BufferLength = (string->Length() * sizeof(uint16_t)) + sizeof(uint16_t); #else params[i].ParameterType = SQL_VARCHAR; params[i].BufferLength = string->Utf8Length() + 1; diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index f5294062..927df3ca 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -194,13 +194,13 @@ NAN_METHOD(ODBCConnection::Open) { open_connection_work_data* data = (open_connection_work_data *) calloc(1, sizeof(open_connection_work_data)); - data->connectionLength = connection->Length() + 1; - //copy the connection string to the work data #ifdef UNICODE + data->connectionLength = connection->Length() + 1; data->connection = (uint16_t *) malloc(sizeof(uint16_t) * data->connectionLength); connection->Write((uint16_t*) data->connection); #else + data->connectionLength = connection->Utf8Length() + 1; data->connection = (char *) malloc(sizeof(char) * data->connectionLength); connection->WriteUtf8((char*) data->connection); #endif @@ -351,12 +351,12 @@ NAN_METHOD(ODBCConnection::OpenSync) { SQLRETURN ret; bool err = false; - int connectionLength = connection->Length() + 1; - #ifdef UNICODE + int connectionLength = connection->Length() + 1; uint16_t* connectionString = (uint16_t *) malloc(connectionLength * sizeof(uint16_t)); connection->Write(connectionString); #else + int connectionLength = connection->Utf8Length() + 1; char* connectionString = (char *) malloc(connectionLength); connection->WriteUtf8(connectionString); #endif @@ -791,14 +791,15 @@ NAN_METHOD(ODBCConnection::Query) { //Done checking arguments data->cb = new Nan::Callback(cb); - data->sqlLen = sql->Length(); #ifdef UNICODE + data->sqlLen = sql->Length(); data->sqlSize = (data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t); data->sql = (uint16_t *) malloc(data->sqlSize); sql->Write((uint16_t *) data->sql); #else - data->sqlSize = sql->Utf8Length() + 1; + data->sqlLen = sql->Utf8Length(); + data->sqlSize = data->sqlLen + 1; data->sql = (char *) malloc(data->sqlSize); sql->WriteUtf8((char *) data->sql); #endif @@ -1208,7 +1209,7 @@ NAN_METHOD(ODBCConnection::Tables) { data->catalog = (uint16_t *) malloc((catalog->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); catalog->Write((uint16_t *) data->catalog); #else - data->catalog = (char *) malloc(catalog->Length() + 1); + data->catalog = (char *) malloc(catalog->Utf8Length() + 1); catalog->WriteUtf8((char *) data->catalog); #endif } @@ -1218,7 +1219,7 @@ NAN_METHOD(ODBCConnection::Tables) { data->schema = (uint16_t *) malloc((schema->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); schema->Write((uint16_t *) data->schema); #else - data->schema = (char *) malloc(schema->Length() + 1); + data->schema = (char *) malloc(schema->Utf8Length() + 1); schema->WriteUtf8((char *) data->schema); #endif } @@ -1228,7 +1229,7 @@ NAN_METHOD(ODBCConnection::Tables) { data->table = (uint16_t *) malloc((table->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); table->Write((uint16_t *) data->table); #else - data->table = (char *) malloc(table->Length() + 1); + data->table = (char *) malloc(table->Utf8Length() + 1); table->WriteUtf8((char *) data->table); #endif } @@ -1238,7 +1239,7 @@ NAN_METHOD(ODBCConnection::Tables) { data->type = (uint16_t *) malloc((type->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); type->Write((uint16_t *) data->type); #else - data->type = (char *) malloc(type->Length() + 1); + data->type = (char *) malloc(type->Utf8Length() + 1); type->WriteUtf8((char *) data->type); #endif } @@ -1319,7 +1320,7 @@ NAN_METHOD(ODBCConnection::Columns) { data->catalog = (uint16_t *) malloc((catalog->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); catalog->Write((uint16_t *) data->catalog); #else - data->catalog = (char *) malloc(catalog->Length() + 1); + data->catalog = (char *) malloc(catalog->Utf8Length() + 1); catalog->WriteUtf8((char *) data->catalog); #endif } @@ -1329,7 +1330,7 @@ NAN_METHOD(ODBCConnection::Columns) { data->schema = (uint16_t *) malloc((schema->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); schema->Write((uint16_t *) data->schema); #else - data->schema = (char *) malloc(schema->Length() + 1); + data->schema = (char *) malloc(schema->Utf8Length() + 1); schema->WriteUtf8((char *) data->schema); #endif } @@ -1339,7 +1340,7 @@ NAN_METHOD(ODBCConnection::Columns) { data->table = (uint16_t *) malloc((table->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); table->Write((uint16_t *) data->table); #else - data->table = (char *) malloc(table->Length() + 1); + data->table = (char *) malloc(table->Utf8Length() + 1); table->WriteUtf8((char *) data->table); #endif } @@ -1349,7 +1350,7 @@ NAN_METHOD(ODBCConnection::Columns) { data->column = (uint16_t *) malloc((column->Length() * sizeof(uint16_t)) + sizeof(uint16_t)); column->Write((uint16_t *) data->column); #else - data->column = (char *) malloc(column->Length() + 1); + data->column = (char *) malloc(column->Utf8Length() + 1); column->WriteUtf8((char *) data->column); #endif } diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 966d1710..2447c86b 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -439,12 +439,12 @@ NAN_METHOD(ODBCStatement::ExecuteDirect) { data->cb = new Nan::Callback(cb); - data->sqlLen = sql->Length(); - #ifdef UNICODE + data->sqlLen = sql->Length(); data->sql = (uint16_t *) malloc((data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t)); sql->Write((uint16_t *) data->sql); #else + data->sqlLen = sql->Utf8Length(); data->sql = (char *) malloc(data->sqlLen +1); sql->WriteUtf8((char *) data->sql); #endif @@ -591,15 +591,13 @@ NAN_METHOD(ODBCStatement::PrepareSync) { SQLRETURN ret; - int sqlLen = sql->Length() + 1; - #ifdef UNICODE - uint16_t *sql2; - sql2 = (uint16_t *) malloc(sqlLen * sizeof(uint16_t)); + int sqlLen = sql->Length() + 1; + uint16_t* sql2 = (uint16_t *) malloc(sqlLen * sizeof(uint16_t)); sql->Write(sql2); #else - char *sql2; - sql2 = (char *) malloc(sqlLen); + int sqlLen = sql->Utf8Length() + 1; + char* sql2 = (char *) malloc(sqlLen); sql->WriteUtf8(sql2); #endif @@ -646,12 +644,12 @@ NAN_METHOD(ODBCStatement::Prepare) { data->cb = new Nan::Callback(cb); - data->sqlLen = sql->Length(); - #ifdef UNICODE + data->sqlLen = sql->Length(); data->sql = (uint16_t *) malloc((data->sqlLen * sizeof(uint16_t)) + sizeof(uint16_t)); sql->Write((uint16_t *) data->sql); #else + data->sqlLen = sql->Utf8Length(); data->sql = (char *) malloc(data->sqlLen +1); sql->WriteUtf8((char *) data->sql); #endif From c39019a815fb5e96ff5503287693372b6108cb82 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 7 Mar 2017 10:24:09 +0100 Subject: [PATCH 481/511] Fix unref of uninitialized async handle. `ODBCConnection::CloseSync()` tried to unref a `uv_async_t` which hadn't been initialized. Remove the handle altogether, it isn't used anywhere else anymore, and the ersatz reference counting scheme it was used for is taken care of by `uv_queue_work()`. --- src/odbc.cpp | 17 ----------------- src/odbc.h | 1 - src/odbc_connection.cpp | 27 --------------------------- 3 files changed, 45 deletions(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 6f36e79e..c18168ca 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -39,7 +39,6 @@ using namespace v8; using namespace node; uv_mutex_t ODBC::g_odbcMutex; -uv_async_t ODBC::g_async; Nan::Persistent ODBC::constructor; @@ -80,22 +79,6 @@ void ODBC::Init(v8::Handle exports) { exports->Set(Nan::New("ODBC").ToLocalChecked(), constructor_template->GetFunction()); -#if NODE_VERSION_AT_LEAST(0, 7, 9) - // Initialize uv_async so that we can prevent node from exiting - //uv_async_init( uv_default_loop(), - // &ODBC::g_async, - // ODBC::WatcherCallback); - - // Not sure if the init automatically calls uv_ref() because there is weird - // behavior going on. When ODBC::Init is called which initializes the - // uv_async_t g_async above, there seems to be a ref which will keep it alive - // but we only want this available so that we can uv_ref() later on when - // we have a connection. - // so to work around this, I am possibly mistakenly calling uv_unref() once - // so that there are no references on the loop. - //uv_unref((uv_handle_t *)&ODBC::g_async); -#endif - // Initialize the cross platform mutex provided by libuv uv_mutex_init(&ODBC::g_odbcMutex); } diff --git a/src/odbc.h b/src/odbc.h index 52663057..550fd451 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -75,7 +75,6 @@ class ODBC : public Nan::ObjectWrap { public: static Nan::Persistent constructor; static uv_mutex_t g_odbcMutex; - static uv_async_t g_async; static void Init(v8::Handle exports); static Column* GetColumns(SQLHSTMT hStmt, short* colCount); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 5947c94b..488e03f6 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -307,13 +307,6 @@ void ODBCConnection::UV_AfterOpen(uv_work_t* req, int status) { if (!err) { data->conn->self()->connected = true; - - //only uv_ref if the connection was successful -//#if NODE_VERSION_AT_LEAST(0, 7, 9) -// uv_ref((uv_handle_t *)&ODBC::g_async); -//#else -// uv_ref(uv_default_loop()); -//#endif } Nan::TryCatch try_catch; @@ -419,13 +412,6 @@ NAN_METHOD(ODBCConnection::OpenSync) { ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt); conn->self()->connected = true; - - //only uv_ref if the connection was successful - /*#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_ref((uv_handle_t *)&ODBC::g_async); - #else - uv_ref(uv_default_loop()); - #endif*/ } uv_mutex_unlock(&ODBC::g_odbcMutex); @@ -504,13 +490,6 @@ void ODBCConnection::UV_AfterClose(uv_work_t* req, int status) { } else { conn->connected = false; - - //only unref if the connection was closed -//#if NODE_VERSION_AT_LEAST(0, 7, 9) -// uv_unref((uv_handle_t *)&ODBC::g_async); -//#else -// uv_unref(uv_default_loop()); -//#endif } Nan::TryCatch try_catch; @@ -545,12 +524,6 @@ NAN_METHOD(ODBCConnection::CloseSync) { conn->connected = false; -#if NODE_VERSION_AT_LEAST(0, 7, 9) - uv_unref((uv_handle_t *)&ODBC::g_async); -#else - uv_unref(uv_default_loop()); -#endif - info.GetReturnValue().Set(Nan::True()); } From a7c4c5467bc40477dae5288446db5acba1d6b8a1 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 3 May 2017 09:59:16 -0400 Subject: [PATCH 482/511] Added requirements for RedHat/CentOS by: @willfarrell closes #24 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6e610de6..7668ffc6 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ requirements * unixODBC binaries and development libraries for module compilation * on Ubuntu/Debian `sudo apt-get install unixodbc unixodbc-dev` + * on RedHat/CentOS `sudo yum install unixODBC unixODBC-devel` * on OSX using macports.org `sudo port unixODBC` * odbc drivers for target database * properly configured odbc.ini and odbcinst.ini. From bd9b59c9f3a8fb50a9e63ba565ec62755cc1155d Mon Sep 17 00:00:00 2001 From: Morten Houston Ludvigsen Date: Sun, 18 Jun 2017 15:17:05 +0200 Subject: [PATCH 483/511] Add typings --- lib/odbc.d.ts | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 171 insertions(+) create mode 100644 lib/odbc.d.ts diff --git a/lib/odbc.d.ts b/lib/odbc.d.ts new file mode 100644 index 00000000..8df8b731 --- /dev/null +++ b/lib/odbc.d.ts @@ -0,0 +1,170 @@ +declare module 'odbc' { + function odbc(options?: odbc.DatabaseOptions): odbc.Database; + + namespace odbc { + export const SQL_CLOSE: number; + export const SQL_DROP: number; + export const SQL_UNBIND: number; + export const SQL_RESET_PARAMS: number; + export const SQL_DESTROY: number; + export const FETCH_ARRAY: number; + export const FETCH_OBJECT: number; + + export let debug: boolean; + + export interface ConnctionInfo { + [key: string]: string; + } + + export interface DatabaseOptions { + connectTimeout?: number; + loginTimeout?: number; + } + + export interface DescribeOptions { + database: string; + schema?: string; + table?: string; + type?: string; + column?: string; + } + + export interface SimpleQueue { + push(fn: Function): void; + next(): any; + maybeNext(): any; + } + + export interface ODBCTable { + TABLE_CAT: string; + TABLE_SCHEM: string; + TABLE_NAME: string; + TABLE_TYPE: string; + REMARKS: string; + } + + export interface ODBCColumn { + TABLE_CAT: string; + TABLE_SCHEM: string; + TABLE_NAME: string; + COLUMN_NAME: string; + DATA_TYPE: number; + TYPE_NAME: string; + COLUMN_SIZE: number; + BUFFER_LENGTH: number; + DECIMAL_DIGITS: number; + NUM_PREC_RADIX: number; + NULLABLE: number; + REMARKS: string; + COLUMN_DEF: string; + SQL_DATA_TYPE: number; + SQL_DATETIME_SUB: number; + CHAR_OCTET_LENGTH: number; + ORDINAL_POSITION: number; + IS_NULLABLE: string; + } + + export interface ODBCConnection { + connected: boolean; + connectTimeout: number; + loginTimeout: number; + open(connctionString: string | ConnctionInfo, cb: (err: any, result: any) => void): void; + openSync(connctionString: string | ConnctionInfo): void; + close(cb: (err: any) => void): void; + closeSync(): void; + createStatement(cb: (err: any, stmt: ODBCStatement) => void): void; + createStatementSync(): ODBCStatement; + query(sql: string, cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; + query(sql: string, bindingParameters: any[], cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; + querySync(sql: string, bindingParameters?: any[]): ResultRow[]; + beginTransaction(cb: (err: any) => void): void; + beginTransactionSync(): void; + endTransaction(rollback: boolean, cb: (err: any) => void): void; + endTransactionSync(rollback: boolean): void; + tables(catalog: string | null, schema: string | null, table: string | null, type: string | null, cb: (err: any, result: ODBCResult) => void): void; + columns(catalog: string | null, schema: string | null, table: string | null, column: string | null, cb: (err: any, result: ODBCResult) => void): void; + } + + export interface ResultRow { + [key: string]: any; + } + + export interface ODBCResult { + fetchMode: number; + fetchAll(cb: (err: any, data: ResultRow[]) => void): void; + fetchAllSync(): ResultRow[]; + fetch(cb: (err: any, data: ResultRow) => void): void; + fetchSync(): ResultRow; + closeSync(): void; + moreResultsSync(): any; + getColumnNamesSync(): string[]; + } + + export interface ODBCStatement { + queue: SimpleQueue; + execute(cb: (err: any, result: ODBCResult) => void): void; + execute(bindingParameters: any[], cb: (err: any, result: ODBCResult) => void): void; + executeSync(bindingParameters?: any[]): ODBCResult; + executeDirect(sql: string, cb: (err: any, result: ODBCResult) => void): void; + executeDirect(sql: string, bindingParameters: any[], cb: (err: any, result: ODBCResult) => void): void; + executeDirectSync(sql: string, bindingParameters?: any[]): ODBCResult; + executeNonQuery(cb: (err: any, result: number) => void): void; + executeNonQuery(bindingParameters: any[], cb: (err: any, result: number) => void): void; + executeNonQuerySync(bindingParameters?: any[]): number; + prepare(sql: string, cb: (err: any) => void): void; + prepareSync(sql: string): void; + bind(bindingParameters: any[], cb: (err: any) => void): void; + bindSync(bindingParameters: any[]): void; + closeSync(): void; + } + + export class Database { + constructor(options?: DatabaseOptions); + conn: ODBCConnection; + queue: SimpleQueue; + connected: boolean; + connectTimeout: number; + loginTimeout: number; + SQL_CLOSE: number; + SQL_DROP: number; + SQL_UNBIND: number; + SQL_RESET_PARAMS: number; + SQL_DESTROY: number; + FETCH_ARRAY: number; + FETCH_OBJECT: number; + open(connctionString: string | ConnctionInfo, cb: (err: any, result: any) => void): void; + openSync(connctionString: string | ConnctionInfo): void; + close(cb: (err: any) => void): void; + closeSync(): void; + query(sql: string, cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; + query(sql: string, bindingParameters: any[], cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; + querySync(sql: string, bindingParameters?: any[]): ResultRow[]; + queryResult(sql: string, cb: (err: any, result: ODBCResult) => void): void; + queryResult(sql: string, bindingParameters: any[], cb: (err: any, result: ODBCResult) => void): void; + queryResultSync(sql: string, bindingParameters?: any[]): ODBCResult; + prepare(sql: string, cb: (err: any, statement: ODBCStatement) => void): void; + prepareSync(sql: string): ODBCStatement; + beginTransaction(cb: (err: any) => void): void; + beginTransactionSync(): void; + endTransaction(rollback: boolean, cb: (err: any) => void): void; + endTransactionSync(rollback: boolean): void; + commitTransaction(cb: (err: any) => void): void; + commitTransactionSync(): void; + rollbackTransaction(cb: (err: any) => void): void; + rollbackTransactionSync(): void; + tables(catalog: string | null, schema: string | null, table: string | null, type: string | null, cb: (err: any, result: ODBCTable[]) => void): void; + columns(catalog: string | null, schema: string | null, table: string | null, column: string | null, cb: (err: any, result: ODBCColumn[]) => void): void; + describe(options: DescribeOptions, cb: (err: any, result: (ODBCTable & ODBCColumn)[]) => void): void; + } + + export class Pool { + constructor(options?: DatabaseOptions); + open(connctionString: string, cb: (err: any, db: Database) => void): void; + close(cb: (err: any) => void): void; + } + + export function open(connctionString: string | ConnctionInfo, cb: (err: any, result: any) => void): void; + } + + export = odbc; +} \ No newline at end of file diff --git a/package.json b/package.json index 0ba5ee60..53358b48 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "description": "unixodbc bindings for node", "version": "1.2.1", "main": "lib/odbc.js", + "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", "repository": { "type": "git", From 6f2e811d8842c86052c2269a2fa682bbcb4653a8 Mon Sep 17 00:00:00 2001 From: Morten Houston Ludvigsen Date: Mon, 19 Jun 2017 12:02:29 +0200 Subject: [PATCH 484/511] No need to declare module --- lib/odbc.d.ts | 334 +++++++++++++++++++++++++------------------------- 1 file changed, 166 insertions(+), 168 deletions(-) diff --git a/lib/odbc.d.ts b/lib/odbc.d.ts index 8df8b731..a3c3a28f 100644 --- a/lib/odbc.d.ts +++ b/lib/odbc.d.ts @@ -1,170 +1,168 @@ -declare module 'odbc' { - function odbc(options?: odbc.DatabaseOptions): odbc.Database; - - namespace odbc { - export const SQL_CLOSE: number; - export const SQL_DROP: number; - export const SQL_UNBIND: number; - export const SQL_RESET_PARAMS: number; - export const SQL_DESTROY: number; - export const FETCH_ARRAY: number; - export const FETCH_OBJECT: number; - - export let debug: boolean; - - export interface ConnctionInfo { - [key: string]: string; - } - - export interface DatabaseOptions { - connectTimeout?: number; - loginTimeout?: number; - } - - export interface DescribeOptions { - database: string; - schema?: string; - table?: string; - type?: string; - column?: string; - } - - export interface SimpleQueue { - push(fn: Function): void; - next(): any; - maybeNext(): any; - } - - export interface ODBCTable { - TABLE_CAT: string; - TABLE_SCHEM: string; - TABLE_NAME: string; - TABLE_TYPE: string; - REMARKS: string; - } - - export interface ODBCColumn { - TABLE_CAT: string; - TABLE_SCHEM: string; - TABLE_NAME: string; - COLUMN_NAME: string; - DATA_TYPE: number; - TYPE_NAME: string; - COLUMN_SIZE: number; - BUFFER_LENGTH: number; - DECIMAL_DIGITS: number; - NUM_PREC_RADIX: number; - NULLABLE: number; - REMARKS: string; - COLUMN_DEF: string; - SQL_DATA_TYPE: number; - SQL_DATETIME_SUB: number; - CHAR_OCTET_LENGTH: number; - ORDINAL_POSITION: number; - IS_NULLABLE: string; - } - - export interface ODBCConnection { - connected: boolean; - connectTimeout: number; - loginTimeout: number; - open(connctionString: string | ConnctionInfo, cb: (err: any, result: any) => void): void; - openSync(connctionString: string | ConnctionInfo): void; - close(cb: (err: any) => void): void; - closeSync(): void; - createStatement(cb: (err: any, stmt: ODBCStatement) => void): void; - createStatementSync(): ODBCStatement; - query(sql: string, cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; - query(sql: string, bindingParameters: any[], cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; - querySync(sql: string, bindingParameters?: any[]): ResultRow[]; - beginTransaction(cb: (err: any) => void): void; - beginTransactionSync(): void; - endTransaction(rollback: boolean, cb: (err: any) => void): void; - endTransactionSync(rollback: boolean): void; - tables(catalog: string | null, schema: string | null, table: string | null, type: string | null, cb: (err: any, result: ODBCResult) => void): void; - columns(catalog: string | null, schema: string | null, table: string | null, column: string | null, cb: (err: any, result: ODBCResult) => void): void; - } - - export interface ResultRow { - [key: string]: any; - } - - export interface ODBCResult { - fetchMode: number; - fetchAll(cb: (err: any, data: ResultRow[]) => void): void; - fetchAllSync(): ResultRow[]; - fetch(cb: (err: any, data: ResultRow) => void): void; - fetchSync(): ResultRow; - closeSync(): void; - moreResultsSync(): any; - getColumnNamesSync(): string[]; - } - - export interface ODBCStatement { - queue: SimpleQueue; - execute(cb: (err: any, result: ODBCResult) => void): void; - execute(bindingParameters: any[], cb: (err: any, result: ODBCResult) => void): void; - executeSync(bindingParameters?: any[]): ODBCResult; - executeDirect(sql: string, cb: (err: any, result: ODBCResult) => void): void; - executeDirect(sql: string, bindingParameters: any[], cb: (err: any, result: ODBCResult) => void): void; - executeDirectSync(sql: string, bindingParameters?: any[]): ODBCResult; - executeNonQuery(cb: (err: any, result: number) => void): void; - executeNonQuery(bindingParameters: any[], cb: (err: any, result: number) => void): void; - executeNonQuerySync(bindingParameters?: any[]): number; - prepare(sql: string, cb: (err: any) => void): void; - prepareSync(sql: string): void; - bind(bindingParameters: any[], cb: (err: any) => void): void; - bindSync(bindingParameters: any[]): void; - closeSync(): void; - } - - export class Database { - constructor(options?: DatabaseOptions); - conn: ODBCConnection; - queue: SimpleQueue; - connected: boolean; - connectTimeout: number; - loginTimeout: number; - SQL_CLOSE: number; - SQL_DROP: number; - SQL_UNBIND: number; - SQL_RESET_PARAMS: number; - SQL_DESTROY: number; - FETCH_ARRAY: number; - FETCH_OBJECT: number; - open(connctionString: string | ConnctionInfo, cb: (err: any, result: any) => void): void; - openSync(connctionString: string | ConnctionInfo): void; - close(cb: (err: any) => void): void; - closeSync(): void; - query(sql: string, cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; - query(sql: string, bindingParameters: any[], cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; - querySync(sql: string, bindingParameters?: any[]): ResultRow[]; - queryResult(sql: string, cb: (err: any, result: ODBCResult) => void): void; - queryResult(sql: string, bindingParameters: any[], cb: (err: any, result: ODBCResult) => void): void; - queryResultSync(sql: string, bindingParameters?: any[]): ODBCResult; - prepare(sql: string, cb: (err: any, statement: ODBCStatement) => void): void; - prepareSync(sql: string): ODBCStatement; - beginTransaction(cb: (err: any) => void): void; - beginTransactionSync(): void; - endTransaction(rollback: boolean, cb: (err: any) => void): void; - endTransactionSync(rollback: boolean): void; - commitTransaction(cb: (err: any) => void): void; - commitTransactionSync(): void; - rollbackTransaction(cb: (err: any) => void): void; - rollbackTransactionSync(): void; - tables(catalog: string | null, schema: string | null, table: string | null, type: string | null, cb: (err: any, result: ODBCTable[]) => void): void; - columns(catalog: string | null, schema: string | null, table: string | null, column: string | null, cb: (err: any, result: ODBCColumn[]) => void): void; - describe(options: DescribeOptions, cb: (err: any, result: (ODBCTable & ODBCColumn)[]) => void): void; - } - - export class Pool { - constructor(options?: DatabaseOptions); - open(connctionString: string, cb: (err: any, db: Database) => void): void; - close(cb: (err: any) => void): void; - } - - export function open(connctionString: string | ConnctionInfo, cb: (err: any, result: any) => void): void; +declare function odbc(options?: odbc.DatabaseOptions): odbc.Database; + +declare namespace odbc { + export const SQL_CLOSE: number; + export const SQL_DROP: number; + export const SQL_UNBIND: number; + export const SQL_RESET_PARAMS: number; + export const SQL_DESTROY: number; + export const FETCH_ARRAY: number; + export const FETCH_OBJECT: number; + + export let debug: boolean; + + export interface ConnctionInfo { + [key: string]: string; + } + + export interface DatabaseOptions { + connectTimeout?: number; + loginTimeout?: number; + } + + export interface DescribeOptions { + database: string; + schema?: string; + table?: string; + type?: string; + column?: string; + } + + export interface SimpleQueue { + push(fn: Function): void; + next(): any; + maybeNext(): any; + } + + export interface ODBCTable { + TABLE_CAT: string; + TABLE_SCHEM: string; + TABLE_NAME: string; + TABLE_TYPE: string; + REMARKS: string; + } + + export interface ODBCColumn { + TABLE_CAT: string; + TABLE_SCHEM: string; + TABLE_NAME: string; + COLUMN_NAME: string; + DATA_TYPE: number; + TYPE_NAME: string; + COLUMN_SIZE: number; + BUFFER_LENGTH: number; + DECIMAL_DIGITS: number; + NUM_PREC_RADIX: number; + NULLABLE: number; + REMARKS: string; + COLUMN_DEF: string; + SQL_DATA_TYPE: number; + SQL_DATETIME_SUB: number; + CHAR_OCTET_LENGTH: number; + ORDINAL_POSITION: number; + IS_NULLABLE: string; } - export = odbc; -} \ No newline at end of file + export interface ODBCConnection { + connected: boolean; + connectTimeout: number; + loginTimeout: number; + open(connctionString: string | ConnctionInfo, cb: (err: any, result: any) => void): void; + openSync(connctionString: string | ConnctionInfo): void; + close(cb: (err: any) => void): void; + closeSync(): void; + createStatement(cb: (err: any, stmt: ODBCStatement) => void): void; + createStatementSync(): ODBCStatement; + query(sql: string, cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; + query(sql: string, bindingParameters: any[], cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; + querySync(sql: string, bindingParameters?: any[]): ResultRow[]; + beginTransaction(cb: (err: any) => void): void; + beginTransactionSync(): void; + endTransaction(rollback: boolean, cb: (err: any) => void): void; + endTransactionSync(rollback: boolean): void; + tables(catalog: string | null, schema: string | null, table: string | null, type: string | null, cb: (err: any, result: ODBCResult) => void): void; + columns(catalog: string | null, schema: string | null, table: string | null, column: string | null, cb: (err: any, result: ODBCResult) => void): void; + } + + export interface ResultRow { + [key: string]: any; + } + + export interface ODBCResult { + fetchMode: number; + fetchAll(cb: (err: any, data: ResultRow[]) => void): void; + fetchAllSync(): ResultRow[]; + fetch(cb: (err: any, data: ResultRow) => void): void; + fetchSync(): ResultRow; + closeSync(): void; + moreResultsSync(): any; + getColumnNamesSync(): string[]; + } + + export interface ODBCStatement { + queue: SimpleQueue; + execute(cb: (err: any, result: ODBCResult) => void): void; + execute(bindingParameters: any[], cb: (err: any, result: ODBCResult) => void): void; + executeSync(bindingParameters?: any[]): ODBCResult; + executeDirect(sql: string, cb: (err: any, result: ODBCResult) => void): void; + executeDirect(sql: string, bindingParameters: any[], cb: (err: any, result: ODBCResult) => void): void; + executeDirectSync(sql: string, bindingParameters?: any[]): ODBCResult; + executeNonQuery(cb: (err: any, result: number) => void): void; + executeNonQuery(bindingParameters: any[], cb: (err: any, result: number) => void): void; + executeNonQuerySync(bindingParameters?: any[]): number; + prepare(sql: string, cb: (err: any) => void): void; + prepareSync(sql: string): void; + bind(bindingParameters: any[], cb: (err: any) => void): void; + bindSync(bindingParameters: any[]): void; + closeSync(): void; + } + + export class Database { + constructor(options?: DatabaseOptions); + conn: ODBCConnection; + queue: SimpleQueue; + connected: boolean; + connectTimeout: number; + loginTimeout: number; + SQL_CLOSE: number; + SQL_DROP: number; + SQL_UNBIND: number; + SQL_RESET_PARAMS: number; + SQL_DESTROY: number; + FETCH_ARRAY: number; + FETCH_OBJECT: number; + open(connctionString: string | ConnctionInfo, cb: (err: any, result: any) => void): void; + openSync(connctionString: string | ConnctionInfo): void; + close(cb: (err: any) => void): void; + closeSync(): void; + query(sql: string, cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; + query(sql: string, bindingParameters: any[], cb: (err: any, rows: ResultRow[], moreResultSets: any) => void): void; + querySync(sql: string, bindingParameters?: any[]): ResultRow[]; + queryResult(sql: string, cb: (err: any, result: ODBCResult) => void): void; + queryResult(sql: string, bindingParameters: any[], cb: (err: any, result: ODBCResult) => void): void; + queryResultSync(sql: string, bindingParameters?: any[]): ODBCResult; + prepare(sql: string, cb: (err: any, statement: ODBCStatement) => void): void; + prepareSync(sql: string): ODBCStatement; + beginTransaction(cb: (err: any) => void): void; + beginTransactionSync(): void; + endTransaction(rollback: boolean, cb: (err: any) => void): void; + endTransactionSync(rollback: boolean): void; + commitTransaction(cb: (err: any) => void): void; + commitTransactionSync(): void; + rollbackTransaction(cb: (err: any) => void): void; + rollbackTransactionSync(): void; + tables(catalog: string | null, schema: string | null, table: string | null, type: string | null, cb: (err: any, result: ODBCTable[]) => void): void; + columns(catalog: string | null, schema: string | null, table: string | null, column: string | null, cb: (err: any, result: ODBCColumn[]) => void): void; + describe(options: DescribeOptions, cb: (err: any, result: (ODBCTable & ODBCColumn)[]) => void): void; + } + + export class Pool { + constructor(options?: DatabaseOptions); + open(connctionString: string, cb: (err: any, db: Database) => void): void; + close(cb: (err: any) => void): void; + } + + export function open(connctionString: string | ConnctionInfo, cb: (err: any, result: any) => void): void; +} + +export = odbc; From 0b5978e0a680a43ddb90a23e8dc959c49639cde8 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 7 Aug 2017 09:49:03 -0400 Subject: [PATCH 485/511] 1.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 53358b48..353e4b83 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.2.1", + "version": "1.3.0", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From 727db01e1861c788276d5006807d33af61c2d9d0 Mon Sep 17 00:00:00 2001 From: Quentin Rousseau Date: Mon, 12 Feb 2018 22:08:11 -0800 Subject: [PATCH 486/511] Update README to install unixODBC using brew --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7668ffc6..9f4b19e1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ requirements * unixODBC binaries and development libraries for module compilation * on Ubuntu/Debian `sudo apt-get install unixodbc unixodbc-dev` * on RedHat/CentOS `sudo yum install unixODBC unixODBC-devel` - * on OSX using macports.org `sudo port unixODBC` + * on OSX + * using macports.org `sudo port unixODBC` + * using brew `brew install unixODBC` * odbc drivers for target database * properly configured odbc.ini and odbcinst.ini. From 9db6a691c433de4e7301bf30b73dae5591411c8f Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 14 Mar 2018 14:57:59 -0400 Subject: [PATCH 487/511] Update nan to latest (v2.9.2) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 353e4b83..752e3a44 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ }, "dependencies": { "bindings": "^1.2.1", - "nan": "^2.0.9" + "nan": "^2.9.2" }, "gypfile": true } From 3ba64cf7fbddad15a504607a1933857472bffbe3 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Wed, 14 Mar 2018 14:58:02 -0400 Subject: [PATCH 488/511] 1.3.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 752e3a44..4431bd49 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.3.0", + "version": "1.3.1", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From d22e09b209622792d63aac66281d1a52f8360071 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 19 Mar 2018 23:53:24 -0400 Subject: [PATCH 489/511] Add ODBCResult::GetRowCountSync for issue #116 --- src/odbc_result.cpp | 22 +++++++++++++++ src/odbc_result.h | 1 + test/test-queryResultSync-getRowCount.js | 34 ++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 test/test-queryResultSync-getRowCount.js diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index c71c7a24..9858a458 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -54,6 +54,7 @@ void ODBCResult::Init(v8::Handle exports) { Nan::SetPrototypeMethod(constructor_template, "fetchSync", FetchSync); Nan::SetPrototypeMethod(constructor_template, "fetchAllSync", FetchAllSync); Nan::SetPrototypeMethod(constructor_template, "getColumnNamesSync", GetColumnNamesSync); + Nan::SetPrototypeMethod(constructor_template, "getRowCountSync", GetRowCountSync); // Properties OPTION_FETCH_MODE.Reset(Nan::New("fetchMode").ToLocalChecked()); @@ -750,3 +751,24 @@ NAN_METHOD(ODBCResult::GetColumnNamesSync) { info.GetReturnValue().Set(cols); } + +/* + * GetRowCountSync + */ + +NAN_METHOD(ODBCResult::GetRowCountSync) { + DEBUG_PRINTF("ODBCResult::GetRowCountSync\n"); + Nan::HandleScope scope; + + ODBCResult* self = Nan::ObjectWrap::Unwrap(info.Holder()); + + SQLLEN rowCount = 0; + + SQLRETURN ret = SQLRowCount(self->m_hSTMT, &rowCount); + + if (!SQL_SUCCEEDED(ret)) { + rowCount = 0; + } + + info.GetReturnValue().Set(Nan::New(rowCount)); +} diff --git a/src/odbc_result.h b/src/odbc_result.h index b75778ae..f100614d 100644 --- a/src/odbc_result.h +++ b/src/odbc_result.h @@ -62,6 +62,7 @@ class ODBCResult : public Nan::ObjectWrap { static NAN_METHOD(FetchSync); static NAN_METHOD(FetchAllSync); static NAN_METHOD(GetColumnNamesSync); + static NAN_METHOD(GetRowCountSync); //property getter/setters static NAN_GETTER(FetchModeGetter); diff --git a/test/test-queryResultSync-getRowCount.js b/test/test-queryResultSync-getRowCount.js new file mode 100644 index 00000000..b6fa2d64 --- /dev/null +++ b/test/test-queryResultSync-getRowCount.js @@ -0,0 +1,34 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.openSync(common.connectionString); +assert.equal(db.connected, true); + +common.dropTables(db, function () { + common.createTables(db, function (err, data) { + if (err) { + console.log(err); + + return finish(2); + } + + var rs = db.queryResultSync("insert into " + common.tableName + " (colint, coltext) VALUES (100, 'hello world')"); + assert.equal(rs.constructor.name, "ODBCResult"); + + assert.equal(rs.getRowCountSync(), 1); + + common.dropTables(db, function () { + return finish(0); + }); + }); +}); + +function finish(retValue) { + console.log("finish exit value: %s", retValue); + + db.closeSync(); + process.exit(retValue || 0); +} From a4d63edb0ee0a4f0f0b75816f003e5dc90ef74fa Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 20 Mar 2018 00:06:07 -0400 Subject: [PATCH 490/511] Fix broken ODBCResult::GetColumnNamesSync Needed to do the UNICODE IFDEF around the code that gets the column names. Added a test for this feature as well --- src/odbc_result.cpp | 8 +++++++- test/test-queryResultSync-getColumnNamesSync.js | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 test/test-queryResultSync-getColumnNamesSync.js diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index 9858a458..c043ae33 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -745,8 +745,14 @@ NAN_METHOD(ODBCResult::GetColumnNamesSync) { } for (int i = 0; i < self->colCount; i++) { +#ifdef UNICODE cols->Set(Nan::New(i), - Nan::New((const char *) self->columns[i].name).ToLocalChecked()); + Nan::New((uint16_t*) self->columns[i].name).ToLocalChecked()); +#else + cols->Set(Nan::New(i), + Nan::New((char *) self->columns[i].name).ToLocalChecked()); +#endif + } info.GetReturnValue().Set(cols); diff --git a/test/test-queryResultSync-getColumnNamesSync.js b/test/test-queryResultSync-getColumnNamesSync.js new file mode 100644 index 00000000..67d95d95 --- /dev/null +++ b/test/test-queryResultSync-getColumnNamesSync.js @@ -0,0 +1,14 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert") + ; + +db.openSync(common.connectionString); +assert.equal(db.connected, true); + +var rs = db.queryResultSync("select 1 as SomeIntField, 'string' as someStringField"); + +assert.deepEqual(rs.getColumnNamesSync(), ['SomeIntField', 'someStringField']); + +db.closeSync(); From 0f4b9c467b5e0d2c4ad7667e4c82b9b714ebf139 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 22 Mar 2018 12:24:14 -0400 Subject: [PATCH 491/511] 1.3.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4431bd49..d61a5377 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.3.1", + "version": "1.3.2", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From a927a263d00bbcb3e74f175b849f942b12ec65a0 Mon Sep 17 00:00:00 2001 From: Ondrej Patrovic Date: Mon, 2 Apr 2018 14:24:14 -0400 Subject: [PATCH 492/511] Added ODBCConnection::GetInfoSync using SQLGetInfo Synchronous implementation of SQLGetInfo via getInfoSync. Currently only supports SQL_USER_NAME as an argument. Also added SQL_USER_NAME as a constant within ODBC. --- src/odbc.cpp | 1 + src/odbc_connection.cpp | 49 ++++++++++++++++++++++++++++++++++++++++- src/odbc_connection.h | 1 + 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/odbc.cpp b/src/odbc.cpp index 41d2f11e..aecf140a 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -68,6 +68,7 @@ void ODBC::Init(v8::Handle exports) { constructor_template->Set(Nan::New("SQL_RESET_PARAMS").ToLocalChecked(), Nan::New(SQL_RESET_PARAMS), constant_attributes); constructor_template->Set(Nan::New("SQL_DESTROY").ToLocalChecked(), Nan::New(SQL_DESTROY), constant_attributes); constructor_template->Set(Nan::New("FETCH_ARRAY").ToLocalChecked(), Nan::New(FETCH_ARRAY), constant_attributes); + constructor_template->Set(Nan::New("SQL_USER_NAME").ToLocalChecked(), Nan::New(SQL_USER_NAME), constant_attributes); NODE_ODBC_DEFINE_CONSTANT(constructor_template, FETCH_OBJECT); // Prototype Methods diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 3faa7263..251aa9ca 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -72,7 +72,9 @@ void ODBCConnection::Init(v8::Handle exports) { Nan::SetPrototypeMethod(constructor_template, "beginTransactionSync", BeginTransactionSync); Nan::SetPrototypeMethod(constructor_template, "endTransaction", EndTransaction); Nan::SetPrototypeMethod(constructor_template, "endTransactionSync", EndTransactionSync); - + + Nan::SetPrototypeMethod(constructor_template, "getInfoSync", GetInfoSync); + Nan::SetPrototypeMethod(constructor_template, "columns", Columns); Nan::SetPrototypeMethod(constructor_template, "tables", Tables); @@ -1143,6 +1145,51 @@ NAN_METHOD(ODBCConnection::QuerySync) { } } + +/* + * GetInfoSync + */ + +NAN_METHOD(ODBCConnection::GetInfoSync) { + DEBUG_PRINTF("ODBCConnection::GetInfoSync\n"); + Nan::HandleScope scope; + + ODBCConnection* conn = Nan::ObjectWrap::Unwrap(info.Holder()); + + if (info.Length() == 1) { + if ( !info[0]->IsNumber() ) { + return Nan::ThrowTypeError("ODBCConnection::GetInfoSync(): Argument 0 must be a Number."); + } + } + else { + return Nan::ThrowTypeError("ODBCConnection::GetInfoSync(): Requires 1 Argument."); + } + + SQLUSMALLINT InfoType = info[0]->NumberValue(); + + switch (InfoType) { + case SQL_USER_NAME: + SQLRETURN ret; + SQLTCHAR userName[255]; + SQLSMALLINT userNameLength; + + ret = SQLGetInfo(conn->m_hDBC, SQL_USER_NAME, userName, sizeof(userName), &userNameLength); + + if (SQL_SUCCEEDED(ret)) { +#ifdef UNICODE + info.GetReturnValue().Set(Nan::New((uint16_t *)userName).ToLocalChecked()); +#else + info.GetReturnValue().Set(Nan::New(userName).ToLocalChecked()); +#endif + } + break; + + default: + return Nan::ThrowTypeError("ODBCConnection::GetInfoSync(): The only supported Argument is SQL_USER_NAME."); + } +} + + /* * Tables */ diff --git a/src/odbc_connection.h b/src/odbc_connection.h index a5131df0..5da34417 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -106,6 +106,7 @@ class ODBCConnection : public Nan::ObjectWrap { static NAN_METHOD(QuerySync); static NAN_METHOD(BeginTransactionSync); static NAN_METHOD(EndTransactionSync); + static NAN_METHOD(GetInfoSync); protected: struct Fetch_Request { From a9d66277146e4a05f1328d1ff2490d2096102d52 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 2 Apr 2018 15:38:39 -0400 Subject: [PATCH 493/511] Add test for getInfoSync() --- test/common.js | 2 ++ test/config.testConnectionStrings.json | 8 ++++---- test/test-getInfoSync.js | 10 ++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 test/test-getInfoSync.js diff --git a/test/common.js b/test/common.js index 2941e72d..552cfae8 100644 --- a/test/common.js +++ b/test/common.js @@ -6,6 +6,7 @@ var odbc = require("../"); exports.connectionString = "DRIVER={SQLite3};DATABASE=data/sqlite-test.db"; exports.title = "Sqlite3"; exports.dialect = "sqlite"; +exports.user = ""; if (process.argv.length === 3) { exports.connectionString = process.argv[2]; @@ -39,6 +40,7 @@ if (process.argv.length === 3) { if (connectionString && (connectionString.title == lookup || connectionString.connectionString == lookup)) { exports.connectionString = connectionString.connectionString; exports.dialect = connectionString.dialect; + exports.user = connectionString.user; } }); } diff --git a/test/config.testConnectionStrings.json b/test/config.testConnectionStrings.json index 0e5b7168..1954bfae 100644 --- a/test/config.testConnectionStrings.json +++ b/test/config.testConnectionStrings.json @@ -1,6 +1,6 @@ [ - { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db", "dialect" : "sqlite" } - , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;SOCKET=/var/run/mysqld/mysqld.sock;USER=test;", "dialect" : "mysql" } - , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test;AutoTranslate=yes;TEXTSIZE=10000000", "dialect" : "mssql" } - , { "title" : "MSSQL-NativeCLI-Remote", "connectionString" : "DRIVER={SQL Server Native Client 11.0};SERVER=sql2;DATABASE=test;UID=test;PWD=test;", "dialect": "mssql" } + { "title" : "Sqlite3", "connectionString" : "DRIVER={SQLite3};DATABASE=data/sqlite-test.db", "dialect" : "sqlite", "user": "" } + , { "title" : "MySQL-Local", "connectionString" : "DRIVER={MySQL};DATABASE=test;HOST=localhost;SOCKET=/var/run/mysqld/mysqld.sock;USER=test;", "dialect" : "mysql", "user" : "test" } + , { "title" : "MSSQL-FreeTDS-Remote", "connectionString" : "DRIVER={FreeTDS};SERVERNAME=sql2;DATABASE=test;UID=test;PWD=test;AutoTranslate=yes;TEXTSIZE=10000000", "dialect" : "mssql", "user" : "test" } + , { "title" : "MSSQL-NativeCLI-Remote", "connectionString" : "DRIVER={SQL Server Native Client 11.0};SERVER=sql2;DATABASE=test;UID=test;PWD=test;", "dialect": "mssql", "user" : "test" } ] diff --git a/test/test-getInfoSync.js b/test/test-getInfoSync.js new file mode 100644 index 00000000..516d4246 --- /dev/null +++ b/test/test-getInfoSync.js @@ -0,0 +1,10 @@ +var common = require("./common") + , odbc = require("../") + , db = new odbc.Database() + , assert = require("assert"); + +db.openSync(common.connectionString); +console.log(common); +var userName = db.conn.getInfoSync(odbc.SQL_USER_NAME); +assert.equal(userName, common.user); + From 3ba2e7372a0d1578b89159d33f0bb3c3dcc393c7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Mon, 2 Apr 2018 15:39:00 -0400 Subject: [PATCH 494/511] 1.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d61a5377..72c1a426 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.3.2", + "version": "1.4.0", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From 2ec659b5cceacf54d783a81d0dadfc63d27c9bca Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 6 Apr 2018 23:07:32 -0400 Subject: [PATCH 495/511] fix: add (const char *) to non-unicode GetInfoSync() case; fixes #118 --- src/odbc_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 251aa9ca..315bb91e 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -1179,7 +1179,7 @@ NAN_METHOD(ODBCConnection::GetInfoSync) { #ifdef UNICODE info.GetReturnValue().Set(Nan::New((uint16_t *)userName).ToLocalChecked()); #else - info.GetReturnValue().Set(Nan::New(userName).ToLocalChecked()); + info.GetReturnValue().Set(Nan::New((const char *) userName).ToLocalChecked()); #endif } break; From a884bf69dc4931cdcf493946ff0c7a196b7c163e Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 6 Apr 2018 23:07:58 -0400 Subject: [PATCH 496/511] 1.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 72c1a426..c59d6aea 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.4.0", + "version": "1.4.1", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From 6f7206ac6f0d5acfee03ecdb9b22340686a584c7 Mon Sep 17 00:00:00 2001 From: Abdirahim Musse Date: Wed, 9 May 2018 21:23:00 -0500 Subject: [PATCH 497/511] Just adjusted edited a few line to Allow for AIX compiling --- src/odbc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index aecf140a..d46a8581 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -446,8 +446,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, , tm_wday : 0 , tm_yday : 0 , tm_isdst : 0 + #ifndef _AIX //AIX does not have these , tm_gmtoff : 0 , tm_zone : 0 + #endif }; SQL_TIMESTAMP_STRUCT odbcTime; @@ -483,6 +485,9 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, return scope.Escape(Nan::New((double(timegm(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000)).ToLocalChecked()); #else +#ifdef _AIX + #define timelocal mktime +#endif return scope.Escape(Nan::New((double(timelocal(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000)).ToLocalChecked()); #endif From ff2502005f73b0f517c21e29c89271f33433366e Mon Sep 17 00:00:00 2001 From: Joshua Pinter Date: Sat, 26 May 2018 23:11:01 -0400 Subject: [PATCH 498/511] Add `.connected` to README. It's a useful method/attribute to call so it should be in the README. --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 7668ffc6..cc77c4b7 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,16 @@ var Database = require("odbc").Database , db = new Database(); ``` +#### .connected + +Returns a Boolean of whether the database is currently connected. + +```javascript +var db = require("odbc")(); + +console.log( "Connected: " + db.connected ); +``` + #### .open(connectionString, callback) Open a connection to a database. From 8e5244b79decac1fc1682eb6248cf5008183ffc7 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 22 Jun 2018 09:47:29 -0400 Subject: [PATCH 499/511] fixes for node v10 --- package.json | 4 ++-- src/odbc.cpp | 10 +++++----- src/odbc_connection.cpp | 9 ++++----- src/odbc_result.cpp | 8 ++++---- src/odbc_statement.cpp | 10 +++++----- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index c59d6aea..21d3ca2e 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "test": "cd test && node run-tests.js" }, "dependencies": { - "bindings": "^1.2.1", - "nan": "^2.9.2" + "bindings": "^1.3.0", + "nan": "^2.10.0" }, "gypfile": true } diff --git a/src/odbc.cpp b/src/odbc.cpp index aecf140a..6fd59dfd 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -205,7 +205,7 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { info[0] = Nan::New(data->dbo->m_hEnv); info[1] = Nan::New(data->hDBC); - Local js_result = Nan::New(ODBCConnection::constructor)->NewInstance(2, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCConnection::constructor), 2, info).ToLocalChecked(); info[0] = Nan::Null(); info[1] = js_result; @@ -252,7 +252,7 @@ NAN_METHOD(ODBC::CreateConnectionSync) { params[0] = Nan::New(dbo->m_hEnv); params[1] = Nan::New(hDBC); - Local js_result = Nan::New(ODBCConnection::constructor)->NewInstance(2, params); + Local js_result = Nan::NewInstance(Nan::New(ODBCConnection::constructor), 2, params).ToLocalChecked(); info.GetReturnValue().Set(js_result); } @@ -848,11 +848,11 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* // First error is assumed the primary error objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message).ToLocalChecked()); #ifdef UNICODE - objError->SetPrototype(Exception::Error(Nan::New((uint16_t *)errorMessage).ToLocalChecked())); + Nan::SetPrototype(objError, Exception::Error(Nan::New((uint16_t *) errorMessage).ToLocalChecked())); objError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage).ToLocalChecked()); objError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState).ToLocalChecked()); #else - objError->SetPrototype(Exception::Error(Nan::New(errorMessage).ToLocalChecked())); + Nan::SetPrototype(objError, Exception::Error(Nan::New(errorMessage).ToLocalChecked())); objError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage).ToLocalChecked()); objError->Set(Nan::New("state").ToLocalChecked(), Nan::New(errorSQLState).ToLocalChecked()); #endif @@ -877,7 +877,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* if (statusRecCount == 0) { //Create a default error object if there were no diag records objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message).ToLocalChecked()); - objError->SetPrototype(Exception::Error(Nan::New(message).ToLocalChecked())); + Nan::SetPrototype(objError, Exception::Error(Nan::New(message).ToLocalChecked())); objError->Set(Nan::New("message").ToLocalChecked(), Nan::New( (const char *) "[node-odbc] An error occurred but no diagnostic information was available.").ToLocalChecked()); } diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 315bb91e..3e748ee1 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -556,7 +556,7 @@ NAN_METHOD(ODBCConnection::CreateStatementSync) { params[1] = Nan::New(conn->m_hDBC); params[2] = Nan::New(hSTMT); - Local js_result(Nan::New(ODBCStatement::constructor)->NewInstance(3, params)); + Local js_result(Nan::NewInstance(Nan::New(ODBCStatement::constructor), 3, params).ToLocalChecked()); info.GetReturnValue().Set(js_result); } @@ -645,12 +645,11 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { info[1] = Nan::New(data->conn->m_hDBC); info[2] = Nan::New(data->hSTMT); - Local js_result = Nan::New(ODBCStatement::constructor)->NewInstance(3, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCStatement::constructor), 3, info).ToLocalChecked(); info[0] = Nan::Null(); info[1] = js_result; - Nan::TryCatch try_catch; data->cb->Call( 2, info); @@ -890,7 +889,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { info[2] = Nan::New(data->hSTMT); info[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, info).ToLocalChecked(); // Check now to see if there was an error (as there may be further result sets) if (data->result == SQL_ERROR) { @@ -1139,7 +1138,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { result[2] = Nan::New(hSTMT); result[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, result); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, result).ToLocalChecked(); info.GetReturnValue().Set(js_result); } diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index c043ae33..1c1a7160 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -185,7 +185,7 @@ NAN_METHOD(ODBCResult::Fetch) { Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { - data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); + data->fetchMode = Nan::To(obj->Get(fetchModeKey)).ToLocalChecked()->Value(); } } else { @@ -338,7 +338,7 @@ NAN_METHOD(ODBCResult::FetchSync) { Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { - fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); + fetchMode = Nan::To(obj->Get(fetchModeKey)).ToLocalChecked()->Value(); } } @@ -434,7 +434,7 @@ NAN_METHOD(ODBCResult::FetchAll) { Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { - data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); + data->fetchMode = Nan::To(obj->Get(fetchModeKey)).ToLocalChecked()->Value(); } } else { @@ -595,7 +595,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { - fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); + fetchMode = Nan::To(obj->Get(fetchModeKey)).ToLocalChecked()->Value(); } } diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 2447c86b..9f92ecc8 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -216,7 +216,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { info[2] = Nan::New(self->m_hSTMT); info[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, info).ToLocalChecked(); info[0] = Nan::Null(); info[1] = js_result; @@ -271,7 +271,7 @@ NAN_METHOD(ODBCStatement::ExecuteSync) { result[2] = Nan::New(stmt->m_hSTMT); result[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, result); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, result).ToLocalChecked(); info.GetReturnValue().Set(js_result); } @@ -504,7 +504,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { info[2] = Nan::New(self->m_hSTMT); info[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, info).ToLocalChecked(); info[0] = Nan::Null(); info[1] = js_result; @@ -569,8 +569,8 @@ NAN_METHOD(ODBCStatement::ExecuteDirectSync) { result[2] = Nan::New(stmt->m_hSTMT); result[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, result); - + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, result).ToLocalChecked(); + info.GetReturnValue().Set(js_result); } } From 7ea73330f039134c2a6f8b91a0ad67c9a58b2d15 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 22 Jun 2018 09:48:45 -0400 Subject: [PATCH 500/511] 1.4.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21d3ca2e..afe28cb0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.4.1", + "version": "1.4.2", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From 24750cc6686b0c8d2d88757f9a2311af53144256 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 24 Jul 2018 16:23:09 -0400 Subject: [PATCH 501/511] 1.4.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index afe28cb0..a9053ebc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.4.2", + "version": "1.4.3", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From 7f88df4844d779727744e1fb5bd5a76c8dbb4b8c Mon Sep 17 00:00:00 2001 From: Abdirahim Musse Date: Wed, 9 May 2018 21:23:00 -0500 Subject: [PATCH 502/511] Just adjusted edited a few line to Allow for AIX compiling --- src/odbc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/odbc.cpp b/src/odbc.cpp index aecf140a..d46a8581 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -446,8 +446,10 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, , tm_wday : 0 , tm_yday : 0 , tm_isdst : 0 + #ifndef _AIX //AIX does not have these , tm_gmtoff : 0 , tm_zone : 0 + #endif }; SQL_TIMESTAMP_STRUCT odbcTime; @@ -483,6 +485,9 @@ Handle ODBC::GetColumnValue( SQLHSTMT hStmt, Column column, return scope.Escape(Nan::New((double(timegm(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000)).ToLocalChecked()); #else +#ifdef _AIX + #define timelocal mktime +#endif return scope.Escape(Nan::New((double(timelocal(&timeInfo)) * 1000) + (odbcTime.fraction / 1000000)).ToLocalChecked()); #endif From 0525014b24dace497ed1acbb090959c808ce2334 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 22 Jun 2018 09:47:29 -0400 Subject: [PATCH 503/511] fixes for node v10 --- package.json | 4 ++-- src/odbc.cpp | 10 +++++----- src/odbc_connection.cpp | 9 ++++----- src/odbc_result.cpp | 8 ++++---- src/odbc_statement.cpp | 10 +++++----- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index c59d6aea..21d3ca2e 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "test": "cd test && node run-tests.js" }, "dependencies": { - "bindings": "^1.2.1", - "nan": "^2.9.2" + "bindings": "^1.3.0", + "nan": "^2.10.0" }, "gypfile": true } diff --git a/src/odbc.cpp b/src/odbc.cpp index d46a8581..114b1f4b 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -205,7 +205,7 @@ void ODBC::UV_AfterCreateConnection(uv_work_t* req, int status) { info[0] = Nan::New(data->dbo->m_hEnv); info[1] = Nan::New(data->hDBC); - Local js_result = Nan::New(ODBCConnection::constructor)->NewInstance(2, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCConnection::constructor), 2, info).ToLocalChecked(); info[0] = Nan::Null(); info[1] = js_result; @@ -252,7 +252,7 @@ NAN_METHOD(ODBC::CreateConnectionSync) { params[0] = Nan::New(dbo->m_hEnv); params[1] = Nan::New(hDBC); - Local js_result = Nan::New(ODBCConnection::constructor)->NewInstance(2, params); + Local js_result = Nan::NewInstance(Nan::New(ODBCConnection::constructor), 2, params).ToLocalChecked(); info.GetReturnValue().Set(js_result); } @@ -853,11 +853,11 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* // First error is assumed the primary error objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message).ToLocalChecked()); #ifdef UNICODE - objError->SetPrototype(Exception::Error(Nan::New((uint16_t *)errorMessage).ToLocalChecked())); + Nan::SetPrototype(objError, Exception::Error(Nan::New((uint16_t *) errorMessage).ToLocalChecked())); objError->Set(Nan::New("message").ToLocalChecked(), Nan::New((uint16_t *)errorMessage).ToLocalChecked()); objError->Set(Nan::New("state").ToLocalChecked(), Nan::New((uint16_t *)errorSQLState).ToLocalChecked()); #else - objError->SetPrototype(Exception::Error(Nan::New(errorMessage).ToLocalChecked())); + Nan::SetPrototype(objError, Exception::Error(Nan::New(errorMessage).ToLocalChecked())); objError->Set(Nan::New("message").ToLocalChecked(), Nan::New(errorMessage).ToLocalChecked()); objError->Set(Nan::New("state").ToLocalChecked(), Nan::New(errorSQLState).ToLocalChecked()); #endif @@ -882,7 +882,7 @@ Local ODBC::GetSQLError (SQLSMALLINT handleType, SQLHANDLE handle, char* if (statusRecCount == 0) { //Create a default error object if there were no diag records objError->Set(Nan::New("error").ToLocalChecked(), Nan::New(message).ToLocalChecked()); - objError->SetPrototype(Exception::Error(Nan::New(message).ToLocalChecked())); + Nan::SetPrototype(objError, Exception::Error(Nan::New(message).ToLocalChecked())); objError->Set(Nan::New("message").ToLocalChecked(), Nan::New( (const char *) "[node-odbc] An error occurred but no diagnostic information was available.").ToLocalChecked()); } diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 315bb91e..3e748ee1 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -556,7 +556,7 @@ NAN_METHOD(ODBCConnection::CreateStatementSync) { params[1] = Nan::New(conn->m_hDBC); params[2] = Nan::New(hSTMT); - Local js_result(Nan::New(ODBCStatement::constructor)->NewInstance(3, params)); + Local js_result(Nan::NewInstance(Nan::New(ODBCStatement::constructor), 3, params).ToLocalChecked()); info.GetReturnValue().Set(js_result); } @@ -645,12 +645,11 @@ void ODBCConnection::UV_AfterCreateStatement(uv_work_t* req, int status) { info[1] = Nan::New(data->conn->m_hDBC); info[2] = Nan::New(data->hSTMT); - Local js_result = Nan::New(ODBCStatement::constructor)->NewInstance(3, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCStatement::constructor), 3, info).ToLocalChecked(); info[0] = Nan::Null(); info[1] = js_result; - Nan::TryCatch try_catch; data->cb->Call( 2, info); @@ -890,7 +889,7 @@ void ODBCConnection::UV_AfterQuery(uv_work_t* req, int status) { info[2] = Nan::New(data->hSTMT); info[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, info).ToLocalChecked(); // Check now to see if there was an error (as there may be further result sets) if (data->result == SQL_ERROR) { @@ -1139,7 +1138,7 @@ NAN_METHOD(ODBCConnection::QuerySync) { result[2] = Nan::New(hSTMT); result[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, result); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, result).ToLocalChecked(); info.GetReturnValue().Set(js_result); } diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index c043ae33..1c1a7160 100644 --- a/src/odbc_result.cpp +++ b/src/odbc_result.cpp @@ -185,7 +185,7 @@ NAN_METHOD(ODBCResult::Fetch) { Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { - data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); + data->fetchMode = Nan::To(obj->Get(fetchModeKey)).ToLocalChecked()->Value(); } } else { @@ -338,7 +338,7 @@ NAN_METHOD(ODBCResult::FetchSync) { Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { - fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); + fetchMode = Nan::To(obj->Get(fetchModeKey)).ToLocalChecked()->Value(); } } @@ -434,7 +434,7 @@ NAN_METHOD(ODBCResult::FetchAll) { Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { - data->fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); + data->fetchMode = Nan::To(obj->Get(fetchModeKey)).ToLocalChecked()->Value(); } } else { @@ -595,7 +595,7 @@ NAN_METHOD(ODBCResult::FetchAllSync) { Local fetchModeKey = Nan::New(OPTION_FETCH_MODE); if (obj->Has(fetchModeKey) && obj->Get(fetchModeKey)->IsInt32()) { - fetchMode = obj->Get(fetchModeKey)->ToInt32()->Value(); + fetchMode = Nan::To(obj->Get(fetchModeKey)).ToLocalChecked()->Value(); } } diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 2447c86b..9f92ecc8 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -216,7 +216,7 @@ void ODBCStatement::UV_AfterExecute(uv_work_t* req, int status) { info[2] = Nan::New(self->m_hSTMT); info[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, info).ToLocalChecked(); info[0] = Nan::Null(); info[1] = js_result; @@ -271,7 +271,7 @@ NAN_METHOD(ODBCStatement::ExecuteSync) { result[2] = Nan::New(stmt->m_hSTMT); result[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, result); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, result).ToLocalChecked(); info.GetReturnValue().Set(js_result); } @@ -504,7 +504,7 @@ void ODBCStatement::UV_AfterExecuteDirect(uv_work_t* req, int status) { info[2] = Nan::New(self->m_hSTMT); info[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, info); + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, info).ToLocalChecked(); info[0] = Nan::Null(); info[1] = js_result; @@ -569,8 +569,8 @@ NAN_METHOD(ODBCStatement::ExecuteDirectSync) { result[2] = Nan::New(stmt->m_hSTMT); result[3] = Nan::New(canFreeHandle); - Local js_result = Nan::New(ODBCResult::constructor)->NewInstance(4, result); - + Local js_result = Nan::NewInstance(Nan::New(ODBCResult::constructor), 4, result).ToLocalChecked(); + info.GetReturnValue().Set(js_result); } } From ca69d383a932644c892e94720be46f524c6d5444 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Fri, 22 Jun 2018 09:48:45 -0400 Subject: [PATCH 504/511] 1.4.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21d3ca2e..afe28cb0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.4.1", + "version": "1.4.2", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From 2f90824f70b4a8ab2bee7f91a50f066994947d89 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 24 Jul 2018 16:23:09 -0400 Subject: [PATCH 505/511] 1.4.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index afe28cb0..a9053ebc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.4.2", + "version": "1.4.3", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From a77384683af63c44523835d6387ef827b1afd8d5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Tue, 24 Jul 2018 16:50:32 -0400 Subject: [PATCH 506/511] 1.4.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9053ebc..15647c0e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.4.3", + "version": "1.4.4", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From f08680c296b69cbfeabdf5c13b21ba2e04727de0 Mon Sep 17 00:00:00 2001 From: Abdirahim Musse Date: Wed, 25 Jul 2018 09:44:16 -0500 Subject: [PATCH 507/511] Added conditional to npm install correctly when on IBM i --- binding.gyp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/binding.gyp b/binding.gyp index a142ff13..52f55f6e 100644 --- a/binding.gyp +++ b/binding.gyp @@ -42,6 +42,18 @@ 'libraries' : [ '-lodbccp32.lib' ] + }], + [ 'OS=="aix"', { + 'variables': { + 'os_name': ' Date: Wed, 25 Jul 2018 12:07:05 -0500 Subject: [PATCH 508/511] Document IBM i install instructions --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 65ff72aa..7095b301 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ requirements * on OSX * using macports.org `sudo port unixODBC` * using brew `brew install unixODBC` + * on IBM i `yum install unixODBC unixODBC-devel` (requires [yum](http://ibm.biz/ibmi-rpms)) * odbc drivers for target database * properly configured odbc.ini and odbcinst.ini. From dfa40e3ad307fb2184f9e1264ef66f4e11afbea5 Mon Sep 17 00:00:00 2001 From: Dan VerWeire Date: Thu, 26 Jul 2018 08:51:04 -0400 Subject: [PATCH 509/511] 1.4.5 --- package-lock.json | 18 ++++++++++++++++++ package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..17417d41 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,18 @@ +{ + "name": "odbc", + "version": "1.4.5", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "bindings": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", + "integrity": "sha1-s0b27PapX1qBXFg5/HzbIlAvHtc=" + }, + "nan": { + "version": "2.10.0", + "resolved": "https://npm.paviliongift.com/nan/-/nan-2.10.0.tgz", + "integrity": "sha1-ltDNYQ69WNS03pzAxoKM2pnHVI8=" + } + } +} diff --git a/package.json b/package.json index 15647c0e..b001b25b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.4.4", + "version": "1.4.5", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/", From b21a45f27046ccc6f1d6d0665109424b74b37b92 Mon Sep 17 00:00:00 2001 From: Mark Irish Date: Fri, 5 Apr 2019 14:37:47 -0500 Subject: [PATCH 510/511] Adding files to .gitignore and advertising the v2.0.0-beta.0 release --- .gitignore | 5 ++++- README.md | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e17ea04b..24e554cf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ a.out build node_modules deps -.idea \ No newline at end of file +.idea +core +.env +.vscode \ No newline at end of file diff --git a/README.md b/README.md index 7095b301..dda7e0d5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +# node-odbc 2.0.0 is now in beta! +A new version of `odbc` has been released in beta! The initial release of this beta can be found at [https://www.npmjs.com/package/odbc/v/2.0.0-beta.0](https://www.npmjs.com/package/odbc/v/2.0.0-beta.0), while later releases can be found under the Versions tab on npm. This URL contains information including the README file outlining the new API. + +To install the beta version, include the `beta` tag with your command: +``` +npm install odbc@beta +``` + +Test it out and give feedback on the issues of the official git repository! + +--- + node-odbc --------- From 6f30c05bb81744fa21e339b9d91f6681794757aa Mon Sep 17 00:00:00 2001 From: Mark Irish Date: Fri, 5 Apr 2019 14:43:15 -0500 Subject: [PATCH 511/511] Updating version number so that it can be npm published --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b001b25b..98f8a0b3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "1.4.5", + "version": "1.4.6", "main": "lib/odbc.js", "types": "./lib/odbc.d.ts", "homepage": "http://github.com/wankdanker/node-odbc/",