Skip to content

Commit

Permalink
wrote API for syncronous operations
Browse files Browse the repository at this point in the history
  • Loading branch information
Joshua Wise committed Sep 12, 2016
1 parent 68a5c28 commit 75b6f48
Show file tree
Hide file tree
Showing 51 changed files with 310 additions and 826 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ addons:
- gcc-4.9
- g++-4.9
before_install:
- export CC="gcc-4.9" CXX="g++-4.9"
- export CC="gcc-4.9" CXX="g++-4.9"
before_script:
- node-gyp rebuild --debug
12 changes: 8 additions & 4 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
---------- Version 1 ----------
write tests
should a new HandleScope be inside the .each() loop?

re-write benchmarks for sync

re-write readme for sync

re-write tests for sync

write tests
compare tests to node-sqlite3

---------- Version 2 ----------
use special objects to deal with 64bit integers

---------- Backlog ----------
Make sure that Promise.promisify(fn, {deoptimize: true}) does not reduce performance too much for situations with ~6 arguments
10 changes: 2 additions & 8 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,8 @@
"src/objects/database/database.cc",
"src/objects/statement/statement.cc",
"src/objects/transaction/transaction.cc",
"src/workers/database-workers/open.cc",
"src/workers/database-workers/close.cc",
"src/workers/database-workers/checkpoint.cc",
"src/workers/statement-workers/run.cc",
"src/workers/statement-workers/get.cc",
"src/workers/statement-workers/all.cc",
"src/workers/statement-workers/each.cc",
"src/workers/transaction-worker.cc",
"src/workers/open.cc",
"src/workers/close.cc",
"src/binder/binder.cc",
"src/multi-binder/multi-binder.cc",
"src/better_sqlite3.cc"
Expand Down
2 changes: 1 addition & 1 deletion deps/sqlite3.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
],
'defines': [
'_REENTRANT=1',
'SQLITE_THREADSAFE=1',
'SQLITE_THREADSAFE=2',
'SQLITE_ENABLE_FTS5',
'SQLITE_ENABLE_JSON1',
'SQLITE_ENABLE_RTREE',
Expand Down
2 changes: 1 addition & 1 deletion lib/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function Database(filenameGiven, options) {
.replace(/\?/g, '%3f')
.replace(/\/\/+/g, '/')
.replace(/#/g, '%23')
+ '?mode=memory';
+ '?mode=memory&cache=shared';
}

return new CPPDatabase(filename, filenameGiven, NullFactory);
Expand Down
9 changes: 1 addition & 8 deletions src/binder/bind-buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,7 @@
void Binder::BindBuffer(v8::Local<v8::Object> value, int index) {
if (!index) {index = NextAnonIndex();}

int status;
size_t length = node::Buffer::Length(value);
if (length > 1024) {
persistent->Set((uint32_t)index, value);
status = sqlite3_bind_blob(handle, index, (void*)node::Buffer::Data(value), length, SQLITE_STATIC);
} else {
status = sqlite3_bind_blob(handle, index, (void*)node::Buffer::Data(value), length, SQLITE_TRANSIENT);
}
int status = sqlite3_bind_blob(handle, index, (void*)node::Buffer::Data(value), node::Buffer::Length(value), bind_type);

SetBindingError(status);
}
8 changes: 1 addition & 7 deletions src/binder/bind-string.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@
void Binder::BindString(v8::Local<v8::String> value, int index) {
if (!index) {index = NextAnonIndex();}

int status;
v8::String::Value utf16(value);
if (utf16.length() > 1024) {
persistent->Set((uint32_t)index, value);
status = sqlite3_bind_text16(handle, index, *utf16, utf16.length() * sizeof (uint16_t), SQLITE_STATIC);
} else {
status = sqlite3_bind_text16(handle, index, *utf16, utf16.length() * sizeof (uint16_t), SQLITE_TRANSIENT);
}
int status = sqlite3_bind_text16(handle, index, *utf16, utf16.length() * sizeof (uint16_t), bind_type);

SetBindingError(status);
}
4 changes: 2 additions & 2 deletions src/binder/binder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
#include "bind-object.cc"
#include "bind.cc"

Binder::Binder(sqlite3_stmt* handle, v8::Local<v8::Object> persistent)
Binder::Binder(sqlite3_stmt* handle, sqlite3_destructor_type bind_type)
: handle(handle)
, param_count(sqlite3_bind_parameter_count(handle))
, anon_index(0)
, error(NULL)
, error_extra(NULL)
, error_full(NULL)
, persistent(persistent) {}
, bind_type(bind_type) {}

Binder::~Binder() {
delete[] error_extra;
Expand Down
4 changes: 2 additions & 2 deletions src/binder/binder.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Query;

class Binder {
public:
Binder(sqlite3_stmt*, v8::Local<v8::Object>);
Binder(sqlite3_stmt*, sqlite3_destructor_type);
~Binder();
virtual void Bind(Nan::NAN_METHOD_ARGS_TYPE, int, Query*);
const char* GetError();
Expand Down Expand Up @@ -36,7 +36,7 @@ class Binder {
char* error_extra;
const char* error_full;

v8::Local<v8::Object> persistent;
sqlite3_destructor_type bind_type;
};

#endif
4 changes: 2 additions & 2 deletions src/multi-binder/multi-binder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
#include "bind-object.cc"
#include "bind.cc"

MultiBinder::MultiBinder(sqlite3_stmt** handles, unsigned int handle_count, v8::Local<v8::Object> persistent)
: Binder(handles[0], persistent)
MultiBinder::MultiBinder(sqlite3_stmt** handles, unsigned int handle_count, sqlite3_destructor_type bind_type)
: Binder(handles[0], bind_type)
, handles(handles)
, handle_count(handle_count)
, handle_index(0)
Expand Down
2 changes: 1 addition & 1 deletion src/multi-binder/multi-binder.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Query;

class MultiBinder : public Binder {
public:
MultiBinder(sqlite3_stmt**, unsigned int, v8::Local<v8::Object>);
MultiBinder(sqlite3_stmt**, unsigned int, sqlite3_destructor_type);
void Bind(Nan::NAN_METHOD_ARGS_TYPE, int, Query*);

protected:
Expand Down
44 changes: 27 additions & 17 deletions src/objects/database/checkpoint.cc
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
// .checkpoint([boolean force], Function callback) -> this

NAN_METHOD(Database::Checkpoint) {
bool force;
v8::Local<v8::Function> func;
if (info.Length() >= 2) {
TRUTHINESS_OF_ARGUMENT(0, a);
REQUIRE_ARGUMENT_FUNCTION(1, b);
force = a;
func = b;
} else {
force = false;
REQUIRE_ARGUMENT_FUNCTION(0, a);
func = a;
}

TRUTHINESS_OF_ARGUMENT(0, force);
Database* db = Nan::ObjectWrap::Unwrap<Database>(info.This());
if (db->in_each) {
return Nan::ThrowTypeError("This database connection is busy executing a query.");
}
if (db->state != DB_READY) {
return Nan::ThrowError("The database connection is not open.");
}

if (db->workers++ == 0) {db->Ref();}
db->checkpoints += 1;
Nan::AsyncQueueWorker(new CheckpointWorker(db, force, new Nan::Callback(func)));
int total_frames;
int checkpointed_frames;
int status = sqlite3_wal_checkpoint_v2(
db->db_handle,
"main",
force ? SQLITE_CHECKPOINT_RESTART : SQLITE_CHECKPOINT_PASSIVE,
&total_frames,
&checkpointed_frames
);

info.GetReturnValue().Set(info.This());
if (status != SQLITE_OK) {
CONCAT2(message, "SQLite: ", sqlite3_errmsg(db->db_handle));
return Nan::ThrowError(message);
}

if (checkpointed_frames < 0 || total_frames < 0) {
info.GetReturnValue().Set(Nan::New<v8::Number>(0));
} else if (total_frames == 0) {
info.GetReturnValue().Set(Nan::New<v8::Number>(1));
} else {
info.GetReturnValue().Set(Nan::New<v8::Number>(
(double)checkpointed_frames / (double)total_frames
));
}
}
36 changes: 6 additions & 30 deletions src/objects/database/close.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,17 @@ NAN_METHOD(Database::Close) {
Database* db = Nan::ObjectWrap::Unwrap<Database>(info.This());

if (db->state != DB_DONE) {
if (db->workers++ == 0) {db->Ref();}

// Try to close as many query objects as possible. If some queries are
// busy, we'll have to wait for the last QueryWorker to actually close
// the database.
std::set<Statement*, Statement::Compare>::iterator stmts_it = db->stmts.begin();
std::set<Statement*, Statement::Compare>::iterator stmts_end = db->stmts.end();
while (stmts_it != stmts_end) {
if ((*stmts_it)->CloseIfPossible()) {
stmts_it = db->stmts.erase(stmts_it);
} else {
++stmts_it;
}
if (db->in_each) {
return Nan::ThrowTypeError("You cannot close a database while it is executing a query.");
}
std::set<Transaction*, Transaction::Compare>::iterator transs_it = db->transs.begin();
std::set<Transaction*, Transaction::Compare>::iterator transs_end = db->transs.end();
while (transs_it != transs_end) {
if ((*transs_it)->CloseIfPossible()) {
transs_it = db->transs.erase(transs_it);
} else {
++transs_it;
}
}
db->MaybeClose();

// This must be after the MaybeClose() attempt, so the CloseWorker
if (db->workers++ == 0) {db->Ref();}
Nan::AsyncQueueWorker(new CloseWorker(db, db->state == DB_CONNECTING));

// This must be after the CloseWorker is created, so the CloseWorker
// can detect if the database is still connecting.
db->state = DB_DONE;
}

info.GetReturnValue().Set(info.This());
}

void Database::MaybeClose() {
if (stmts.empty() && transs.empty() && checkpoints == 0) {
Nan::AsyncQueueWorker(new CloseWorker(this, state == DB_CONNECTING));
}
}
10 changes: 5 additions & 5 deletions src/objects/database/create-statement.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ NAN_METHOD(Database::CreateStatement) {
REQUIRE_ARGUMENT_STRING(0, source);

Database* db = Nan::ObjectWrap::Unwrap<Database>(info.This());
if (db->in_each) {
return Nan::ThrowTypeError("This database connection is busy executing a query.");
}
if (db->state != DB_READY) {
return Nan::ThrowError("The database connection is not open.");
}
Expand All @@ -20,20 +23,16 @@ NAN_METHOD(Database::CreateStatement) {

// Digest the source string.
v8::String::Value utf16(source);
int source_bytes_plus1 = utf16.length() * sizeof (uint16_t) + 1;

// Builds actual sqlite3_stmt handle.
const void* tail;
LOCK_DB(db->db_handle);
int status = sqlite3_prepare16(db->db_handle, *utf16, source_bytes_plus1, &stmt->st_handle, &tail);
int status = sqlite3_prepare16(db->db_handle, *utf16, utf16.length() * sizeof (uint16_t) + 1, &stmt->st_handle, &tail);

// Validates the newly created statement.
if (status != SQLITE_OK) {
CONCAT3(message, "Failed to construct SQL statement (", sqlite3_errmsg(db->db_handle), ").");
UNLOCK_DB(db->db_handle);
return Nan::ThrowError(message);
}
UNLOCK_DB(db->db_handle);
if (stmt->st_handle == NULL) {
return Nan::ThrowTypeError("The supplied SQL string contains no statements.");
}
Expand All @@ -51,6 +50,7 @@ NAN_METHOD(Database::CreateStatement) {
}
}
Nan::ForceSet(statement, NEW_INTERNAL_STRING_FAST("source"), source, FROZEN);
statement->SetHiddenValue(Nan::New("_").ToLocalChecked(), info.This());

// Pushes onto stmts set.
stmt->id = NEXT_STATEMENT_ID++;
Expand Down
7 changes: 4 additions & 3 deletions src/objects/database/create-transaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ NAN_METHOD(Database::CreateTransaction) {
REQUIRE_ARGUMENT_ARRAY(0, sources);

Database* db = Nan::ObjectWrap::Unwrap<Database>(info.This());
if (db->in_each) {
return Nan::ThrowTypeError("This database connection is busy executing a query.");
}
if (db->state != DB_READY) {
return Nan::ThrowError("The database connection is not open.");
}
Expand Down Expand Up @@ -59,16 +62,13 @@ NAN_METHOD(Database::CreateTransaction) {
v8::String::Value utf16(source);
const void* tail;

LOCK_DB(db->db_handle);
int status = sqlite3_prepare16(db->db_handle, *utf16, utf16.length() * sizeof (uint16_t) + 1, &(trans->handles[i]), &tail);

// Validates the newly created statement.
if (status != SQLITE_OK) {
CONCAT3(message, "Failed to construct SQL statement (", sqlite3_errmsg(db->db_handle), ").");
UNLOCK_DB(db->db_handle);
return Nan::ThrowError(message);
}
UNLOCK_DB(db->db_handle);
if (trans->handles[i] == NULL) {
return Nan::ThrowTypeError("One of the supplied SQL strings contains no statements.");
}
Expand All @@ -80,6 +80,7 @@ NAN_METHOD(Database::CreateTransaction) {
}
}
Nan::ForceSet(transaction, NEW_INTERNAL_STRING_FAST("source"), joinedSource, FROZEN);
transaction->SetHiddenValue(Nan::New("_").ToLocalChecked(), info.This());

// Pushes onto transs set.
trans->id = NEXT_TRANSACTION_ID++;
Expand Down
19 changes: 11 additions & 8 deletions src/objects/database/database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
#include "database.h"
#include "../statement/statement.h"
#include "../transaction/transaction.h"
#include "../../workers/database-workers/open.h"
#include "../../workers/database-workers/close.h"
#include "../../workers/database-workers/checkpoint.h"
#include "../../workers/open.h"
#include "../../workers/close.h"
#include "../../util/macros.h"
#include "../../util/data.h"
#include "../../util/list.h"
Expand All @@ -30,7 +29,7 @@ Database::Database() : Nan::ObjectWrap(),
t_handles(NULL),
state(DB_CONNECTING),
workers(0),
checkpoints(0) {}
in_each(false) {}
Database::~Database() {
state = DB_DONE;

Expand All @@ -39,10 +38,7 @@ Database::~Database() {
// first, so it needs to tell all of its statements "hey, I don't exist
// anymore". By the same nature, statements must remove themselves from a
// database's sets when they are garbage collected first.
for (Statement* stmt : stmts) {stmt->CloseHandles();}
for (Transaction* trans : transs) {trans->CloseHandles();}
stmts.clear();
transs.clear();
CloseChildHandles();

CloseHandles();
}
Expand Down Expand Up @@ -72,3 +68,10 @@ int Database::CloseHandles() {
db_handle = NULL;
return status;
}

void Database::CloseChildHandles() {
for (Statement* stmt : stmts) {stmt->CloseHandles();}
for (Transaction* trans : transs) {trans->CloseHandles();}
stmts.clear();
transs.clear();
}
Loading

0 comments on commit 75b6f48

Please sign in to comment.