diff --git a/.gitignore b/.gitignore index e17ea04..24e554c 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 7668ffc..dda7e0d 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 --------- @@ -10,7 +22,10 @@ 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` + * 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. @@ -83,6 +98,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. diff --git a/binding.gyp b/binding.gyp index e2aa3ff..071a218 100644 --- a/binding.gyp +++ b/binding.gyp @@ -38,6 +38,18 @@ ], 'libraries' : [ ] + }], + [ 'OS=="aix"', { + 'variables': { + 'os_name': ' 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 @@ -204,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; @@ -251,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); } @@ -445,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; @@ -482,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 @@ -847,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 @@ -876,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 3faa726..3e748ee 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); @@ -554,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); } @@ -643,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); @@ -888,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) { @@ -1137,12 +1138,57 @@ 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); } } + +/* + * 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((const char *) 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 a5131df..5da3441 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 { diff --git a/src/odbc_result.cpp b/src/odbc_result.cpp index c71c7a2..1c1a716 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()); @@ -184,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 { @@ -337,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(); } } @@ -433,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 { @@ -594,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(); } } @@ -744,9 +745,36 @@ 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); } + +/* + * 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 b75778a..f100614 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/src/odbc_statement.cpp b/src/odbc_statement.cpp index 2447c86..9f92ecc 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); } } diff --git a/test/common.js b/test/common.js index 2941e72..552cfae 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 0e5b716..1954bfa 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 0000000..516d424 --- /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); + diff --git a/test/test-queryResultSync-getColumnNamesSync.js b/test/test-queryResultSync-getColumnNamesSync.js new file mode 100644 index 0000000..67d95d9 --- /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(); diff --git a/test/test-queryResultSync-getRowCount.js b/test/test-queryResultSync-getRowCount.js new file mode 100644 index 0000000..b6fa2d6 --- /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); +}