diff --git a/lib/sqlite3.js b/lib/sqlite3.js index be9205c50..54483f3e0 100644 --- a/lib/sqlite3.js +++ b/lib/sqlite3.js @@ -22,6 +22,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); @@ -57,6 +59,28 @@ var Statement = sqlite3.Statement; inherits(Database, EventEmitter); inherits(Statement, EventEmitter); +// Managed counterpart of Database::new +Database.prototype._init = function() { + EventEmitter.call(this); +}; + +// Managed counterpart of Statement::new +Statement.prototype._init = function() { + EventEmitter.call(this); +}; + +var nativeExec = Database.prototype.exec; +// Database#exec(sql, [callback]) +Database.prototype.exec = function(sql) { + var params = arguments; + if (process.domain && params.length === 2 && typeof params[1] === 'function') { + params = Array.prototype.slice.call(params,0); + // Make sure the callback is called in the current domain. + params[1] = process.domain.bind(params[1]); + } + return nativeExec.apply(this,params); +}; + // Database#prepare(sql, [bind1, bind2, ...], [callback]) Database.prototype.prepare = function(sql) { var params = Array.prototype.slice.call(arguments, 1); diff --git a/src/database.cc b/src/database.cc index 50206e6c6..c3650f448 100644 --- a/src/database.cc +++ b/src/database.cc @@ -134,6 +134,9 @@ Handle Database::New(const Arguments& args) { OpenBaton* baton = new OpenBaton(db, callback, *filename, mode); Work_BeginOpen(baton); + Handle argv[0]; + MakeCallback(args.This(),"_init",0,argv); + return args.This(); } diff --git a/src/macros.h b/src/macros.h index dbd14d0c3..9a5319d95 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/src/statement.cc b/src/statement.cc index 86df3152e..91bf1b628 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -112,6 +112,9 @@ Handle Statement::New(const Arguments& args) { baton->sql = std::string(*String::Utf8Value(sql)); db->Schedule(Work_BeginPrepare, baton); + Handle argv[0]; + MakeCallback(args.This(),"_init",0,argv); + return args.This(); } diff --git a/test/domain.test.js b/test/domain.test.js new file mode 100644 index 000000000..43d8fd504 --- /dev/null +++ b/test/domain.test.js @@ -0,0 +1,99 @@ +var sqlite3 = require('..'); +var assert = require('assert'); +var domain; + +try { + domain = require('domain'); +} catch (e) { + return; +} + +function testThrowException(dom,done) { + var expectedException = 'THIS IS AN EXPECTED EXCEPTION'; + var oldListeners; + function handleExceptionListeners() { + // Detatch existing exception listeners (of Mocha) + oldListeners = process.listeners('uncaughtException').slice(0); + oldListeners.forEach(function(fn) { + process.removeListener('uncaughtException',fn); + }); + // Attach exception listeners of this test. + process.on('uncaughtException',processExceptionCallback); + dom.on('error',domainExceptionCallback); + } + function unhandleExceptionListeners() { + // Detach exception listeners of this test. + process.removeListener('uncaughtException',processExceptionCallback); + dom.removeListener('error',domainExceptionCallback); + // Reattach the existing exception listeners (of Mocha) + oldListeners.forEach(function(fn) { + process.on('uncaughtException',fn); + }); + } + function processExceptionCallback(e) { + unhandleExceptionListeners(); + done('Exception was not caught by domain:',e); + } + function domainExceptionCallback(e) { + unhandleExceptionListeners(); + assert.equal(expectedException,e); + + done(); + } + + handleExceptionListeners(); + + throw expectedException; +} + +describe('domain',function() { + describe('on database creation',function() { + var dom; + before(function() { + dom = domain.create(); + }); + + it('should work for open',function(done) { + dom.run(function() { + var db = new sqlite3.Database(':memory:',function() { + assert.equal(process.domain, dom); + testThrowException(dom,done); + }); + }); + }); + }); + + describe('on individual calls',function() { + var db,dom; + beforeEach(function(done) { + dom = domain.create(); + db = new sqlite3.Database(':memory:',function() { + done(); + }); + }); + + function testFn(functionName) { + it('should work for Database#'+functionName,function(done) { + dom.run(function() { + db[functionName]('select 0',function() { + assert.equal(process.domain,dom); + testThrowException(dom,done); + }); + }); + }); + } + + testFn('run'); + testFn('exec'); + testFn('prepare'); + testFn('get'); + testFn('map'); + testFn('each'); + + afterEach(function(done) { + dom.dispose(); + done(); + }); + }); +}); + diff --git a/test/profile.test.js b/test/profile.test.js index 5dc29b465..26b2725af 100644 --- a/test/profile.test.js +++ b/test/profile.test.js @@ -31,10 +31,10 @@ describe('profiling', function() { assert.ok(!create); db.run("CREATE TABLE foo (id int)", function(err) { if (err) throw err; - process.nextTick(function() { + setTimeout(function() { assert.ok(create); done(); - }); + },10); }); }); @@ -43,10 +43,10 @@ describe('profiling', function() { assert.ok(!select); db.run("SELECT * FROM foo", function(err) { if (err) throw err; - process.nextTick(function() { + setTimeout(function() { assert.ok(select); done(); - }); + },10); }); });