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) { ...@@ -22,6 +22,7 @@ void Database::Init(Handle<Object> target) {
constructor_template->SetClassName(String::NewSymbol("Database")); constructor_template->SetClassName(String::NewSymbol("Database"));
NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); 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, "serialize", Serialize);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "parallelize", Parallelize); NODE_SET_PROTOTYPE_METHOD(constructor_template, "parallelize", Parallelize);
...@@ -126,9 +127,7 @@ Handle<Value> Database::New(const Arguments& args) { ...@@ -126,9 +127,7 @@ Handle<Value> Database::New(const Arguments& args) {
args.This()->Set(String::NewSymbol("mode"), Integer::New(mode), ReadOnly); args.This()->Set(String::NewSymbol("mode"), Integer::New(mode), ReadOnly);
// Start opening the database. // Start opening the database.
OpenBaton* baton = new OpenBaton(db, callback); OpenBaton* baton = new OpenBaton(db, callback, *filename, SQLITE_OPEN_FULLMUTEX | mode);
baton->filename = *filename;
baton->mode = SQLITE_OPEN_FULLMUTEX | mode;
EIO_BeginOpen(baton); EIO_BeginOpen(baton);
return args.This(); return args.This();
...@@ -301,6 +300,76 @@ Handle<Value> Database::Parallelize(const Arguments& args) { ...@@ -301,6 +300,76 @@ Handle<Value> Database::Parallelize(const Arguments& args) {
return args.This(); 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 * Override this so that we can properly close the database when this object
* gets garbage collected. * gets garbage collected.
......
...@@ -29,13 +29,14 @@ public: ...@@ -29,13 +29,14 @@ public:
return constructor_template->HasInstance(obj); return constructor_template->HasInstance(obj);
} }
struct Baton { static struct Baton {
Database* db; Database* db;
Persistent<Function> callback; Persistent<Function> callback;
int status; int status;
std::string message; std::string message;
Baton(Database* db_, Handle<Function> cb_) : db(db_) { Baton(Database* db_, Handle<Function> cb_) :
db(db_), status(SQLITE_OK) {
db->Ref(); db->Ref();
ev_ref(EV_DEFAULT_UC); ev_ref(EV_DEFAULT_UC);
callback = Persistent<Function>::New(cb_); callback = Persistent<Function>::New(cb_);
...@@ -47,16 +48,22 @@ public: ...@@ -47,16 +48,22 @@ public:
} }
}; };
struct OpenBaton : Baton { static struct OpenBaton : Baton {
std::string filename; std::string filename;
int mode; 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); typedef void (*EIO_Callback)(Baton* baton);
struct Call { static struct Call {
Call(EIO_Callback cb_, Baton* baton_, bool exclusive_ = false) : Call(EIO_Callback cb_, Baton* baton_, bool exclusive_ = false) :
callback(cb_), exclusive(exclusive_), baton(baton_) {}; callback(cb_), exclusive(exclusive_), baton(baton_) {};
EIO_Callback callback; EIO_Callback callback;
...@@ -88,6 +95,11 @@ protected: ...@@ -88,6 +95,11 @@ protected:
void Schedule(EIO_Callback callback, Baton* baton, bool exclusive = false); void Schedule(EIO_Callback callback, Baton* baton, bool exclusive = false);
void Process(); 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 Handle<Value> Close(const Arguments& args);
static void EIO_BeginClose(Baton* baton); static void EIO_BeginClose(Baton* baton);
static int EIO_Close(eio_req *req); 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