Commit db72749d by Konstantin Käfer

unify indentation and rework database opening, closing and garbage collection

parent 493050a9
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
var sqlite3 = module.exports = exports = require('./sqlite3_bindings'); var sqlite3 = module.exports = exports = require('./sqlite3_bindings');
var sys = require("sys"); var sys = require("sys");
/*
sqlite3.Database.prototype.query = function(sql, bindings, rowCallback) { sqlite3.Database.prototype.query = function(sql, bindings, rowCallback) {
var self = this; var self = this;
...@@ -68,7 +71,7 @@ function _doStep(db, statement, rowCallback) { ...@@ -68,7 +71,7 @@ function _doStep(db, statement, rowCallback) {
// Execute a single SQL query with the given optional parameters. Calls // Execute a single SQL query with the given optional parameters. Calls
// `callback` with all rows or an error on query completion. // `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 self = this;
var bindings, callback; var bindings, callback;
var n = arguments.length; var n = arguments.length;
...@@ -248,3 +251,4 @@ sqlite3.sanitizeError = function(err, data) { ...@@ -248,3 +251,4 @@ sqlite3.sanitizeError = function(err, data) {
'" with values ' + JSON.stringify(data, false, 4); '" with values ' + JSON.stringify(data, false, 4);
return err; return err;
}; };
*/
\ No newline at end of file
...@@ -38,17 +38,19 @@ void Database::Init(v8::Handle<Object> target) { ...@@ -38,17 +38,19 @@ void Database::Init(v8::Handle<Object> target) {
constructor_template->SetClassName(String::NewSymbol("Database")); constructor_template->SetClassName(String::NewSymbol("Database"));
NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open); 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, "close", Close);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "closeSync", CloseSync);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare); NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepare", Prepare);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "prepareAndStep", PrepareAndStep);
target->Set(v8::String::NewSymbol("Database"), target->Set(v8::String::NewSymbol("Database"),
constructor_template->GetFunction()); constructor_template->GetFunction());
// insert/update execution result mask // insert/update execution result mask
NODE_DEFINE_CONSTANT (target, EXEC_EMPTY); NODE_DEFINE_CONSTANT(target, EXEC_EMPTY);
NODE_DEFINE_CONSTANT (target, EXEC_LAST_INSERT_ID); NODE_DEFINE_CONSTANT(target, EXEC_LAST_INSERT_ID);
NODE_DEFINE_CONSTANT (target, EXEC_AFFECTED_ROWS); NODE_DEFINE_CONSTANT(target, EXEC_AFFECTED_ROWS);
} }
Handle<Value> Database::New(const Arguments& args) { Handle<Value> Database::New(const Arguments& args) {
...@@ -58,152 +60,170 @@ Handle<Value> Database::New(const Arguments& args) { ...@@ -58,152 +60,170 @@ Handle<Value> Database::New(const Arguments& args) {
return args.This(); return args.This();
} }
int Database::EIO_AfterOpen(eio_req *req) {
ev_unref(EV_DEFAULT_UC);
Handle<Value> Database::OpenSync(const Arguments& args) {
HandleScope scope; HandleScope scope;
struct open_request *open_req = (struct open_request *)(req->data); Database* db = ObjectWrap::Unwrap<Database>(args.This());
Local<Value> argv[1]; if (db->readyState == CLOSED) {
bool err = false; if (!Open(db)) {
if (req->result) { EXCEPTION(db->error_message.c_str(), db->error_status, exception);
err = true; return ThrowException(exception);
argv[0] = Exception::Error(String::New("Error opening database"));
} }
else {
args.This()->Set(String::NewSymbol("opened"), True(), ReadOnly);
db->Emit(String::NewSymbol("opened"), 0, NULL);
TryCatch try_catch; if (db->pending == 0) {
db->Emit(String::NewSymbol("idle"), 0, NULL);
}
}
}
open_req->db->Unref(); return args.This();
open_req->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv); }
if (try_catch.HasCaught()) { Handle<Value> Database::Open(const Arguments& args) {
FatalException(try_catch); HandleScope scope;
} Database* db = ObjectWrap::Unwrap<Database>(args.This());
open_req->db->Emit(String::New("ready"), 0, NULL); if (db->readyState == CLOSED) {
open_req->cb.Dispose(); db->readyState = OPENING;
db->Ref();
eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, db);
ev_ref(EV_DEFAULT_UC);
}
free(open_req); return args.This();
}
return 0; 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;
}
} }
int Database::EIO_Open(eio_req *req) { int Database::EIO_Open(eio_req *req) {
struct open_request *open_req = (struct open_request *)(req->data); Database* db = static_cast<Database*>(req->data);
Open(db);
sqlite3 **dbptr = open_req->db->GetDBPtr(); return 0;
int rc = sqlite3_open_v2( open_req->filename }
, dbptr
, SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
| SQLITE_OPEN_FULLMUTEX
, NULL);
req->result = rc; int Database::EIO_AfterOpen(eio_req *req) {
HandleScope scope;
Database* db = static_cast<Database*>(req->data);
ev_unref(EV_DEFAULT_UC);
db->Unref();
// Set the a 10s timeout valuei for retries on BUSY errors. Local<Value> argv[1];
sqlite3_busy_timeout(*dbptr, 10000); 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);
}
db->Emit(String::NewSymbol("opened"), 1, argv);
// sqlite3 *db = *dbptr; if (db->pending == 0) {
// sqlite3_commit_hook(db, CommitHook, open_req->db); db->Emit(String::NewSymbol("idle"), 0, NULL);
// sqlite3_rollback_hook(db, RollbackHook, open_req->db); }
// sqlite3_update_hook(db, UpdateHook, open_req->db);
return 0; return 0;
} }
Handle<Value> Database::Open(const Arguments& args) {
HandleScope scope;
REQ_STR_ARG(0, filename);
REQ_FUN_ARG(1, cb);
Handle<Value> Database::CloseSync(const Arguments& args) {
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This()); Database* db = ObjectWrap::Unwrap<Database>(args.This());
struct open_request *open_req = (struct open_request *) if (db->readyState == OPEN) {
calloc(1, sizeof(struct open_request) + filename.length()); if (!Close(db)) {
EXCEPTION(db->error_message.c_str(), db->error_status, exception);
if (!open_req) { return ThrowException(exception);
V8::LowMemoryNotification(); }
return ThrowException(Exception::Error( else {
String::New("Could not allocate enough memory"))); args.This()->Set(String::NewSymbol("opened"), False(), ReadOnly);
db->Emit(String::NewSymbol("closed"), 0, NULL);
}
} }
strcpy(open_req->filename, *filename); return True();
open_req->cb = Persistent<Function>::New(cb); }
open_req->db = db;
eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, open_req); Handle<Value> Database::Close(const Arguments& args) {
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This());
ev_ref(EV_DEFAULT_UC); if (db->readyState == OPEN) {
db->readyState = CLOSING;
db->Ref(); db->Ref();
eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, db);
ev_ref(EV_DEFAULT_UC);
}
return Undefined(); return args.This();
} }
int Database::EIO_AfterClose(eio_req *req) { bool Database::Close(Database* db) {
ev_unref(EV_DEFAULT_UC); assert(db->handle);
HandleScope scope;
struct close_request *close_req = (struct close_request *)(req->data); db->error_status = sqlite3_close(db->handle);
Local<Value> argv[1]; if (db->error_status != SQLITE_OK) {
bool err = false; db->error_message = std::string(sqlite3_errmsg(db->handle));
if (req->result) { db->readyState = OPEN;
err = true; return false;
argv[0] = Exception::Error(String::New("Error closing database"));
} }
else {
TryCatch try_catch; db->readyState = CLOSED;
db->handle = NULL;
close_req->db->Unref(); return true;
close_req->cb->Call(Context::GetCurrent()->Global(), err ? 1 : 0, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
} }
close_req->cb.Dispose();
free(close_req);
return 0;
} }
int Database::EIO_Close(eio_req *req) { int Database::EIO_Close(eio_req *req) {
struct close_request *close_req = (struct close_request *)(req->data); Database* db = static_cast<Database*>(req->data);
Database* db = close_req->db; Close(db);
req->result = sqlite3_close(db->db_);
db->db_ = NULL;
return 0; return 0;
} }
Handle<Value> Database::Close(const Arguments& args) { int Database::EIO_AfterClose(eio_req *req) {
HandleScope scope; HandleScope scope;
Database* db = static_cast<Database*>(req->data);
ev_unref(EV_DEFAULT_UC);
db->Unref();
REQ_FUN_ARG(0, cb); Local<Value> argv[1];
if (db->error_status != SQLITE_OK) {
Database* db = ObjectWrap::Unwrap<Database>(args.This()); EXCEPTION(db->error_message.c_str(), db->error_status, exception);
argv[0] = exception;
struct close_request *close_req = (struct close_request *) }
calloc(1, sizeof(struct close_request)); else {
argv[0] = Local<Value>::New(Null());
if (!close_req) { db->handle_->Set(String::NewSymbol("opened"), False(), ReadOnly);
V8::LowMemoryNotification();
return ThrowException(Exception::Error(
String::New("Could not allocate enough memory")));
} }
close_req->cb = Persistent<Function>::New(cb); db->Emit(String::NewSymbol("closed"), 1, argv);
close_req->db = db;
eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, close_req);
ev_ref(EV_DEFAULT_UC);
db->Ref();
return Undefined(); return 0;
} }
// // TODO: libeio'fy // // TODO: libeio'fy
...@@ -243,7 +263,7 @@ int Database::EIO_AfterPrepareAndStep(eio_req *req) { ...@@ -243,7 +263,7 @@ int Database::EIO_AfterPrepareAndStep(eio_req *req) {
// if the prepare failed // if the prepare failed
if (req->result != SQLITE_OK) { if (req->result != SQLITE_OK) {
argv[0] = Exception::Error( argv[0] = Exception::Error(
String::New(sqlite3_errmsg(prep_req->db->db_))); String::New(sqlite3_errmsg(prep_req->db->handle)));
argc = 1; argc = 1;
} }
...@@ -307,7 +327,7 @@ int Database::EIO_PrepareAndStep(eio_req *req) { ...@@ -307,7 +327,7 @@ int Database::EIO_PrepareAndStep(eio_req *req) {
prep_req->stmt = NULL; prep_req->stmt = NULL;
prep_req->tail = NULL; prep_req->tail = NULL;
sqlite3* db = prep_req->db->db_; sqlite3* db = prep_req->db->handle;
int rc = sqlite3_prepare_v2(db, prep_req->sql, -1, int rc = sqlite3_prepare_v2(db, prep_req->sql, -1,
&(prep_req->stmt), &(prep_req->tail)); &(prep_req->stmt), &(prep_req->tail));
...@@ -346,9 +366,9 @@ int Database::EIO_PrepareAndStep(eio_req *req) { ...@@ -346,9 +366,9 @@ int Database::EIO_PrepareAndStep(eio_req *req) {
Handle<Value> Database::PrepareAndStep(const Arguments& args) { Handle<Value> Database::PrepareAndStep(const Arguments& args) {
HandleScope scope; HandleScope scope;
REQ_STR_ARG(0, sql); REQUIRE_ARGUMENT_STRING(0, sql);
REQ_FUN_ARG(1, cb); REQUIRE_ARGUMENT_FUNCTION(1, cb);
OPT_INT_ARG(2, mode, EXEC_EMPTY); OPTIONAL_ARGUMENT_INTEGER(2, mode, EXEC_EMPTY);
Database* db = ObjectWrap::Unwrap<Database>(args.This()); Database* db = ObjectWrap::Unwrap<Database>(args.This());
...@@ -385,7 +405,7 @@ int Database::EIO_AfterPrepare(eio_req *req) { ...@@ -385,7 +405,7 @@ int Database::EIO_AfterPrepare(eio_req *req) {
// if the prepare failed // if the prepare failed
if (req->result != SQLITE_OK) { if (req->result != SQLITE_OK) {
argv[0] = Exception::Error( argv[0] = Exception::Error(
String::New(sqlite3_errmsg(prep_req->db->db_))); String::New(sqlite3_errmsg(prep_req->db->handle)));
argc = 1; argc = 1;
} }
else { else {
...@@ -423,7 +443,7 @@ int Database::EIO_Prepare(eio_req *req) { ...@@ -423,7 +443,7 @@ int Database::EIO_Prepare(eio_req *req) {
prep_req->stmt = NULL; prep_req->stmt = NULL;
prep_req->tail = NULL; prep_req->tail = NULL;
sqlite3* db = prep_req->db->db_; sqlite3* db = prep_req->db->handle;
int rc = sqlite3_prepare_v2(db, prep_req->sql, -1, int rc = sqlite3_prepare_v2(db, prep_req->sql, -1,
&(prep_req->stmt), &(prep_req->tail)); &(prep_req->stmt), &(prep_req->tail));
...@@ -449,7 +469,7 @@ Handle<Value> Database::Prepare(const Arguments& args) { ...@@ -449,7 +469,7 @@ Handle<Value> Database::Prepare(const Arguments& args) {
Local<Function> cb; Local<Function> cb;
int mode; int mode;
REQ_STR_ARG(0, sql); REQUIRE_ARGUMENT_STRING(0, sql);
// middle argument could be options or // middle argument could be options or
switch (args.Length()) { switch (args.Length()) {
...@@ -509,3 +529,33 @@ Handle<Value> Database::Prepare(const Arguments& args) { ...@@ -509,3 +529,33 @@ Handle<Value> Database::Prepare(const Arguments& args) {
return Undefined(); 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;
}
...@@ -19,35 +19,51 @@ ...@@ -19,35 +19,51 @@
#include <node.h> #include <node.h>
#include <node_events.h> #include <node_events.h>
#include <string>
#include <sqlite3.h> #include <sqlite3.h>
using namespace v8; using namespace v8;
using namespace node; using namespace node;
enum ReadyState {
CLOSED,
OPENING,
OPEN,
CLOSING
};
class Database : public EventEmitter { class Database : public EventEmitter {
public: public:
static Persistent<FunctionTemplate> constructor_template; static Persistent<FunctionTemplate> constructor_template;
static void Init(v8::Handle<Object> target); static void Init(v8::Handle<Object> target);
protected: protected:
Database() : EventEmitter(), db_(NULL) { } Database() : EventEmitter(),
handle(NULL),
pending(0),
readyState(CLOSED) {
}
~Database() { ~Database() {
assert(db_ == NULL); fprintf(stderr, "Calling destructor\n");
printf("Destroying database\n");
} }
static Handle<Value> New(const Arguments& args); static Handle<Value> New(const Arguments& args);
static int EIO_AfterOpen(eio_req *req); static Handle<Value> OpenSync(const Arguments& args);
static int EIO_Open(eio_req *req);
static Handle<Value> Open(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 Handle<Value> CloseSync(const Arguments& args);
static int EIO_Close(eio_req *req);
static Handle<Value> Close(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_AfterPrepareAndStep(eio_req *req);
static int EIO_PrepareAndStep(eio_req *req); static int EIO_PrepareAndStep(eio_req *req);
static Handle<Value> PrepareAndStep(const Arguments& args); static Handle<Value> PrepareAndStep(const Arguments& args);
...@@ -56,35 +72,29 @@ class Database : public EventEmitter { ...@@ -56,35 +72,29 @@ class Database : public EventEmitter {
static int EIO_Prepare(eio_req *req); static int EIO_Prepare(eio_req *req);
static Handle<Value> Prepare(const Arguments& args); static Handle<Value> Prepare(const Arguments& args);
// Return a pointer to the Sqlite handle pointer so that EIO_Open can void Wrap (Handle<Object> handle);
// pass it to sqlite3_open which wants a pointer to an sqlite3 pointer. This static void Destruct (Persistent<Value> value, void *data);
// is because it wants to initialize our original (sqlite3*) pointer to static int EIO_Destruct(eio_req *req);
// point to an valid object. static int EIO_AfterDestruct(eio_req *req);
sqlite3** GetDBPtr(void) { return &db_; }
protected: 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_EMPTY = 0,
EXEC_LAST_INSERT_ID = 1, EXEC_LAST_INSERT_ID = 1,
EXEC_AFFECTED_ROWS = 2 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 { struct prepare_request {
Persistent<Function> cb; Persistent<Function> cb;
Database *db; Database *db;
......
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
#ifndef NODE_SQLITE3_SRC_MACROS_H #ifndef NODE_SQLITE3_SRC_MACROS_H
#define 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) \ #define CHECK(rc) { if ((rc) != SQLITE_OK) \
return ThrowException(Exception::Error(String::New( \ return ThrowException(Exception::Error(String::New( \
sqlite3_errmsg(*db)))); } sqlite3_errmsg(*db)))); }
...@@ -23,37 +26,110 @@ ...@@ -23,37 +26,110 @@
return ThrowException(Exception::Error(String::New( \ return ThrowException(Exception::Error(String::New( \
sqlite3_errmsg(sqlite3_db_handle(sto->stmt_))))); } 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) \ #define REQUIRE_ARGUMENTS(n) \
if (args.Length() <= (I) || !args[I]->IsString()) \ 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( \ return ThrowException(Exception::TypeError( \
String::New("Argument " #I " must be a string"))); \ String::New("Argument " #i " must be a function")) \
String::Utf8Value VAR(args[I]->ToString()); ); \
} \
Local<Function> var = Local<Function>::Cast(args[i]);
#define REQ_FUN_ARG(I, VAR) \ #define REQUIRE_ARGUMENT_STRING(i, var) \
if (args.Length() <= (I) || !args[I]->IsFunction()) \ if (args.Length() <= (i) || !args[i]->IsString()) { \
return ThrowException(Exception::TypeError( \ return ThrowException(Exception::TypeError( \
String::New("Argument " #I " must be a function"))); \ String::New("Argument " #i " must be a string")) \
Local<Function> VAR = Local<Function>::Cast(args[I]); ); \
} \
String::Utf8Value var(args[i]->ToString());
#define REQ_EXT_ARG(I, VAR) \
if (args.Length() <= (I) || !args[I]->IsExternal()) \ #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( \ return ThrowException(Exception::TypeError( \
String::New("Argument " #I " invalid"))); \ String::New("Argument " #i " must be a function")) \
Local<External> VAR = Local<External>::Cast(args[I]); ); \
} \
#define OPT_INT_ARG(I, VAR, DEFAULT) \ var = Local<Function>::Cast(args[i]); \
int VAR; \ var ## _exists = true; \
if (args.Length() <= (I)) { \ }
VAR = (DEFAULT); \
} else if (args[I]->IsInt32()) { \
VAR = args[I]->Int32Value(); \ #define OPTIONAL_ARGUMENT_INTEGER(i, var, default) \
} else { \ int var; \
if (args.Length() <= (i)) { \
var = (default); \
} \
else if (args[i]->IsInt32()) { \
var = args[i]->Int32Value(); \
} \
else { \
return ThrowException(Exception::TypeError( \ return ThrowException(Exception::TypeError( \
String::New("Argument " #I " must be an integer"))); \ 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 #endif
...@@ -16,11 +16,54 @@ ...@@ -16,11 +16,54 @@
#include <node.h> #include <node.h>
#include <node_events.h> #include <node_events.h>
#include <sqlite3.h>
#include "macros.h"
#include "database.h" #include "database.h"
#include "statement.h" #include "statement.h"
extern "C" void init (v8::Handle<Object> target) { extern "C" void init (v8::Handle<Object> target) {
Database::Init(target); Database::Init(target);
Statement::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) { ...@@ -50,7 +50,7 @@ void Statement::Init(v8::Handle<Object> target) {
Handle<Value> Statement::New(const Arguments& args) { Handle<Value> Statement::New(const Arguments& args) {
HandleScope scope; HandleScope scope;
REQ_EXT_ARG(0, stmt); REQUIRE_ARGUMENT_EXTERNAL(0, stmt);
int first_rc = args[1]->IntegerValue(); int first_rc = args[1]->IntegerValue();
int mode = args[2]->IntegerValue(); int mode = args[2]->IntegerValue();
...@@ -169,8 +169,8 @@ Handle<Value> Statement::BindObject(const Arguments& args) { ...@@ -169,8 +169,8 @@ Handle<Value> Statement::BindObject(const Arguments& args) {
HandleScope scope; HandleScope scope;
Statement* sto = ObjectWrap::Unwrap<Statement>(args.This()); Statement* sto = ObjectWrap::Unwrap<Statement>(args.This());
REQ_ARGS(2); REQUIRE_ARGUMENTS(2);
REQ_FUN_ARG(1, cb); REQUIRE_ARGUMENT_FUNCTION(1, cb);
if (! args[0]->IsObject()) if (! args[0]->IsObject())
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
...@@ -253,8 +253,8 @@ Handle<Value> Statement::BindArray(const Arguments& args) { ...@@ -253,8 +253,8 @@ Handle<Value> Statement::BindArray(const Arguments& args) {
HandleScope scope; HandleScope scope;
Statement* sto = ObjectWrap::Unwrap<Statement>(args.This()); Statement* sto = ObjectWrap::Unwrap<Statement>(args.This());
REQ_ARGS(2); REQUIRE_ARGUMENTS(2);
REQ_FUN_ARG(1, cb); REQUIRE_ARGUMENT_FUNCTION(1, cb);
if (! args[0]->IsArray()) if (! args[0]->IsArray())
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
String::New("First argument must be an Array."))); String::New("First argument must be an Array.")));
...@@ -349,8 +349,8 @@ Handle<Value> Statement::Bind(const Arguments& args) { ...@@ -349,8 +349,8 @@ Handle<Value> Statement::Bind(const Arguments& args) {
HandleScope scope; HandleScope scope;
Statement* sto = ObjectWrap::Unwrap<Statement>(args.This()); Statement* sto = ObjectWrap::Unwrap<Statement>(args.This());
REQ_ARGS(2); REQUIRE_ARGUMENTS(2);
REQ_FUN_ARG(2, cb); REQUIRE_ARGUMENT_FUNCTION(2, cb);
if (!( args[0]->IsString() if (!( args[0]->IsString()
|| args[0]->IsInt32() || args[0]->IsInt32()
...@@ -475,7 +475,7 @@ Handle<Value> Statement::Finalize(const Arguments& args) { ...@@ -475,7 +475,7 @@ Handle<Value> Statement::Finalize(const Arguments& args) {
return ThrowException(Exception::Error(String::New("Already stepping"))); return ThrowException(Exception::Error(String::New("Already stepping")));
} }
REQ_FUN_ARG(0, cb); REQUIRE_ARGUMENT_FUNCTION(0, cb);
sto->SetCallback(cb); sto->SetCallback(cb);
...@@ -740,7 +740,7 @@ Handle<Value> Statement::Step(const Arguments& args) { ...@@ -740,7 +740,7 @@ Handle<Value> Statement::Step(const Arguments& args) {
return ThrowException(Exception::Error(String::New("Already stepping"))); return ThrowException(Exception::Error(String::New("Already stepping")));
} }
REQ_FUN_ARG(0, cb); REQUIRE_ARGUMENT_FUNCTION(0, cb);
sto->SetCallback(cb); sto->SetCallback(cb);
...@@ -1011,7 +1011,7 @@ int Statement::EIO_FetchAll(eio_req *req) { ...@@ -1011,7 +1011,7 @@ int Statement::EIO_FetchAll(eio_req *req) {
Handle<Value> Statement::FetchAll(const Arguments& args) { Handle<Value> Statement::FetchAll(const Arguments& args) {
HandleScope scope; HandleScope scope;
REQ_FUN_ARG(0, cb); REQUIRE_ARGUMENT_FUNCTION(0, cb);
struct fetchall_request *fetchall_req = (struct fetchall_request *) struct fetchall_request *fetchall_req = (struct fetchall_request *)
calloc(1, sizeof(struct fetchall_request)); calloc(1, sizeof(struct fetchall_request));
......
...@@ -14,11 +14,10 @@ exports['Blob overflow test'] = function(beforeExit) { ...@@ -14,11 +14,10 @@ exports['Blob overflow test'] = function(beforeExit) {
var inserted = 0; var inserted = 0;
var retrieved = 0; var retrieved = 0;
db.openSync('');
Step( Step(
function() { function() {
db.open('', this);
},
function() {
var next = this; var next = this;
db.prepare('CREATE TABLE elmos (image BLOB);', function(err, statement) { db.prepare('CREATE TABLE elmos (image BLOB);', function(err, statement) {
assert.isUndefined(err); 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