diff --git a/lib/sqlite3.js b/lib/sqlite3.js index 032de4b03..7c37da7df 100644 --- a/lib/sqlite3.js +++ b/lib/sqlite3.js @@ -17,6 +17,8 @@ function inherits(target, source) { sqlite3.cached = { Database: function(file, a, b) { + EventEmitter.call(this) + if (file === '' || file === ':memory:') { // Don't cache special databases. return new Database(file, a, b); @@ -55,20 +57,28 @@ inherits(Statement, EventEmitter); // Database#prepare(sql, [bind1, bind2, ...], [callback]) Database.prototype.prepare = function(sql) { var params = Array.prototype.slice.call(arguments, 1); + var statement; if (!params.length || (params.length === 1 && typeof params[0] === 'function')) { - return new Statement(this, sql, params[0]); + statement = new Statement(this, sql, params[0]); } else { - var statement = new Statement(this, sql, errorCallback(params)); - return statement.bind.apply(statement, params); + statement = new Statement(this, sql, errorCallback(params)); + statement = statement.bind.apply(statement, params); } + + statement.domain = process.domain; + + return statement }; // Database#run(sql, [bind1, bind2, ...], [callback]) Database.prototype.run = function(sql) { var params = Array.prototype.slice.call(arguments, 1); var statement = new Statement(this, sql, errorCallback(params)); + + statement.domain = process.domain; + statement.run.apply(statement, params).finalize(); return this; }; @@ -77,6 +87,9 @@ Database.prototype.run = function(sql) { Database.prototype.get = function(sql) { var params = Array.prototype.slice.call(arguments, 1); var statement = new Statement(this, sql, errorCallback(params)); + + statement.domain = process.domain; + statement.get.apply(statement, params).finalize(); return this; }; @@ -85,6 +98,9 @@ Database.prototype.get = function(sql) { Database.prototype.all = function(sql) { var params = Array.prototype.slice.call(arguments, 1); var statement = new Statement(this, sql, errorCallback(params)); + + statement.domain = process.domain; + statement.all.apply(statement, params).finalize(); return this; }; @@ -93,6 +109,9 @@ Database.prototype.all = function(sql) { Database.prototype.each = function(sql) { var params = Array.prototype.slice.call(arguments, 1); var statement = new Statement(this, sql, errorCallback(params)); + + statement.domain = process.domain; + statement.each.apply(statement, params).finalize(); return this; }; @@ -100,6 +119,9 @@ Database.prototype.each = function(sql) { Database.prototype.map = function(sql) { var params = Array.prototype.slice.call(arguments, 1); var statement = new Statement(this, sql, errorCallback(params)); + + statement.domain = process.domain; + statement.map.apply(statement, params).finalize(); return this; }; diff --git a/src/macros.h b/src/macros.h index d5c91fd9a..17caeaac7 100644 --- a/src/macros.h +++ b/src/macros.h @@ -1,5 +1,6 @@ #ifndef NODE_SQLITE3_SRC_MACROS_H #define NODE_SQLITE3_SRC_MACROS_H +#include const char* sqlite_code_string(int code); const char* sqlite_authorizer_string(int type); @@ -114,12 +115,18 @@ const char* sqlite_authorizer_string(int type); argc, argv \ ); +#if NODE_VERSION_AT_LEAST(0,8,0) +#define TRY_CATCH_CALL(context, callback, argc, argv) \ +{ MakeCallback(context, callback, argc, argv); } +#else #define TRY_CATCH_CALL(context, callback, argc, argv) \ { TryCatch try_catch; \ (callback)->Call((context), (argc), (argv)); \ if (try_catch.HasCaught()) { \ FatalException(try_catch); \ } } +#endif + #define WORK_DEFINITION(name) \ static Handle name(const Arguments& args); \ diff --git a/test/domain.test.js b/test/domain.test.js new file mode 100644 index 000000000..6e57ebfcd --- /dev/null +++ b/test/domain.test.js @@ -0,0 +1,107 @@ +var sqlite3 = require('sqlite3'); +var assert = require('assert'); +var shouldSkip = false; +var domain; + +try { + domain = require('domain'); +} catch (e) { + shouldSkip = true; +} + + +if(shouldSkip) { + exports['skipping domain tests'] = function(beforeExit) { + beforeExit(function() { + + }); + }; + return; +} + +if (process.setMaxListeners) process.setMaxListeners(0); + +var protect = function(fn) { + return function(beforeExit) { + var oldListeners = process.listeners('uncaughtException').splice(0); + + oldListeners.filter(function(fn) { + // XXX: This is a bit of a hack: + // we know the name of the function that + // the domain module adds as a listener + // for `uncaughtException`, and we want + // that to remain available -- but we + // don't want expresso's uncaughtHandler + // to be fired as well. + return fn.name === 'uncaughtHandler' + }).forEach(function(fn) { + process.on('uncaughtException', fn) + }) + + var realBeforeExit; + var injectBeforeExit = function(fn) { + realBeforeExit = fn; + }; + + beforeExit(function() { + var listeners = process.listeners('uncaughtException'); + + // put expresso's uncaughtException handler + // back in place. + listeners.splice.apply( + listeners, [0, listeners.length].concat(oldListeners) + ); + + // call the real `beforeExit` function defined + // by the test, if any. + if (realBeforeExit) { + realBeforeExit(); + } + }) + + fn(injectBeforeExit); + }; +} + +var testStatementMethod = function(method) { + return function(beforeExit) { + var dom = domain.create(); + var db = new sqlite3.Database(':memory:'); + var caughtCount = 0; + var expected = 1 + ~~(Math.random() * 10); + + dom.on('error', function(err) { + ++caughtCount; + }); + + dom.run(function() { + db[method]('garbled nonsense', function(err) { + // set a timeout, so that we ensure that the + // domain chain gets passed along. + + for (var i = 0; i < expected; ++i) { + setTimeout(function() { + if (err) { + throw err; + } + }, 0); + } + }); + }); + + beforeExit(function() { + assert.equal(caughtCount, expected); + }); + }; +} + +exports['test Database#run works with domains'] = protect(testStatementMethod('run')); +exports['test Database#prepare works with domains'] = protect(testStatementMethod('prepare')); +exports['test Database#get works with domains'] = protect(testStatementMethod('get')); +exports['test Database#map works with domains'] = protect(testStatementMethod('all')); +exports['test Database#each works with domains'] = protect(testStatementMethod('each')); +exports['test Database#map works with domains'] = protect(testStatementMethod('map')); + + + +