Commit d2647714 by Konstantin Käfer

throw error messages when the queue gets stuck

parent 9a79d3df
......@@ -83,6 +83,21 @@ void Database::ProcessQueue(Database* db) {
if (!(call->Data() & db->status)) {
// The next task in the queue requires a different status than the
// one we're currently in. Wait before invoking it.
if (db->pending == 0) {
EXCEPTION("Invalid function call sequence", SQLITE_MISUSE, exception);
Local<Value> argv[] = { String::NewSymbol("error"), exception };
Local<Function> fn = Local<Function>::Cast(db->handle_->Get(String::NewSymbol("emit")));
TryCatch try_catch;
fn->Call(db->handle_, 2, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
ev_unref(EV_DEFAULT_UC);
db->Unref();
db->queue.pop();
delete call;
}
break;
}
......@@ -122,8 +137,8 @@ Handle<Value> Database::Open(const Arguments& args) {
}
else {
db->status = IsOpening;
DatabaseBaton* baton =
new DatabaseBaton(db, Persistent<Function>::New(callback));
db->pending++;
Baton* baton = new Baton(db, Persistent<Function>::New(callback));
eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, baton);
}
......@@ -176,17 +191,19 @@ Handle<Value> Database::OpenSync(const Arguments& args) {
}
int Database::EIO_Open(eio_req *req) {
DatabaseBaton* baton = static_cast<DatabaseBaton*>(req->data);
Baton* baton = static_cast<Baton*>(req->data);
Open(baton->db);
return 0;
}
int Database::EIO_AfterOpen(eio_req *req) {
HandleScope scope;
DatabaseBaton* baton = static_cast<DatabaseBaton*>(req->data);
Baton* baton = static_cast<Baton*>(req->data);
Database* db = baton->db;
ev_unref(EV_DEFAULT_UC);
db->Unref();
db->pending--;
Local<Value> argv[1];
if (db->error_status == SQLITE_OK) {
......@@ -226,8 +243,8 @@ Handle<Value> Database::Close(const Arguments& args) {
}
else {
db->status = IsClosing;
DatabaseBaton* baton =
new DatabaseBaton(db, Persistent<Function>::New(callback));
db->pending++;
Baton* baton = new Baton(db, Persistent<Function>::New(callback));
eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, baton);
}
......@@ -276,18 +293,19 @@ Handle<Value> Database::CloseSync(const Arguments& args) {
}
int Database::EIO_Close(eio_req *req) {
DatabaseBaton* baton = static_cast<DatabaseBaton*>(req->data);
Baton* baton = static_cast<Baton*>(req->data);
Close(baton->db);
return 0;
}
int Database::EIO_AfterClose(eio_req *req) {
HandleScope scope;
DatabaseBaton* baton = static_cast<DatabaseBaton*>(req->data);
Baton* baton = static_cast<Baton*>(req->data);
Database* db = baton->db;
ev_unref(EV_DEFAULT_UC);
db->Unref();
db->pending--;
Local<Value> argv[1];
if (db->error_status != SQLITE_OK) {
......
......@@ -45,6 +45,12 @@ class Database : public EventEmitter {
typedef Deferred::Call<Status> Call;
struct Baton {
Baton(Database* db_, Persistent<Function> callback_) :
db(db_), callback(callback_) {};
Database* db;
Persistent<Function> callback;
};
protected:
Database() : EventEmitter(),
......@@ -103,13 +109,6 @@ class Database : public EventEmitter {
};
struct DatabaseBaton {
DatabaseBaton(Database* db_, Persistent<Function> callback_) :
db(db_), callback(callback_) {};
Database* db;
Persistent<Function> callback;
};
enum ExecMode {
EXEC_EMPTY = 0,
EXEC_LAST_INSERT_ID = 1,
......
......@@ -129,8 +129,6 @@ const char* sqlite_code_string(int code);
name ##_obj->Set(NODE_PSYMBOL("code"), \
String::NewSymbol(sqlite_code_string(errno)));
#define EXCEPTION_STR
#define EVENT_ONCE(event, callback) \
Local<Value> argv[2] = { \
String::NewSymbol(event), \
......
......@@ -9,79 +9,79 @@ exports['constants'] = function() {
assert.ok(sqlite3.OPEN_CREATE === 4);
};
// exports['open and close non-existent database'] = function(beforeExit) {
// var opened, closed;
//
// helper.deleteFile('test/tmp/test_create.db');
// var db = new sqlite3.Database('test/tmp/test_create.db');
//
// db.open(function(err) {
// if (err) throw err;
// assert.ok(!opened);
// assert.ok(!closed);
// opened = true;
// });
// db.close(function(err) {
// if (err) throw err;
// assert.ok(opened);
// assert.ok(!closed);
// closed = true;
// });
//
// beforeExit(function() {
// assert.ok(opened, 'Database not opened');
// assert.ok(closed, 'Database not closed');
// assert.fileExists('test/tmp/test_create.db');
// helper.deleteFile('test/tmp/test_create.db');
// });
// };
//
// exports['open inaccessible database'] = function(beforeExit) {
// var notOpened;
//
// var db = new sqlite3.Database('/usr/bin/test.db');
// db.open(function(err) {
// if (err && err.code === 'SQLITE_CANTOPEN') {
// notOpened = true;
// }
// else if (err) throw err;
// });
//
// beforeExit(function() {
// assert.ok(notOpened, 'Database could be opened');
// });
// };
//
//
// exports['open non-existent database without create'] = function(beforeExit) {
// var notOpened;
//
// helper.deleteFile('tmp/test_readonly.db');
// var db = new sqlite3.Database('tmp/test_readonly.db', sqlite3.OPEN_READONLY);
//
// db.open(function(err) {
// if (err && err.code === 'SQLITE_CANTOPEN') {
// notOpened = true;
// }
// else if (err) throw err;
// });
//
// beforeExit(function() {
// assert.ok(notOpened, 'Database could be opened');
// assert.fileDoesNotExist('tmp/test_readonly.db');
// });
// };
exports['open and close non-existent database'] = function(beforeExit) {
var opened, closed;
helper.deleteFile('test/tmp/test_create.db');
var db = new sqlite3.Database('test/tmp/test_create.db');
db.open(function(err) {
if (err) throw err;
assert.ok(!opened);
assert.ok(!closed);
opened = true;
});
db.close(function(err) {
if (err) throw err;
assert.ok(opened);
assert.ok(!closed);
closed = true;
});
beforeExit(function() {
assert.ok(opened, 'Database not opened');
assert.ok(closed, 'Database not closed');
assert.fileExists('test/tmp/test_create.db');
helper.deleteFile('test/tmp/test_create.db');
});
};
exports['open inaccessible database'] = function(beforeExit) {
var notOpened;
var db = new sqlite3.Database('/usr/bin/test.db');
db.open(function(err) {
if (err && err.code === 'SQLITE_CANTOPEN') {
notOpened = true;
}
else if (err) throw err;
});
beforeExit(function() {
assert.ok(notOpened, 'Database could be opened');
});
};
exports['open non-existent database without create'] = function(beforeExit) {
var notOpened;
helper.deleteFile('tmp/test_readonly.db');
var db = new sqlite3.Database('tmp/test_readonly.db', sqlite3.OPEN_READONLY);
db.open(function(err) {
if (err && err.code === 'SQLITE_CANTOPEN') {
notOpened = true;
}
else if (err) throw err;
});
beforeExit(function() {
assert.ok(notOpened, 'Database could be opened');
assert.fileDoesNotExist('tmp/test_readonly.db');
});
};
exports['open and close memory database queuing'] = function(beforeExit) {
var opened = 0, closed = 0;
var db = new sqlite3.Database(':memory:');
function openedCallback(err) {
if (err) throw err;
opened++;
}
function closedCallback(err) {
if (err) console.warn(err);
if (err) throw err;
......@@ -89,11 +89,8 @@ exports['open and close memory database queuing'] = function(beforeExit) {
}
db.open(openedCallback);
db.open(openedCallback);
db.open(openedCallback);
db.open(openedCallback);
db.open(openedCallback);
db.close(closedCallback);
db.open(openedCallback);
db.close(closedCallback);
db.open(openedCallback);
db.close(closedCallback);
......@@ -102,8 +99,30 @@ exports['open and close memory database queuing'] = function(beforeExit) {
db.open(openedCallback);
beforeExit(function() {
console.log(opened, closed);
assert.equal(opened, 5, 'Database not opened');
assert.equal(closed, 4, 'Database not closed');
});
};
exports['two opens in a row'] = function(beforeExit) {
var opened = 0, openErrors = 0;
var db = new sqlite3.Database(':memory:');
function openedCallback(err) {
if (err) throw err;
opened++;
}
db.on('error', function(err) {
openErrors++;
});
db.open(openedCallback);
db.open(openedCallback);
beforeExit(function() {
assert.equal(opened, 1, 'Database not opened');
assert.equal(openErrors, 1, 'Second open succeeded');
});
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment