Commit db72749d by Konstantin Käfer

unify indentation and rework database opening, closing and garbage collection

parent 493050a9
......@@ -16,6 +16,9 @@
var sqlite3 = module.exports = exports = require('./sqlite3_bindings');
var sys = require("sys");
/*
sqlite3.Database.prototype.query = function(sql, bindings, rowCallback) {
var self = this;
......@@ -68,7 +71,7 @@ function _doStep(db, statement, rowCallback) {
// Execute a single SQL query with the given optional parameters. Calls
// `callback` with all rows or an error on query completion.
sqlite3.Database.prototype.execute = function (sql /* , bindings, callback */) {
sqlite3.Database.prototype.execute = function (sql /* , bindings, callback ) {
var self = this;
var bindings, callback;
var n = arguments.length;
......@@ -248,3 +251,4 @@ sqlite3.sanitizeError = function(err, data) {
'" with values ' + JSON.stringify(data, false, 4);
return err;
};
*/
\ No newline at end of file
// Copyright (c) 2010, Orlando Vazquez <ovazquez@gmail.com>
//
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
......@@ -28,182 +28,202 @@ using namespace node;
Persistent<FunctionTemplate> Database::constructor_template;
void Database::Init(v8::Handle<Object> target) {
HandleScope scope;
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(New);
Local<FunctionTemplate> t = FunctionTemplate::New(New);
constructor_template = Persistent<FunctionTemplate>::New(t);
constructor_template->Inherit(EventEmitter::constructor_template);
constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
constructor_template->SetClassName(String::NewSymbol("Database"));
constructor_template = Persistent<FunctionTemplate>::New(t);
constructor_template->Inherit(EventEmitter::constructor_template);
constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
constructor_template->SetClassName(String::NewSymbol("Database"));
NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepareAndStep", PrepareAndStep);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "openSync", OpenSync);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync);
target->Set(v8::String::NewSymbol("Database"),
constructor_template->GetFunction());
NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare);
// insert/update execution result mask
NODE_DEFINE_CONSTANT (target, EXEC_EMPTY);
NODE_DEFINE_CONSTANT (target, EXEC_LAST_INSERT_ID);
NODE_DEFINE_CONSTANT (target, EXEC_AFFECTED_ROWS);
}
target->Set(v8::String::NewSymbol("Database"),
constructor_template->GetFunction());
Handle<Value> Database::New(const Arguments& args) {
HandleScope scope;
Database* db = new Database();
db->Wrap(args.This());
return args.This();
// insert/update execution result mask
NODE_DEFINE_CONSTANT(target, EXEC_EMPTY);
NODE_DEFINE_CONSTANT(target, EXEC_LAST_INSERT_ID);
NODE_DEFINE_CONSTANT(target, EXEC_AFFECTED_ROWS);
}
int Database::EIO_AfterOpen(eio_req *req) {
ev_unref(EV_DEFAULT_UC);
HandleScope scope;
struct open_request *open_req = (struct open_request *)(req->data);
Local<Value> argv[1];
bool err = false;
if (req->result) {
err = true;
argv[0] = Exception::Error(String::New("Error opening database"));
}
TryCatch try_catch;
open_req->db->Unref();
open_req->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
open_req->db->Emit(String::New("ready"), 0, NULL);
open_req->cb.Dispose();
free(open_req);
return 0;
Handle<Value> Database::New(const Arguments& args) {
HandleScope scope;
Database* db = new Database();
db->Wrap(args.This());
return args.This();
}
int Database::EIO_Open(eio_req *req) {
struct open_request *open_req = (struct open_request *)(req->data);
sqlite3 **dbptr = open_req->db->GetDBPtr();
int rc = sqlite3_open_v2( open_req->filename
, dbptr
, SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
| SQLITE_OPEN_FULLMUTEX
, NULL);
req->result = rc;
// Set the a 10s timeout valuei for retries on BUSY errors.
sqlite3_busy_timeout(*dbptr, 10000);
Handle<Value> Database::OpenSync(const Arguments& args) {
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This());
if (db->readyState == CLOSED) {
if (!Open(db)) {
EXCEPTION(db->error_message.c_str(), db->error_status, exception);
return ThrowException(exception);
}
else {
args.This()->Set(String::NewSymbol("opened"), True(), ReadOnly);
db->Emit(String::NewSymbol("opened"), 0, NULL);
// sqlite3 *db = *dbptr;
// sqlite3_commit_hook(db, CommitHook, open_req->db);
// sqlite3_rollback_hook(db, RollbackHook, open_req->db);
// sqlite3_update_hook(db, UpdateHook, open_req->db);
if (db->pending == 0) {
db->Emit(String::NewSymbol("idle"), 0, NULL);
}
}
}
return 0;
return args.This();
}
Handle<Value> Database::Open(const Arguments& args) {
HandleScope scope;
REQ_STR_ARG(0, filename);
REQ_FUN_ARG(1, cb);
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This());
if (db->readyState == CLOSED) {
db->readyState = OPENING;
db->Ref();
eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, db);
ev_ref(EV_DEFAULT_UC);
}
Database* db = ObjectWrap::Unwrap<Database>(args.This());
return args.This();
}
struct open_request *open_req = (struct open_request *)
calloc(1, sizeof(struct open_request) + filename.length());
bool Database::Open(Database* db) {
db->error_status = sqlite3_open_v2(
db->filename.c_str(),
&db->handle,
SQLITE_OPEN_FULLMUTEX | db->open_mode,
NULL
);
if (db->error_status != SQLITE_OK) {
db->error_message = std::string(sqlite3_errmsg(db->handle));
db->readyState = CLOSED;
return false;
}
else {
db->readyState = OPEN;
return true;
}
}
if (!open_req) {
V8::LowMemoryNotification();
return ThrowException(Exception::Error(
String::New("Could not allocate enough memory")));
}
int Database::EIO_Open(eio_req *req) {
Database* db = static_cast<Database*>(req->data);
Open(db);
return 0;
}
strcpy(open_req->filename, *filename);
open_req->cb = Persistent<Function>::New(cb);
open_req->db = db;
int Database::EIO_AfterOpen(eio_req *req) {
HandleScope scope;
Database* db = static_cast<Database*>(req->data);
ev_unref(EV_DEFAULT_UC);
db->Unref();
Local<Value> argv[1];
if (db->error_status != SQLITE_OK) {
EXCEPTION(db->error_message.c_str(), db->error_status, exception);
argv[0] = exception;
}
else {
argv[0] = Local<Value>::New(Null());
db->handle_->Set(String::NewSymbol("opened"), True(), ReadOnly);
}
eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, open_req);
db->Emit(String::NewSymbol("opened"), 1, argv);
ev_ref(EV_DEFAULT_UC);
db->Ref();
if (db->pending == 0) {
db->Emit(String::NewSymbol("idle"), 0, NULL);
}
return Undefined();
return 0;
}
int Database::EIO_AfterClose(eio_req *req) {
ev_unref(EV_DEFAULT_UC);
HandleScope scope;
struct close_request *close_req = (struct close_request *)(req->data);
Handle<Value> Database::CloseSync(const Arguments& args) {
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This());
Local<Value> argv[1];
bool err = false;
if (req->result) {
err = true;
argv[0] = Exception::Error(String::New("Error closing database"));
}
if (db->readyState == OPEN) {
if (!Close(db)) {
EXCEPTION(db->error_message.c_str(), db->error_status, exception);
return ThrowException(exception);
}
else {
args.This()->Set(String::NewSymbol("opened"), False(), ReadOnly);
db->Emit(String::NewSymbol("closed"), 0, NULL);
}
}
TryCatch try_catch;
return True();
}
close_req->db->Unref();
close_req->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv);
Handle<Value> Database::Close(const Arguments& args) {
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This());
if (db->readyState == OPEN) {
db->readyState = CLOSING;
db->Ref();
eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, db);
ev_ref(EV_DEFAULT_UC);
}
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
return args.This();
}
close_req->cb.Dispose();
bool Database::Close(Database* db) {
assert(db->handle);
free(close_req);
db->error_status = sqlite3_close(db->handle);
return 0;
if (db->error_status != SQLITE_OK) {
db->error_message = std::string(sqlite3_errmsg(db->handle));
db->readyState = OPEN;
return false;
}
else {
db->readyState = CLOSED;
db->handle = NULL;
return true;
}
}
int Database::EIO_Close(eio_req *req) {
struct close_request *close_req = (struct close_request *)(req->data);
Database* db = close_req->db;
req->result = sqlite3_close(db->db_);
db->db_ = NULL;
return 0;
Database* db = static_cast<Database*>(req->data);
Close(db);
return 0;
}
Handle<Value> Database::Close(const Arguments& args) {
HandleScope scope;
REQ_FUN_ARG(0, cb);
Database* db = ObjectWrap::Unwrap<Database>(args.This());
struct close_request *close_req = (struct close_request *)
calloc(1, sizeof(struct close_request));
if (!close_req) {
V8::LowMemoryNotification();
return ThrowException(Exception::Error(
String::New("Could not allocate enough memory")));
}
close_req->cb = Persistent<Function>::New(cb);
close_req->db = db;
eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, close_req);
int Database::EIO_AfterClose(eio_req *req) {
HandleScope scope;
Database* db = static_cast<Database*>(req->data);
ev_unref(EV_DEFAULT_UC);
db->Unref();
Local<Value> argv[1];
if (db->error_status != SQLITE_OK) {
EXCEPTION(db->error_message.c_str(), db->error_status, exception);
argv[0] = exception;
}
else {
argv[0] = Local<Value>::New(Null());
db->handle_->Set(String::NewSymbol("opened"), False(), ReadOnly);
}
ev_ref(EV_DEFAULT_UC);
db->Ref();
db->Emit(String::NewSymbol("closed"), 1, argv);
return Undefined();
return 0;
}
// // TODO: libeio'fy
......@@ -233,279 +253,309 @@ Handle<Value> Database::Close(const Arguments& args) {
// }
int Database::EIO_AfterPrepareAndStep(eio_req *req) {
ev_unref(EV_DEFAULT_UC);
struct prepare_request *prep_req = (struct prepare_request *)(req->data);
HandleScope scope;
ev_unref(EV_DEFAULT_UC);
struct prepare_request *prep_req = (struct prepare_request *)(req->data);
HandleScope scope;
Local<Value> argv[2];
int argc = 0;
Local<Value> argv[2];
int argc = 0;
// if the prepare failed
if (req->result != SQLITE_OK) {
argv[0] = Exception::Error(
String::New(sqlite3_errmsg(prep_req->db->db_)));
argc = 1;
// if the prepare failed
if (req->result != SQLITE_OK) {
argv[0] = Exception::Error(
String::New(sqlite3_errmsg(prep_req->db->handle)));
argc = 1;
}
else {
if (req->int1 == SQLITE_DONE) {
}
else {
if (req->int1 == SQLITE_DONE) {
if (prep_req->mode != EXEC_EMPTY) {
argv[0] = Local<Value>::New(Undefined()); // no error
if (prep_req->mode != EXEC_EMPTY) {
argv[0] = Local<Value>::New(Undefined()); // no error
Local<Object> info = Object::New();
Local<Object> info = Object::New();
if (prep_req->mode & EXEC_LAST_INSERT_ID) {
info->Set(String::NewSymbol("last_inserted_id"),
Integer::NewFromUnsigned (prep_req->lastInsertId));
}
if (prep_req->mode & EXEC_AFFECTED_ROWS) {
info->Set(String::NewSymbol("affected_rows"),
Integer::New (prep_req->affectedRows));
}
argv[1] = info;
argc = 2;
if (prep_req->mode & EXEC_LAST_INSERT_ID) {
info->Set(String::NewSymbol("last_inserted_id"),
Integer::NewFromUnsigned (prep_req->lastInsertId));
}
if (prep_req->mode & EXEC_AFFECTED_ROWS) {
info->Set(String::NewSymbol("affected_rows"),
Integer::New (prep_req->affectedRows));
}
argv[1] = info;
argc = 2;
} else {
argc = 0;
}
} else {
argc = 0;
}
}
else {
argv[0] = External::New(prep_req->stmt);
argv[1] = Integer::New(req->int1);
Persistent<Object> statement(
Statement::constructor_template->GetFunction()->NewInstance(2, argv));
if (prep_req->tail) {
statement->Set(String::New("tail"), String::New(prep_req->tail));
}
argv[0] = Local<Value>::New(Undefined());
argv[1] = Local<Value>::New(statement);
argc = 2;
}
}
else {
argv[0] = External::New(prep_req->stmt);
argv[1] = Integer::New(req->int1);
Persistent<Object> statement(
Statement::constructor_template->GetFunction()->NewInstance(2, argv));
if (prep_req->tail) {
statement->Set(String::New("tail"), String::New(prep_req->tail));
}
argv[0] = Local<Value>::New(Undefined());
argv[1] = Local<Value>::New(statement);
argc = 2;
}
}
TryCatch try_catch;
TryCatch try_catch;
prep_req->db->Unref();
prep_req->cb->Call(Context::GetCurrent()->Global(), argc, argv);
prep_req->db->Unref();
prep_req->cb->Call(Context::GetCurrent()->Global(), argc, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
prep_req->cb.Dispose();
free(prep_req);
prep_req->cb.Dispose();
free(prep_req);
return 0;
return 0;
}
int Database::EIO_PrepareAndStep(eio_req *req) {
struct prepare_request *prep_req = (struct prepare_request *)(req->data);
prep_req->stmt = NULL;
prep_req->tail = NULL;
sqlite3* db = prep_req->db->db_;
int rc = sqlite3_prepare_v2(db, prep_req->sql, -1,
&(prep_req->stmt), &(prep_req->tail));
req->result = rc;
req->int1 = -1;
// This might be a INSERT statement. Let's try to get the first row.
// This is to optimize out further calls to the thread pool. This is only
// possible in the case where there are no variable placeholders/bindings
// in the SQL.
if (rc == SQLITE_OK && !sqlite3_bind_parameter_count(prep_req->stmt)) {
rc = sqlite3_step(prep_req->stmt);
req->int1 = rc;
// no more rows to return, clean up statement
if (rc == SQLITE_DONE) {
rc = sqlite3_finalize(prep_req->stmt);
prep_req->stmt = NULL;
assert(rc == SQLITE_OK);
struct prepare_request *prep_req = (struct prepare_request *)(req->data);
prep_req->stmt = NULL;
prep_req->tail = NULL;
sqlite3* db = prep_req->db->handle;
int rc = sqlite3_prepare_v2(db, prep_req->sql, -1,
&(prep_req->stmt), &(prep_req->tail));
req->result = rc;
req->int1 = -1;
// This might be a INSERT statement. Let's try to get the first row.
// This is to optimize out further calls to the thread pool. This is only
// possible in the case where there are no variable placeholders/bindings
// in the SQL.
if (rc == SQLITE_OK && !sqlite3_bind_parameter_count(prep_req->stmt)) {
rc = sqlite3_step(prep_req->stmt);
req->int1 = rc;
// no more rows to return, clean up statement
if (rc == SQLITE_DONE) {
rc = sqlite3_finalize(prep_req->stmt);
prep_req->stmt = NULL;
assert(rc == SQLITE_OK);
}
}
}
prep_req->lastInsertId = 0;
prep_req->affectedRows = 0;
prep_req->lastInsertId = 0;
prep_req->affectedRows = 0;
// load custom properties
if (prep_req->mode & EXEC_LAST_INSERT_ID)
prep_req->lastInsertId = sqlite3_last_insert_rowid(db);
if (prep_req->mode & EXEC_AFFECTED_ROWS)
prep_req->affectedRows = sqlite3_changes(db);
// load custom properties
if (prep_req->mode & EXEC_LAST_INSERT_ID)
prep_req->lastInsertId = sqlite3_last_insert_rowid(db);
if (prep_req->mode & EXEC_AFFECTED_ROWS)
prep_req->affectedRows = sqlite3_changes(db);
return 0;
return 0;
}
Handle<Value> Database::PrepareAndStep(const Arguments& args) {
HandleScope scope;
HandleScope scope;
REQ_STR_ARG(0, sql);
REQ_FUN_ARG(1, cb);
OPT_INT_ARG(2, mode, EXEC_EMPTY);
REQUIRE_ARGUMENT_STRING(0, sql);
REQUIRE_ARGUMENT_FUNCTION(1, cb);
OPTIONAL_ARGUMENT_INTEGER(2, mode, EXEC_EMPTY);
Database* db = ObjectWrap::Unwrap<Database>(args.This());
Database* db = ObjectWrap::Unwrap<Database>(args.This());
struct prepare_request *prep_req = (struct prepare_request *)
calloc(1, sizeof(struct prepare_request) + sql.length());
struct prepare_request *prep_req = (struct prepare_request *)
calloc(1, sizeof(struct prepare_request) + sql.length());
if (!prep_req) {
V8::LowMemoryNotification();
return ThrowException(Exception::Error(
String::New("Could not allocate enough memory")));
}
if (!prep_req) {
V8::LowMemoryNotification();
return ThrowException(Exception::Error(
String::New("Could not allocate enough memory")));
}
strcpy(prep_req->sql, *sql);
prep_req->cb = Persistent<Function>::New(cb);
prep_req->db = db;
prep_req->mode = mode;
strcpy(prep_req->sql, *sql);
prep_req->cb = Persistent<Function>::New(cb);
prep_req->db = db;
prep_req->mode = mode;
eio_custom(EIO_PrepareAndStep, EIO_PRI_DEFAULT, EIO_AfterPrepareAndStep, prep_req);
eio_custom(EIO_PrepareAndStep, EIO_PRI_DEFAULT, EIO_AfterPrepareAndStep, prep_req);
ev_ref(EV_DEFAULT_UC);
db->Ref();
ev_ref(EV_DEFAULT_UC);
db->Ref();
return Undefined();
return Undefined();
}
int Database::EIO_AfterPrepare(eio_req *req) {
ev_unref(EV_DEFAULT_UC);
struct prepare_request *prep_req = (struct prepare_request *)(req->data);
HandleScope scope;
Local<Value> argv[3];
int argc = 0;
// if the prepare failed
if (req->result != SQLITE_OK) {
argv[0] = Exception::Error(
String::New(sqlite3_errmsg(prep_req->db->db_)));
argc = 1;
}
else {
argv[0] = External::New(prep_req->stmt);
argv[1] = Integer::New(-1);
argv[2] = Integer::New(prep_req->mode);
Persistent<Object> statement(
Statement::constructor_template->GetFunction()->NewInstance(3, argv));
if (prep_req->tail) {
statement->Set(String::New("tail"), String::New(prep_req->tail));
ev_unref(EV_DEFAULT_UC);
struct prepare_request *prep_req = (struct prepare_request *)(req->data);
HandleScope scope;
Local<Value> argv[3];
int argc = 0;
// if the prepare failed
if (req->result != SQLITE_OK) {
argv[0] = Exception::Error(
String::New(sqlite3_errmsg(prep_req->db->handle)));
argc = 1;
}
else {
argv[0] = External::New(prep_req->stmt);
argv[1] = Integer::New(-1);
argv[2] = Integer::New(prep_req->mode);
Persistent<Object> statement(
Statement::constructor_template->GetFunction()->NewInstance(3, argv));
if (prep_req->tail) {
statement->Set(String::New("tail"), String::New(prep_req->tail));
}
argc = 2;
argv[0] = Local<Value>::New(Undefined());
argv[1] = Local<Value>::New(statement);
}
argc = 2;
argv[0] = Local<Value>::New(Undefined());
argv[1] = Local<Value>::New(statement);
}
TryCatch try_catch;
TryCatch try_catch;
prep_req->db->Unref();
prep_req->cb->Call(Context::GetCurrent()->Global(), argc, argv);
prep_req->db->Unref();
prep_req->cb->Call(Context::GetCurrent()->Global(), argc, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
prep_req->cb.Dispose();
free(prep_req);
prep_req->cb.Dispose();
free(prep_req);
return 0;
return 0;
}
int Database::EIO_Prepare(eio_req *req) {
struct prepare_request *prep_req = (struct prepare_request *)(req->data);
struct prepare_request *prep_req = (struct prepare_request *)(req->data);
prep_req->stmt = NULL;
prep_req->tail = NULL;
sqlite3* db = prep_req->db->db_;
prep_req->stmt = NULL;
prep_req->tail = NULL;
sqlite3* db = prep_req->db->handle;
int rc = sqlite3_prepare_v2(db, prep_req->sql, -1,
&(prep_req->stmt), &(prep_req->tail));
int rc = sqlite3_prepare_v2(db, prep_req->sql, -1,
&(prep_req->stmt), &(prep_req->tail));
req->result = rc;
req->result = rc;
prep_req->lastInsertId = 0;
prep_req->affectedRows = 0;
prep_req->lastInsertId = 0;
prep_req->affectedRows = 0;
// load custom properties
if (prep_req->mode & EXEC_LAST_INSERT_ID)
prep_req->lastInsertId = sqlite3_last_insert_rowid(db);
if (prep_req->mode & EXEC_AFFECTED_ROWS)
prep_req->affectedRows = sqlite3_changes(db);
// load custom properties
if (prep_req->mode & EXEC_LAST_INSERT_ID)
prep_req->lastInsertId = sqlite3_last_insert_rowid(db);
if (prep_req->mode & EXEC_AFFECTED_ROWS)
prep_req->affectedRows = sqlite3_changes(db);
return 0;
return 0;
}
// Statement#prepare(sql, [ options ,] callback);
Handle<Value> Database::Prepare(const Arguments& args) {
HandleScope scope;
Local<Object> options;
Local<Function> cb;
int mode;
REQ_STR_ARG(0, sql);
// middle argument could be options or
switch (args.Length()) {
case 2:
if (!args[1]->IsFunction()) {
return ThrowException(Exception::TypeError(
String::New("Argument 1 must be a function")));
}
cb = Local<Function>::Cast(args[1]);
options = Object::New();
break;
case 3:
if (!args[1]->IsObject()) {
return ThrowException(Exception::TypeError(
String::New("Argument 1 must be an object")));
}
options = Local<Function>::Cast(args[1]);
if (!args[2]->IsFunction()) {
return ThrowException(Exception::TypeError(
String::New("Argument 2 must be a function")));
}
cb = Local<Function>::Cast(args[2]);
break;
}
mode = EXEC_EMPTY;
if (options->Get(String::New("lastInsertRowID"))->IsTrue()) {
mode |= EXEC_LAST_INSERT_ID;
}
if (options->Get(String::New("affectedRows"))->IsTrue()) {
mode |= EXEC_AFFECTED_ROWS;
}
Database* db = ObjectWrap::Unwrap<Database>(args.This());
struct prepare_request *prep_req = (struct prepare_request *)
calloc(1, sizeof(struct prepare_request) + sql.length());
if (!prep_req) {
V8::LowMemoryNotification();
return ThrowException(Exception::Error(
String::New("Could not allocate enough memory")));
}
strcpy(prep_req->sql, *sql);
prep_req->cb = Persistent<Function>::New(cb);
prep_req->db = db;
prep_req->mode = mode;
eio_custom(EIO_Prepare, EIO_PRI_DEFAULT, EIO_AfterPrepare, prep_req);
ev_ref(EV_DEFAULT_UC);
db->Ref();
return Undefined();
HandleScope scope;
Local<Object> options;
Local<Function> cb;
int mode;
REQUIRE_ARGUMENT_STRING(0, sql);
// middle argument could be options or
switch (args.Length()) {
case 2:
if (!args[1]->IsFunction()) {
return ThrowException(Exception::TypeError(
String::New("Argument 1 must be a function")));
}
cb = Local<Function>::Cast(args[1]);
options = Object::New();
break;
case 3:
if (!args[1]->IsObject()) {
return ThrowException(Exception::TypeError(
String::New("Argument 1 must be an object")));
}
options = Local<Function>::Cast(args[1]);
if (!args[2]->IsFunction()) {
return ThrowException(Exception::TypeError(
String::New("Argument 2 must be a function")));
}
cb = Local<Function>::Cast(args[2]);
break;
}
mode = EXEC_EMPTY;
if (options->Get(String::New("lastInsertRowID"))->IsTrue()) {
mode |= EXEC_LAST_INSERT_ID;
}
if (options->Get(String::New("affectedRows"))->IsTrue()) {
mode |= EXEC_AFFECTED_ROWS;
}
Database* db = ObjectWrap::Unwrap<Database>(args.This());
struct prepare_request *prep_req = (struct prepare_request *)
calloc(1, sizeof(struct prepare_request) + sql.length());
if (!prep_req) {
V8::LowMemoryNotification();
return ThrowException(Exception::Error(
String::New("Could not allocate enough memory")));
}
strcpy(prep_req->sql, *sql);
prep_req->cb = Persistent<Function>::New(cb);
prep_req->db = db;
prep_req->mode = mode;
eio_custom(EIO_Prepare, EIO_PRI_DEFAULT, EIO_AfterPrepare, prep_req);
ev_ref(EV_DEFAULT_UC);
db->Ref();
return Undefined();
}
/**
* Override this so that we can properly close the database when this object
* gets garbage collected.
*/
void Database::Wrap(Handle<Object> handle) {
assert(handle_.IsEmpty());
assert(handle->InternalFieldCount() > 0);
handle_ = Persistent<Object>::New(handle);
handle_->SetPointerInInternalField(0, this);
handle_.MakeWeak(this, Destruct);
}
void Database::Destruct(Persistent<Value> value, void *data) {
Database* db = static_cast<Database*>(data);
if (db->handle) {
eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterDestruct, db);
ev_ref(EV_DEFAULT_UC);
}
else {
delete db;
}
}
int Database::EIO_AfterDestruct(eio_req *req) {
Database* db = static_cast<Database*>(req->data);
ev_unref(EV_DEFAULT_UC);
delete db;
return 0;
}
// Copyright (c) 2010, Orlando Vazquez <ovazquez@gmail.com>
//
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
......@@ -19,35 +19,51 @@
#include <node.h>
#include <node_events.h>
#include <string>
#include <sqlite3.h>
using namespace v8;
using namespace node;
enum ReadyState {
CLOSED,
OPENING,
OPEN,
CLOSING
};
class Database : public EventEmitter {
public:
static Persistent<FunctionTemplate> constructor_template;
static void Init(v8::Handle<Object> target);
protected:
Database() : EventEmitter(), db_(NULL) { }
Database() : EventEmitter(),
handle(NULL),
pending(0),
readyState(CLOSED) {
}
~Database() {
assert(db_ == NULL);
printf("Destroying database\n");
fprintf(stderr, "Calling destructor\n");
}
static Handle<Value> New(const Arguments& args);
static int EIO_AfterOpen(eio_req *req);
static int EIO_Open(eio_req *req);
static Handle<Value> OpenSync(const Arguments& args);
static Handle<Value> Open(const Arguments& args);
static bool Open(Database* db);
static int EIO_Open(eio_req *req);
static int EIO_AfterOpen(eio_req *req);
static int EIO_AfterClose(eio_req *req);
static int EIO_Close(eio_req *req);
static Handle<Value> CloseSync(const Arguments& args);
static Handle<Value> Close(const Arguments& args);
static bool Close(Database* db);
static int EIO_Close(eio_req *req);
static int EIO_AfterClose(eio_req *req);
// static Handle<Value> LastInsertRowid(const Arguments& args);
static int EIO_AfterPrepareAndStep(eio_req *req);
static int EIO_PrepareAndStep(eio_req *req);
static Handle<Value> PrepareAndStep(const Arguments& args);
......@@ -56,35 +72,29 @@ class Database : public EventEmitter {
static int EIO_Prepare(eio_req *req);
static Handle<Value> Prepare(const Arguments& args);
// Return a pointer to the Sqlite handle pointer so that EIO_Open can
// pass it to sqlite3_open which wants a pointer to an sqlite3 pointer. This
// is because it wants to initialize our original (sqlite3*) pointer to
// point to an valid object.
sqlite3** GetDBPtr(void) { return &db_; }
void Wrap (Handle<Object> handle);
static void Destruct (Persistent<Value> value, void *data);
static int EIO_Destruct(eio_req *req);
static int EIO_AfterDestruct(eio_req *req);
protected:
sqlite3* handle;
std::string filename;
int open_mode;
std::string error_message;
int error_status;
sqlite3* db_;
int pending;
ReadyState readyState;
};
enum ExecMode
{
enum ExecMode {
EXEC_EMPTY = 0,
EXEC_LAST_INSERT_ID = 1,
EXEC_AFFECTED_ROWS = 2
};
struct open_request {
Persistent<Function> cb;
Database *db;
char filename[1];
};
struct close_request {
Persistent<Function> cb;
Database *db;
};
struct prepare_request {
Persistent<Function> cb;
Database *db;
......
......@@ -15,6 +15,9 @@
#ifndef NODE_SQLITE3_SRC_MACROS_H
#define NODE_SQLITE3_SRC_MACROS_H
const char* sqlite_code_string(int code);
#define CHECK(rc) { if ((rc) != SQLITE_OK) \
return ThrowException(Exception::Error(String::New( \
sqlite3_errmsg(*db)))); }
......@@ -23,37 +26,110 @@
return ThrowException(Exception::Error(String::New( \
sqlite3_errmsg(sqlite3_db_handle(sto->stmt_))))); }
#define REQ_ARGS(N) \
if (args.Length() < (N)) \
return ThrowException(Exception::TypeError( \
String::New("Expected " #N "arguments")));
#define REQ_STR_ARG(I, VAR) \
if (args.Length() <= (I) || !args[I]->IsString()) \
return ThrowException(Exception::TypeError( \
String::New("Argument " #I " must be a string"))); \
String::Utf8Value VAR(args[I]->ToString());
#define REQ_FUN_ARG(I, VAR) \
if (args.Length() <= (I) || !args[I]->IsFunction()) \
return ThrowException(Exception::TypeError( \
String::New("Argument " #I " must be a function"))); \
Local<Function> VAR = Local<Function>::Cast(args[I]);
#define REQ_EXT_ARG(I, VAR) \
if (args.Length() <= (I) || !args[I]->IsExternal()) \
return ThrowException(Exception::TypeError( \
String::New("Argument " #I " invalid"))); \
Local<External> VAR = Local<External>::Cast(args[I]);
#define OPT_INT_ARG(I, VAR, DEFAULT) \
int VAR; \
if (args.Length() <= (I)) { \
VAR = (DEFAULT); \
} else if (args[I]->IsInt32()) { \
VAR = args[I]->Int32Value(); \
} else { \
return ThrowException(Exception::TypeError( \
String::New("Argument " #I " must be an integer"))); \
}
#define REQUIRE_ARGUMENTS(n) \
if (args.Length() < (n)) { \
return ThrowException( \
Exception::TypeError(String::New("Expected " #n "arguments")) \
); \
}
#define REQUIRE_ARGUMENT_EXTERNAL(i, var) \
if (args.Length() <= (i) || !args[i]->IsExternal()) { \
return ThrowException( \
Exception::TypeError(String::New("Argument " #i " invalid")) \
); \
} \
Local<External> var = Local<External>::Cast(args[i]);
#define REQUIRE_ARGUMENT_FUNCTION(i, var) \
if (args.Length() <= (i) || !args[i]->IsFunction()) { \
return ThrowException(Exception::TypeError( \
String::New("Argument " #i " must be a function")) \
); \
} \
Local<Function> var = Local<Function>::Cast(args[i]);
#define REQUIRE_ARGUMENT_STRING(i, var) \
if (args.Length() <= (i) || !args[i]->IsString()) { \
return ThrowException(Exception::TypeError( \
String::New("Argument " #i " must be a string")) \
); \
} \
String::Utf8Value var(args[i]->ToString());
#define OPTIONAL_ARGUMENT_FUNCTION(i, var) \
Local<Function> var; \
bool var ## _exists = false; \
if (args.Length() >= i) { \
if (!args[i]->IsFunction()) { \
return ThrowException(Exception::TypeError( \
String::New("Argument " #i " must be a function")) \
); \
} \
var = Local<Function>::Cast(args[i]); \
var ## _exists = true; \
}
#define OPTIONAL_ARGUMENT_INTEGER(i, var, default) \
int var; \
if (args.Length() <= (i)) { \
var = (default); \
} \
else if (args[i]->IsInt32()) { \
var = args[i]->Int32Value(); \
} \
else { \
return ThrowException(Exception::TypeError( \
String::New("Argument " #i " must be an integer")) \
); \
}
#define DEFINE_CONSTANT_INTEGER(target, constant, name) \
(target)->Set( \
String::NewSymbol(#name), \
Integer::New(constant), \
static_cast<PropertyAttribute>(ReadOnly | DontDelete) \
);
#define DEFINE_CONSTANT_STRING(target, constant, name) \
(target)->Set( \
String::NewSymbol(#name), \
String::NewSymbol(constant), \
static_cast<PropertyAttribute>(ReadOnly | DontDelete) \
);
#define NODE_SET_GETTER(target, name, function) \
(target)->InstanceTemplate() \
->SetAccessor(String::NewSymbol(name), (function));
#define GET_STRING(source, name, property) \
String::Utf8Value name((source)->Get(String::NewSymbol(property)));
#define GET_INTEGER(source, name, property) \
int name = (source)->Get(String::NewSymbol(property))->Int32Value();
#define EXCEPTION(msg, errno, name) \
Local<Value> name = Exception::Error( \
String::Concat( \
String::Concat( \
String::NewSymbol(sqlite_code_string(errno)), \
String::NewSymbol(": ") \
), \
String::New(msg) \
) \
); \
Local<Object> name ## _obj = name->ToObject(); \
name ## _obj->Set(NODE_PSYMBOL("errno"), Integer::New(errno)); \
name ## _obj->Set(NODE_PSYMBOL("code"), \
String::NewSymbol(sqlite_code_string(errno)));
#endif
......@@ -16,11 +16,54 @@
#include <node.h>
#include <node_events.h>
#include <sqlite3.h>
#include "macros.h"
#include "database.h"
#include "statement.h"
extern "C" void init (v8::Handle<Object> target) {
Database::Init(target);
Statement::Init(target);
Database::Init(target);
Statement::Init(target);
DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READONLY, OPEN_READONLY);
DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READWRITE, OPEN_READWRITE);
DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_CREATE, OPEN_CREATE);
DEFINE_CONSTANT_STRING(target, SQLITE_VERSION, VERSION);
DEFINE_CONSTANT_INTEGER(target, SQLITE_VERSION_NUMBER, VERSION_NUMBER);
}
const char* sqlite_code_string(int code) {
switch (code) {
case SQLITE_OK: return "SQLITE_OK";
case SQLITE_ERROR: return "SQLITE_ERROR";
case SQLITE_INTERNAL: return "SQLITE_INTERNAL";
case SQLITE_PERM: return "SQLITE_PERM";
case SQLITE_ABORT: return "SQLITE_ABORT";
case SQLITE_BUSY: return "SQLITE_BUSY";
case SQLITE_LOCKED: return "SQLITE_LOCKED";
case SQLITE_NOMEM: return "SQLITE_NOMEM";
case SQLITE_READONLY: return "SQLITE_READONLY";
case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT";
case SQLITE_IOERR: return "SQLITE_IOERR";
case SQLITE_CORRUPT: return "SQLITE_CORRUPT";
case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND";
case SQLITE_FULL: return "SQLITE_FULL";
case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN";
case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL";
case SQLITE_EMPTY: return "SQLITE_EMPTY";
case SQLITE_SCHEMA: return "SQLITE_SCHEMA";
case SQLITE_TOOBIG: return "SQLITE_TOOBIG";
case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT";
case SQLITE_MISMATCH: return "SQLITE_MISMATCH";
case SQLITE_MISUSE: return "SQLITE_MISUSE";
case SQLITE_NOLFS: return "SQLITE_NOLFS";
case SQLITE_AUTH: return "SQLITE_AUTH";
case SQLITE_FORMAT: return "SQLITE_FORMAT";
case SQLITE_RANGE: return "SQLITE_RANGE";
case SQLITE_NOTADB: return "SQLITE_NOTADB";
case SQLITE_ROW: return "SQLITE_ROW";
case SQLITE_DONE: return "SQLITE_DONE";
default: return "UNKNOWN";
}
}
......@@ -50,7 +50,7 @@ void Statement::Init(v8::Handle<Object> target) {
Handle<Value> Statement::New(const Arguments& args) {
HandleScope scope;
REQ_EXT_ARG(0, stmt);
REQUIRE_ARGUMENT_EXTERNAL(0, stmt);
int first_rc = args[1]->IntegerValue();
int mode = args[2]->IntegerValue();
......@@ -169,8 +169,8 @@ Handle<Value> Statement::BindObject(const Arguments& args) {
HandleScope scope;
Statement* sto = ObjectWrap::Unwrap<Statement>(args.This());
REQ_ARGS(2);
REQ_FUN_ARG(1, cb);
REQUIRE_ARGUMENTS(2);
REQUIRE_ARGUMENT_FUNCTION(1, cb);
if (! args[0]->IsObject())
return ThrowException(Exception::TypeError(
......@@ -253,8 +253,8 @@ Handle<Value> Statement::BindArray(const Arguments& args) {
HandleScope scope;
Statement* sto = ObjectWrap::Unwrap<Statement>(args.This());
REQ_ARGS(2);
REQ_FUN_ARG(1, cb);
REQUIRE_ARGUMENTS(2);
REQUIRE_ARGUMENT_FUNCTION(1, cb);
if (! args[0]->IsArray())
return ThrowException(Exception::TypeError(
String::New("First argument must be an Array.")));
......@@ -349,8 +349,8 @@ Handle<Value> Statement::Bind(const Arguments& args) {
HandleScope scope;
Statement* sto = ObjectWrap::Unwrap<Statement>(args.This());
REQ_ARGS(2);
REQ_FUN_ARG(2, cb);
REQUIRE_ARGUMENTS(2);
REQUIRE_ARGUMENT_FUNCTION(2, cb);
if (!( args[0]->IsString()
|| args[0]->IsInt32()
......@@ -475,7 +475,7 @@ Handle<Value> Statement::Finalize(const Arguments& args) {
return ThrowException(Exception::Error(String::New("Already stepping")));
}
REQ_FUN_ARG(0, cb);
REQUIRE_ARGUMENT_FUNCTION(0, cb);
sto->SetCallback(cb);
......@@ -740,7 +740,7 @@ Handle<Value> Statement::Step(const Arguments& args) {
return ThrowException(Exception::Error(String::New("Already stepping")));
}
REQ_FUN_ARG(0, cb);
REQUIRE_ARGUMENT_FUNCTION(0, cb);
sto->SetCallback(cb);
......@@ -1011,7 +1011,7 @@ int Statement::EIO_FetchAll(eio_req *req) {
Handle<Value> Statement::FetchAll(const Arguments& args) {
HandleScope scope;
REQ_FUN_ARG(0, cb);
REQUIRE_ARGUMENT_FUNCTION(0, cb);
struct fetchall_request *fetchall_req = (struct fetchall_request *)
calloc(1, sizeof(struct fetchall_request));
......
......@@ -14,11 +14,10 @@ exports['Blob overflow test'] = function(beforeExit) {
var inserted = 0;
var retrieved = 0;
db.openSync('');
Step(
function() {
db.open('', this);
},
function() {
var next = this;
db.prepare('CREATE TABLE elmos (image BLOB);', function(err, statement) {
assert.isUndefined(err);
......
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