Commit 9a79d3df by Konstantin Käfer

saving state of work

parent db72749d
...@@ -5,7 +5,7 @@ build: ...@@ -5,7 +5,7 @@ build:
clean: clean:
node-waf clean node-waf clean
test: test: build
expresso -I lib test/*.test.js expresso -I lib test/*.test.js
.PHONY: build clean test .PHONY: build clean test
\ No newline at end of file
...@@ -16,6 +16,40 @@ ...@@ -16,6 +16,40 @@
var sqlite3 = module.exports = exports = require('./sqlite3_bindings'); var sqlite3 = module.exports = exports = require('./sqlite3_bindings');
var sys = require("sys"); var sys = require("sys");
function noop(err) {
if (err) throw err;
};
var Database = sqlite3.Database;
// var realClose = Database.prototype.close;
// sqlite3.Database.prototype.close = function(callback) {
// if (this.status === sqlite3.STATUS_IDLE) {
// return realClose.call(this, callback);
// } else {
// this.once('idle', function() {
// realClose.call(this, callback);
// });
// }
// };
// var realClose = sqlite3.Database.prototype.close;
// sqlite3.Database.prototype.close = function() {
// var db = this;
// process.nextTick(function() {
// if (!db.idle) {
// db.on('idle', function fn() {
// realClose.call(db);
// db.removeListener('idle', fn);
// });
// } else {
// realClose.call(db);
// }
// });
// return this;
// };
/* /*
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "macros.h" #include "macros.h"
#include "database.h" #include "database.h"
#include "statement.h" #include "statement.h"
#include "deferred_call.h"
using namespace v8; using namespace v8;
using namespace node; using namespace node;
...@@ -55,44 +56,75 @@ void Database::Init(v8::Handle<Object> target) { ...@@ -55,44 +56,75 @@ void Database::Init(v8::Handle<Object> target) {
Handle<Value> Database::New(const Arguments& args) { Handle<Value> Database::New(const Arguments& args) {
HandleScope scope; HandleScope scope;
REQUIRE_ARGUMENT_STRING(0, filename);
OPTIONAL_ARGUMENT_INTEGER(1, mode, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
Database* db = new Database(); Database* db = new Database();
db->filename = std::string(*filename);
db->open_mode = mode;
args.This()->Set(String::NewSymbol("filename"), args[0]->ToString(), ReadOnly);
args.This()->Set(String::NewSymbol("mode"), Integer::New(mode), ReadOnly);
db->Wrap(args.This()); db->Wrap(args.This());
return args.This(); return args.This();
} }
inline void Database::RunQueue(std::queue<Call*> queue) {
}
Handle<Value> Database::OpenSync(const Arguments& args) { void Database::ProcessQueue(Database* db) {
HandleScope scope; while (!db->queue.empty()) {
Database* db = ObjectWrap::Unwrap<Database>(args.This()); Call* call = db->queue.front();
if (db->readyState == CLOSED) { if (!(call->Data() & db->status)) {
if (!Open(db)) { // The next task in the queue requires a different status than the
EXCEPTION(db->error_message.c_str(), db->error_status, exception); // one we're currently in. Wait before invoking it.
return ThrowException(exception); break;
} }
else {
args.This()->Set(String::NewSymbol("opened"), True(), ReadOnly);
db->Emit(String::NewSymbol("opened"), 0, NULL);
if (db->pending == 0) { if (call->Mode() == Deferred::Exclusive && db->pending > 0) {
db->Emit(String::NewSymbol("idle"), 0, NULL); // We have to wait for the pending tasks to complete before we
} // execute the exclusive task.
// break;
break;
} }
}
return args.This(); ev_unref(EV_DEFAULT_UC);
db->Unref();
TryCatch try_catch;
call->Invoke();
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
db->queue.pop();
delete call;
}
} }
Handle<Value> Database::Open(const Arguments& args) { Handle<Value> Database::Open(const Arguments& args) {
HandleScope scope; HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This()); Database* db = ObjectWrap::Unwrap<Database>(args.This());
if (db->readyState == CLOSED) { REQUIRE_ARGUMENT_FUNCTION(0, callback);
db->readyState = OPENING;
db->Ref(); // Make sure that node doesn't exit before all elements in the queue have
eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, db); // been dealt with.
ev_ref(EV_DEFAULT_UC); db->Ref();
ev_ref(EV_DEFAULT_UC);
if (db->status != IsClosed) {
db->queue.push(new Call(args, IsClosed, Deferred::Exclusive));
}
else {
db->status = IsOpening;
DatabaseBaton* baton =
new DatabaseBaton(db, Persistent<Function>::New(callback));
eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, baton);
} }
return args.This(); return args.This();
...@@ -108,75 +140,95 @@ bool Database::Open(Database* db) { ...@@ -108,75 +140,95 @@ bool Database::Open(Database* db) {
if (db->error_status != SQLITE_OK) { if (db->error_status != SQLITE_OK) {
db->error_message = std::string(sqlite3_errmsg(db->handle)); db->error_message = std::string(sqlite3_errmsg(db->handle));
db->readyState = CLOSED;
return false; return false;
} }
else { else {
db->readyState = OPEN;
return true; return true;
} }
} }
Handle<Value> Database::OpenSync(const Arguments& args) {
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This());
REQUIRE_ARGUMENT_FUNCTION(0, callback);
if (db->status == IsClosed) {
Local<Value> argv[1];
if (Open(db)) {
db->status = IsOpen;
argv[0] = Local<Value>::New(Null());
}
else {
EXCEPTION(db->error_message.c_str(), db->error_status, exception);
argv[0] = exception;
}
TRY_CATCH_CALL(args.This(), callback, 1, argv);
ProcessQueue(db);
}
else {
return ThrowException(Exception::Error(
String::New("Database is already open")));
}
return args.This();
}
int Database::EIO_Open(eio_req *req) { int Database::EIO_Open(eio_req *req) {
Database* db = static_cast<Database*>(req->data); DatabaseBaton* baton = static_cast<DatabaseBaton*>(req->data);
Open(db); Open(baton->db);
return 0; return 0;
} }
int Database::EIO_AfterOpen(eio_req *req) { int Database::EIO_AfterOpen(eio_req *req) {
HandleScope scope; HandleScope scope;
Database* db = static_cast<Database*>(req->data); DatabaseBaton* baton = static_cast<DatabaseBaton*>(req->data);
Database* db = baton->db;
ev_unref(EV_DEFAULT_UC); ev_unref(EV_DEFAULT_UC);
db->Unref(); db->Unref();
Local<Value> argv[1]; Local<Value> argv[1];
if (db->error_status != SQLITE_OK) { if (db->error_status == SQLITE_OK) {
EXCEPTION(db->error_message.c_str(), db->error_status, exception); db->status = IsOpen;
argv[0] = exception; argv[0] = Local<Value>::New(Null());
} }
else { else {
argv[0] = Local<Value>::New(Null()); db->status = IsClosed;
db->handle_->Set(String::NewSymbol("opened"), True(), ReadOnly); EXCEPTION(db->error_message.c_str(), db->error_status, exception);
argv[0] = exception;
} }
db->Emit(String::NewSymbol("opened"), 1, argv); TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
ProcessQueue(db);
if (db->pending == 0) { baton->callback.Dispose();
db->Emit(String::NewSymbol("idle"), 0, NULL); delete baton;
}
return 0; return 0;
} }
Handle<Value> Database::CloseSync(const Arguments& args) { Handle<Value> Database::Close(const Arguments& args) {
HandleScope scope; HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This()); Database* db = ObjectWrap::Unwrap<Database>(args.This());
if (db->readyState == OPEN) { REQUIRE_ARGUMENT_FUNCTION(0, callback);
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);
}
}
return True(); // Make sure that node doesn't exit before all elements in the queue have
} // been dealt with.
db->Ref();
Handle<Value> Database::Close(const Arguments& args) { ev_ref(EV_DEFAULT_UC);
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This());
if (db->readyState == OPEN) { if (db->status != IsOpen || db->pending > 0) {
db->readyState = CLOSING; db->queue.push(new Call(args, IsOpen, Deferred::Exclusive));
db->Ref(); }
eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, db); else {
ev_ref(EV_DEFAULT_UC); db->status = IsClosing;
DatabaseBaton* baton =
new DatabaseBaton(db, Persistent<Function>::New(callback));
eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, baton);
} }
return args.This(); return args.This();
...@@ -184,44 +236,74 @@ Handle<Value> Database::Close(const Arguments& args) { ...@@ -184,44 +236,74 @@ Handle<Value> Database::Close(const Arguments& args) {
bool Database::Close(Database* db) { bool Database::Close(Database* db) {
assert(db->handle); assert(db->handle);
db->error_status = sqlite3_close(db->handle); db->error_status = sqlite3_close(db->handle);
if (db->error_status != SQLITE_OK) { if (db->error_status != SQLITE_OK) {
db->error_message = std::string(sqlite3_errmsg(db->handle)); db->error_message = std::string(sqlite3_errmsg(db->handle));
db->readyState = OPEN;
return false; return false;
} }
else { else {
db->readyState = CLOSED;
db->handle = NULL; db->handle = NULL;
return true; return true;
} }
} }
Handle<Value> Database::CloseSync(const Arguments& args) {
HandleScope scope;
Database* db = ObjectWrap::Unwrap<Database>(args.This());
REQUIRE_ARGUMENT_FUNCTION(0, callback);
if (db->status == IsOpen && db->pending == 0) {
Local<Value> argv[1];
if (Close(db)) {
db->status = IsClosed;
argv[0] = Local<Value>::New(Null());
}
else {
EXCEPTION(db->error_message.c_str(), db->error_status, exception);
argv[0] = exception;
}
TRY_CATCH_CALL(args.This(), callback, 1, argv);
ProcessQueue(db);
}
else {
db->queue.push(new Call(args, IsOpen, Deferred::Exclusive));
}
return args.This();
}
int Database::EIO_Close(eio_req *req) { int Database::EIO_Close(eio_req *req) {
Database* db = static_cast<Database*>(req->data); DatabaseBaton* baton = static_cast<DatabaseBaton*>(req->data);
Close(db); Close(baton->db);
return 0; return 0;
} }
int Database::EIO_AfterClose(eio_req *req) { int Database::EIO_AfterClose(eio_req *req) {
HandleScope scope; HandleScope scope;
Database* db = static_cast<Database*>(req->data); DatabaseBaton* baton = static_cast<DatabaseBaton*>(req->data);
Database* db = baton->db;
ev_unref(EV_DEFAULT_UC); ev_unref(EV_DEFAULT_UC);
db->Unref(); db->Unref();
Local<Value> argv[1]; Local<Value> argv[1];
if (db->error_status != SQLITE_OK) { if (db->error_status != SQLITE_OK) {
db->status = IsOpen;
EXCEPTION(db->error_message.c_str(), db->error_status, exception); EXCEPTION(db->error_message.c_str(), db->error_status, exception);
argv[0] = exception; argv[0] = exception;
} }
else { else {
db->status = IsClosed;
argv[0] = Local<Value>::New(Null()); argv[0] = Local<Value>::New(Null());
db->handle_->Set(String::NewSymbol("opened"), False(), ReadOnly);
} }
db->Emit(String::NewSymbol("closed"), 1, argv); TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
ProcessQueue(db);
baton->callback.Dispose();
delete baton;
return 0; return 0;
} }
......
...@@ -19,30 +19,38 @@ ...@@ -19,30 +19,38 @@
#include <node.h> #include <node.h>
#include <node_events.h> #include <node_events.h>
#include "deferred_call.h"
#include <string> #include <string>
#include <queue>
#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);
static enum Status {
IsClosed = 1 << 0,
IsOpening = 1 << 1,
IsOpen = 1 << 2,
IsClosing = 1 << 3,
DoesntMatter = IsClosed | IsOpening | IsOpen | IsClosing
};
typedef Deferred::Call<Status> Call;
protected: protected:
Database() : EventEmitter(), Database() : EventEmitter(),
handle(NULL), handle(NULL),
pending(0), pending(0),
readyState(CLOSED) { status(IsClosed) {
} }
...@@ -52,6 +60,9 @@ class Database : public EventEmitter { ...@@ -52,6 +60,9 @@ class Database : public EventEmitter {
static Handle<Value> New(const Arguments& args); static Handle<Value> New(const Arguments& args);
static void ProcessQueue(Database* db);
inline static void RunQueue(std::queue<Call*> queue);
static Handle<Value> OpenSync(const Arguments& args); static Handle<Value> OpenSync(const Arguments& args);
static Handle<Value> Open(const Arguments& args); static Handle<Value> Open(const Arguments& args);
static bool Open(Database* db); static bool Open(Database* db);
...@@ -86,7 +97,17 @@ class Database : public EventEmitter { ...@@ -86,7 +97,17 @@ class Database : public EventEmitter {
int error_status; int error_status;
int pending; int pending;
ReadyState readyState; Status status;
std::queue<Call*> queue;
private:
};
struct DatabaseBaton {
DatabaseBaton(Database* db_, Persistent<Function> callback_) :
db(db_), callback(callback_) {};
Database* db;
Persistent<Function> callback;
}; };
enum ExecMode { enum ExecMode {
......
#ifndef NODE_SQLITE3_SRC_DEFERRED_CALL_H
#define NODE_SQLITE3_SRC_DEFERRED_CALL_H
#include <v8.h>
using namespace v8;
namespace Deferred {
enum Mode {
Concurrent,
Exclusive
};
template <typename T> class Call {
public:
Persistent<Function> callback_;
Persistent<Object> receiver_;
enum Mode mode_;
T data_;
int argc_;
Persistent<Value>* argv_;
public:
Call(const Arguments& args, const T data, Mode mode = Concurrent) {
callback_ = Persistent<Function>::New(args.Callee());
receiver_ = Persistent<Object>::New(args.This());
mode_ = mode;
data_ = data;
argc_ = args.Length();
argv_ = new Persistent<Value>[argc_];
for (int i = 0; i < argc_; i++) {
argv_[i] = Persistent<Value>::New(args[i]);
}
}
~Call() {
for (int i = 0; i < argc_; i++) {
argv_[i].Dispose();
}
delete[] argv_;
callback_.Dispose();
receiver_.Dispose();
}
inline Mode Mode() {
return mode_;
}
inline const T Data() {
return data_;
}
inline Local<Value> Invoke() {
return callback_->Call(receiver_, argc_, argv_);
}
};
}
#endif
...@@ -64,15 +64,13 @@ const char* sqlite_code_string(int code); ...@@ -64,15 +64,13 @@ const char* sqlite_code_string(int code);
#define OPTIONAL_ARGUMENT_FUNCTION(i, var) \ #define OPTIONAL_ARGUMENT_FUNCTION(i, var) \
Local<Function> var; \ Local<Function> var; \
bool var ## _exists = false; \ if (args.Length() > i && !args[i]->IsUndefined()) { \
if (args.Length() >= i) { \
if (!args[i]->IsFunction()) { \ if (!args[i]->IsFunction()) { \
return ThrowException(Exception::TypeError( \ return ThrowException(Exception::TypeError( \
String::New("Argument " #i " must be a function")) \ String::New("Argument " #i " must be a function")) \
); \ ); \
} \ } \
var = Local<Function>::Cast(args[i]); \ var = Local<Function>::Cast(args[i]); \
var ## _exists = true; \
} }
...@@ -116,7 +114,6 @@ const char* sqlite_code_string(int code); ...@@ -116,7 +114,6 @@ const char* sqlite_code_string(int code);
#define GET_INTEGER(source, name, property) \ #define GET_INTEGER(source, name, property) \
int name = (source)->Get(String::NewSymbol(property))->Int32Value(); int name = (source)->Get(String::NewSymbol(property))->Int32Value();
#define EXCEPTION(msg, errno, name) \ #define EXCEPTION(msg, errno, name) \
Local<Value> name = Exception::Error( \ Local<Value> name = Exception::Error( \
String::Concat( \ String::Concat( \
...@@ -127,9 +124,37 @@ const char* sqlite_code_string(int code); ...@@ -127,9 +124,37 @@ const char* sqlite_code_string(int code);
String::New(msg) \ String::New(msg) \
) \ ) \
); \ ); \
Local<Object> name ## _obj = name->ToObject(); \ Local<Object> name ##_obj = name->ToObject(); \
name ## _obj->Set(NODE_PSYMBOL("errno"), Integer::New(errno)); \ name ##_obj->Set(NODE_PSYMBOL("errno"), Integer::New(errno)); \
name ## _obj->Set(NODE_PSYMBOL("code"), \ name ##_obj->Set(NODE_PSYMBOL("code"), \
String::NewSymbol(sqlite_code_string(errno))); String::NewSymbol(sqlite_code_string(errno)));
#define EXCEPTION_STR
#define EVENT_ONCE(event, callback) \
Local<Value> argv[2] = { \
String::NewSymbol(event), \
args.This()->Get(String::NewSymbol(callback)) \
}; \
v8::Local<v8::Value> fn_val = args.This()->Get(String::NewSymbol("once")); \
Local<Function> fn = Local<Function>::Cast(fn_val); \
fn->Call(args.This(), 2, argv);
#define SET_STRING(sym, str) \
Set(String::NewSymbol(#sym), String::New(str), ReadOnly)
#define SET_INTEGER(sym, i) \
Set(String::NewSymbol(#sym), Integer::New(i), ReadOnly)
#define TRY_CATCH_CALL(context, callback, argc, argv) \
{ \
TryCatch try_catch; \
(callback)->Call((context), (argc), (argv)); \
if (try_catch.HasCaught()) { \
FatalException(try_catch); \
} \
}
#endif #endif
...@@ -21,16 +21,19 @@ ...@@ -21,16 +21,19 @@
#include "macros.h" #include "macros.h"
#include "database.h" #include "database.h"
#include "statement.h" #include "statement.h"
#include "demo.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);
Demo::Init(target);
DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READONLY, OPEN_READONLY); DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READONLY, OPEN_READONLY);
DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READWRITE, OPEN_READWRITE); DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READWRITE, OPEN_READWRITE);
DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_CREATE, OPEN_CREATE); DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_CREATE, OPEN_CREATE);
DEFINE_CONSTANT_STRING(target, SQLITE_VERSION, VERSION); DEFINE_CONSTANT_STRING(target, SQLITE_VERSION, VERSION);
DEFINE_CONSTANT_INTEGER(target, SQLITE_VERSION_NUMBER, VERSION_NUMBER); DEFINE_CONSTANT_INTEGER(target, SQLITE_VERSION_NUMBER, VERSION_NUMBER);
} }
const char* sqlite_code_string(int code) { const char* sqlite_code_string(int code) {
......
var sqlite = require('sqlite3'),
Step = require('step'),
fs = require('fs'),
assert = require('assert')
Buffer = require('buffer').Buffer;
// lots of elmo
var elmo = fs.readFileSync(__dirname + '/support/elmo.png', 'binary');
var elmo_str = elmo.toString('binary');
exports['Blob overflow test'] = function(beforeExit) {
var db = new sqlite.Database();
var total = 100;
var inserted = 0;
var retrieved = 0;
db.openSync('');
Step(
function() {
var next = this;
db.prepare('CREATE TABLE elmos (image BLOB);', function(err, statement) {
assert.isUndefined(err);
statement.step(next);
});
},
function() {
var group = this.group();
for (var i = 0; i < total; i++) {
var next = group();
db.prepare('INSERT INTO elmos (image) VALUES (?)', function(err, statement) {
assert.isUndefined(err);
statement.bind(1, elmo, function() {
statement.step(function(err) {
assert.isUndefined(err);
inserted++;
next();
});
});
});
}
},
function() {
var next = this;
db.execute('SELECT COUNT(*) as amount FROM elmos', function(err, rows) {
assert.isUndefined(err);
assert.eql(rows[0].amount, total);
next();
});
},
function() {
var next = this;
db.prepare('SELECT image FROM elmos;', function(err, statement) {
assert.isUndefined(err);
fetch();
function fetch() {
statement.step(function(err, row) {
assert.isUndefined(err);
if (row) {
// Not using assert.equal here because it's image data
// and we don't want that in the command line.
assert.ok(elmo_str === row.image);
retrieved++;
fetch();
}
else {
next();
}
});
}
});
}
);
beforeExit(function() {
assert.eql(inserted, total);
assert.eql(retrieved, total);
})
}
var sqlite3 = require('sqlite3');
var assert = require('assert');
var fs = require('fs');
var helper = require('./support/helper');
exports['constants'] = function() {
assert.ok(sqlite3.OPEN_READONLY === 1);
assert.ok(sqlite3.OPEN_READWRITE === 2);
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 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;
closed++;
}
db.open(openedCallback);
db.open(openedCallback);
db.open(openedCallback);
db.open(openedCallback);
db.open(openedCallback);
db.close(closedCallback);
db.close(closedCallback);
db.open(openedCallback);
db.close(closedCallback);
db.open(openedCallback);
db.close(closedCallback);
db.open(openedCallback);
beforeExit(function() {
console.log(opened, closed);
assert.equal(opened, 5, 'Database not opened');
assert.equal(closed, 4, 'Database not closed');
});
};
var assert = require('assert');
var fs = require('fs');
exports.deleteFile = function(name) {
try {
fs.unlinkSync(name);
} catch(err) {
if (err.errno !== process.ENOENT) {
throw err;
}
}
};
assert.fileDoesNotExist = function(name) {
try {
fs.statSync(name);
} catch(err) {
if (err.errno !== process.ENOENT) {
throw err;
}
}
};
assert.fileExists = function(name) {
try {
fs.statSync(name);
} catch(err) {
throw err;
}
};
\ No newline at end of file
...@@ -38,6 +38,7 @@ def build(bld): ...@@ -38,6 +38,7 @@ def build(bld):
obj.source = "src/sqlite3_bindings.cc" obj.source = "src/sqlite3_bindings.cc"
obj.source += " src/database.cc" obj.source += " src/database.cc"
obj.source += " src/statement.cc" obj.source += " src/statement.cc"
obj.source += " src/demo.cc"
obj.uselib = "SQLITE3 PROFILER MPOOL" obj.uselib = "SQLITE3 PROFILER MPOOL"
start_dir = bld.path.find_dir('lib') start_dir = bld.path.find_dir('lib')
# http://www.freehackers.org/~tnagy/wafbook/index.html#_installing_files # http://www.freehackers.org/~tnagy/wafbook/index.html#_installing_files
......
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