Commit 0eb265ad by Konstantin Käfer

add Database#exec to execute many statements at once (read: importing files)

parent f737e967
......@@ -22,6 +22,7 @@ void Database::Init(Handle<Object> target) {
constructor_template->SetClassName(String::NewSymbol("Database"));
NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "exec", Exec);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "serialize", Serialize);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "parallelize", Parallelize);
......@@ -126,9 +127,7 @@ Handle<Value> Database::New(const Arguments& args) {
args.This()->Set(String::NewSymbol("mode"), Integer::New(mode), ReadOnly);
// Start opening the database.
OpenBaton* baton = new OpenBaton(db, callback);
baton->filename = *filename;
baton->mode = SQLITE_OPEN_FULLMUTEX | mode;
OpenBaton* baton = new OpenBaton(db, callback, *filename, SQLITE_OPEN_FULLMUTEX | mode);
EIO_BeginOpen(baton);
return args.This();
......@@ -301,6 +300,76 @@ Handle<Value> Database::Parallelize(const Arguments& args) {
return args.This();
}
Handle<Value> Database::Exec(const Arguments& args) {
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This());
REQUIRE_ARGUMENT_STRING(0, sql);
OPTIONAL_ARGUMENT_FUNCTION(1, callback);
Baton* baton = new ExecBaton(db, callback, *sql);
db->Schedule(EIO_BeginExec, baton, true);
return args.This();
}
void Database::EIO_BeginExec(Baton* baton) {
assert(baton->db->locked);
assert(baton->db->open);
assert(baton->db->handle);
assert(baton->db->pending == 0);
eio_custom(EIO_Exec, EIO_PRI_DEFAULT, EIO_AfterExec, baton);
}
int Database::EIO_Exec(eio_req *req) {
ExecBaton* baton = static_cast<ExecBaton*>(req->data);
char* message = NULL;
baton->status = sqlite3_exec(
baton->db->handle,
baton->sql.c_str(),
NULL,
NULL,
&message
);
if (baton->status != SQLITE_OK && message != NULL) {
baton->message = std::string(message);
sqlite3_free(message);
}
return 0;
}
int Database::EIO_AfterExec(eio_req *req) {
HandleScope scope;
Baton* baton = static_cast<ExecBaton*>(req->data);
Database* db = baton->db;
if (baton->status != SQLITE_OK) {
EXCEPTION(String::New(baton->message.c_str()), baton->status, exception);
if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
Local<Value> argv[] = { exception };
TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
}
else {
Local<Value> args[] = { String::NewSymbol("error"), exception };
EMIT_EVENT(db->handle_, 2, args);
}
}
else if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
Local<Value> argv[] = { Local<Value>::New(Null()) };
TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
}
db->Process();
delete baton;
return 0;
}
/**
* Override this so that we can properly close the database when this object
* gets garbage collected.
......
......@@ -29,13 +29,14 @@ public:
return constructor_template->HasInstance(obj);
}
struct Baton {
static struct Baton {
Database* db;
Persistent<Function> callback;
int status;
std::string message;
Baton(Database* db_, Handle<Function> cb_) : db(db_) {
Baton(Database* db_, Handle<Function> cb_) :
db(db_), status(SQLITE_OK) {
db->Ref();
ev_ref(EV_DEFAULT_UC);
callback = Persistent<Function>::New(cb_);
......@@ -47,16 +48,22 @@ public:
}
};
struct OpenBaton : Baton {
static struct OpenBaton : Baton {
std::string filename;
int mode;
OpenBaton(Database* db_, Handle<Function> cb_, const char* filename_, int mode_) :
Baton(db_, cb_), filename(filename_), mode(mode_) {}
};
OpenBaton(Database* db_, Handle<Function> cb_) : Baton(db_, cb_) {}
static struct ExecBaton : Baton {
std::string sql;
ExecBaton(Database* db_, Handle<Function> cb_, const char* sql_) :
Baton(db_, cb_), sql(sql_) {}
};
typedef void (*EIO_Callback)(Baton* baton);
struct Call {
static struct Call {
Call(EIO_Callback cb_, Baton* baton_, bool exclusive_ = false) :
callback(cb_), exclusive(exclusive_), baton(baton_) {};
EIO_Callback callback;
......@@ -88,6 +95,11 @@ protected:
void Schedule(EIO_Callback callback, Baton* baton, bool exclusive = false);
void Process();
static Handle<Value> Exec(const Arguments& args);
static void EIO_BeginExec(Baton* baton);
static int EIO_Exec(eio_req *req);
static int EIO_AfterExec(eio_req *req);
static Handle<Value> Close(const Arguments& args);
static void EIO_BeginClose(Baton* baton);
static int EIO_Close(eio_req *req);
......
var sqlite3 = require('sqlite3');
var assert = require('assert');
var fs = require('fs');
exports['test Database#exec'] = function(beforeExit) {
var db = new sqlite3.Database(':memory:');
var finished = false;
var sql = fs.readFileSync('test/support/script.sql', 'utf8');
db.exec(sql, function(err) {
if (err) throw err;
db.all("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name", function(err, rows) {
if (err) throw err;
assert.ok(rows.length, 3);
assert.deepEqual(rows, [
{ name: 'bar' },
{ name: 'baz' },
{ name: 'foo' }
]);
finished = true;
});
});
beforeExit(function() {
assert.ok(finished);
});
};
CREATE TABLE foo (id INT, txt TEXT);
INSERT INTO foo VALUES (1, 'test');
INSERT INTO foo VALUES (2, 'test');
INSERT INTO foo VALUES (3, 'test');
INSERT INTO foo VALUES (4, 'test');
INSERT INTO foo VALUES (5, 'test');
INSERT INTO foo VALUES (6, 'test');
INSERT INTO foo VALUES (7, 'test');
CREATE TABLE bar (id INT, flt FLOAT);
CREATE TABLE baz (id INT, blb BLOB);
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