Skip to content

Commit

Permalink
Merge 4a40a72 into cee778d
Browse files Browse the repository at this point in the history
  • Loading branch information
jlongster committed Oct 2, 2016
2 parents cee778d + 4a40a72 commit 57ae1af
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 58 deletions.
2 changes: 1 addition & 1 deletion deps/common-sqlite.gypi
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
'variables': {
'sqlite_version%':'3090100',
'sqlite_version%':'3140100',
"toolset%":'',
},
'target_defaults': {
Expand Down
Binary file added deps/sqlite-autoconf-3140100.tar.gz
Binary file not shown.
6 changes: 4 additions & 2 deletions deps/sqlite3.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@
'SQLITE_THREADSAFE=1',
'SQLITE_ENABLE_FTS3',
'SQLITE_ENABLE_JSON1',
'SQLITE_ENABLE_RTREE'
'SQLITE_ENABLE_RTREE',
'SQLITE_ENABLE_PREUPDATE_HOOK'
],
},
'cflags_cc': [
Expand All @@ -93,7 +94,8 @@
'SQLITE_THREADSAFE=1',
'SQLITE_ENABLE_FTS3',
'SQLITE_ENABLE_JSON1',
'SQLITE_ENABLE_RTREE'
'SQLITE_ENABLE_RTREE',
'SQLITE_ENABLE_PREUPDATE_HOOK'
],
'export_dependent_settings': [
'action_before_build',
Expand Down
2 changes: 1 addition & 1 deletion lib/sqlite3.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ Statement.prototype.map = function() {

var isVerbose = false;

var supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
var supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete', 'preupdate' ];

Database.prototype.addListener = Database.prototype.on = function(type) {
var val = EventEmitter.prototype.addListener.apply(this, arguments);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "sqlite3",
"description": "Asynchronous, non-blocking SQLite3 bindings",
"version": "3.1.4",
"version": "3.1.4-custom-fork",
"homepage": "http://github.com/mapbox/node-sqlite3",
"author": {
"name": "MapBox",
Expand Down
154 changes: 154 additions & 0 deletions src/database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,11 @@ NAN_METHOD(Database::Configure) {
Baton* baton = new Baton(db, handle);
db->Schedule(RegisterProfileCallback, baton);
}
else if(Nan::Equals(info[0], Nan::New("preupdate").ToLocalChecked()).FromJust()) {
Local<Function> handle;
Baton* baton = new Baton(db, handle);
db->Schedule(RegisterPreUpdateCallback, baton);
}
else if (Nan::Equals(info[0], Nan::New("busyTimeout").ToLocalChecked()).FromJust()) {
if (!info[1]->IsInt32()) {
return Nan::ThrowTypeError("Value must be an integer");
Expand Down Expand Up @@ -460,6 +465,151 @@ void Database::ProfileCallback(Database *db, ProfileInfo* info) {
delete info;
}

static void addRowValue(Row *row, sqlite3_value *val) {
int type = sqlite3_value_type(val);
const char *name = "placeholder";

switch (type) {
case SQLITE_INTEGER: {
row->push_back(new Values::Integer(name, sqlite3_value_int(val)));
} break;
case SQLITE_FLOAT: {
row->push_back(new Values::Float(name, sqlite3_value_double(val)));
} break;
case SQLITE_TEXT: {
const char* text = (const char*)sqlite3_value_text(val);
int length = sqlite3_value_bytes(val);
row->push_back(new Values::Text(name, length, text));
} break;
case SQLITE_BLOB: {
const void* blob = sqlite3_value_blob(val);
int length = sqlite3_value_bytes(val);
row->push_back(new Values::Blob(name, length, blob));
} break;
case SQLITE_NULL: {
row->push_back(new Values::Null(name));
} break;
default:
assert(false);
}

}

Local<Value> rowToArray(Row *row) {
Nan::EscapableHandleScope scope;

if(row == NULL) {
return Nan::Null();
}

Local<Array> arr(Nan::New<Array>(row->size()));
Row::const_iterator it = row->begin();
Row::const_iterator end = row->end();
for (int i = 0; it < end; ++it, i++) {
Values::Field* field = *it;
Local<Value> value;

switch (field->type) {
case SQLITE_INTEGER: {
value = Nan::New<Number>(((Values::Integer*)field)->value);
} break;
case SQLITE_FLOAT: {
value = Nan::New<Number>(((Values::Float*)field)->value);
} break;
case SQLITE_TEXT: {
value = Nan::New<String>(((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()).ToLocalChecked();
} break;
case SQLITE_BLOB: {
value = Nan::CopyBuffer(((Values::Blob*)field)->value, ((Values::Blob*)field)->length).ToLocalChecked();
} break;
case SQLITE_NULL: {
value = Nan::Null();
} break;
}

Nan::Set(arr, i, value);

DELETE_FIELD(field);
}

return scope.Escape(arr);
}

void Database::RegisterPreUpdateCallback(Baton* baton) {
assert(baton->db->open);
assert(baton->db->_handle);
Database* db = baton->db;

if (db->preupdate_event == NULL) {
// Add it.
db->preupdate_event = new AsyncPreUpdate(db, PreUpdateCallback);
sqlite3_preupdate_hook(db->_handle, PreUpdateCallback, db);
}
else {
// Remove it.
sqlite3_preupdate_hook(db->_handle, NULL, NULL);
db->preupdate_event->finish();
db->preupdate_event = NULL;
}

delete baton;
}

void Database::PreUpdateCallback(void* db_, sqlite3 *handle, int type, const char* database,
const char* table, sqlite3_int64 key1, sqlite3_int64 key2) {
Database* db = static_cast<Database*>(db_);

// Note: This function is called in the thread pool.
PreUpdateInfo* info = new PreUpdateInfo();
info->type = type;
info->database = std::string(database);
info->table = std::string(table);

int count = sqlite3_preupdate_count(db->_handle);

Row *oldValues = (type != SQLITE_INSERT) ? new Row() : NULL;
Row *newValues = (type != SQLITE_DELETE) ? new Row() : NULL;
for(int i=0; i<count; i++) {
if(type != SQLITE_INSERT) {
sqlite3_value *old;
sqlite3_preupdate_old(db->_handle, i, &old);
addRowValue(oldValues, old);
}

if(type != SQLITE_DELETE) {
sqlite3_value *new_;
sqlite3_preupdate_new(db->_handle, i, &new_);
addRowValue(newValues, new_);
}
}

info->oldValues = oldValues;
info->newValues = newValues;
info->oldRowId = key1;
info->newRowId = key2;

static_cast<Database*>(db)->preupdate_event->send(info);
}

void Database::PreUpdateCallback(Database *db, PreUpdateInfo* info) {
Nan::HandleScope scope;

Local<Value> argv[] = {
Nan::New("preupdate").ToLocalChecked(),
Nan::New(sqlite_authorizer_string(info->type)).ToLocalChecked(),
Nan::New(info->database.c_str()).ToLocalChecked(),
Nan::New(info->table.c_str()).ToLocalChecked(),
rowToArray(info->oldValues),
rowToArray(info->newValues),
Nan::New<Number>(info->oldRowId),
Nan::New<Number>(info->newRowId)
};
EMIT_EVENT(db->handle(), 8, argv);
delete info->oldValues;
delete info->newValues;
delete info;
}

void Database::RegisterUpdateCallback(Baton* baton) {
assert(baton->db->open);
assert(baton->db->_handle);
Expand Down Expand Up @@ -686,4 +836,8 @@ void Database::RemoveCallbacks() {
debug_profile->finish();
debug_profile = NULL;
}
if (preupdate_event) {
preupdate_event->finish();
preupdate_event = NULL;
}
}
71 changes: 70 additions & 1 deletion src/database.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,57 @@ using namespace v8;

namespace node_sqlite3 {

namespace Values {
struct Field {
inline Field(unsigned short _index, unsigned short _type = SQLITE_NULL) :
type(_type), index(_index) {}
inline Field(const char* _name, unsigned short _type = SQLITE_NULL) :
type(_type), index(0), name(_name) {}

unsigned short type;
unsigned short index;
std::string name;
};

struct Integer : Field {
template <class T> inline Integer(T _name, int64_t val) :
Field(_name, SQLITE_INTEGER), value(val) {}
int64_t value;
};

struct Float : Field {
template <class T> inline Float(T _name, double val) :
Field(_name, SQLITE_FLOAT), value(val) {}
double value;
};

struct Text : Field {
template <class T> inline Text(T _name, size_t len, const char* val) :
Field(_name, SQLITE_TEXT), value(val, len) {}
std::string value;
};

struct Blob : Field {
template <class T> inline Blob(T _name, size_t len, const void* val) :
Field(_name, SQLITE_BLOB), length(len) {
value = (char*)malloc(len);
memcpy(value, val, len);
}
inline ~Blob() {
free(value);
}
int length;
char* value;
};

typedef Field Null;
}

typedef std::vector<Values::Field*> Row;
typedef std::vector<Row*> Rows;
typedef Row Parameters;


class Database;


Expand Down Expand Up @@ -90,12 +141,23 @@ class Database : public Nan::ObjectWrap {
sqlite3_int64 rowid;
};

struct PreUpdateInfo {
int type;
std::string database;
std::string table;
sqlite3_int64 oldRowId;
sqlite3_int64 newRowId;
Row* oldValues;
Row* newValues;
};

bool IsOpen() { return open; }
bool IsLocked() { return locked; }

typedef Async<std::string, Database> AsyncTrace;
typedef Async<ProfileInfo, Database> AsyncProfile;
typedef Async<UpdateInfo, Database> AsyncUpdate;
typedef Async<PreUpdateInfo, Database> AsyncPreUpdate;

friend class Statement;

Expand All @@ -109,7 +171,8 @@ class Database : public Nan::ObjectWrap {
serialize(false),
debug_trace(NULL),
debug_profile(NULL),
update_event(NULL) {
update_event(NULL),
preupdate_event(NULL) {
}

~Database() {
Expand Down Expand Up @@ -168,6 +231,11 @@ class Database : public Nan::ObjectWrap {
static void UpdateCallback(void* db, int type, const char* database, const char* table, sqlite3_int64 rowid);
static void UpdateCallback(Database* db, UpdateInfo* info);

static void RegisterPreUpdateCallback(Baton* baton);
static void PreUpdateCallback(void* db, sqlite3 *handle, int type, const char* database,
const char* table, sqlite3_int64 key1, sqlite3_int64 key2);
static void PreUpdateCallback(Database* db, PreUpdateInfo* info);

void RemoveCallbacks();

protected:
Expand All @@ -185,6 +253,7 @@ class Database : public Nan::ObjectWrap {
AsyncTrace* debug_trace;
AsyncProfile* debug_profile;
AsyncUpdate* update_event;
AsyncPreUpdate* preupdate_event;
};

}
Expand Down
52 changes: 0 additions & 52 deletions src/statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,58 +19,6 @@ using namespace node;

namespace node_sqlite3 {

namespace Values {
struct Field {
inline Field(unsigned short _index, unsigned short _type = SQLITE_NULL) :
type(_type), index(_index) {}
inline Field(const char* _name, unsigned short _type = SQLITE_NULL) :
type(_type), index(0), name(_name) {}

unsigned short type;
unsigned short index;
std::string name;
};

struct Integer : Field {
template <class T> inline Integer(T _name, int64_t val) :
Field(_name, SQLITE_INTEGER), value(val) {}
int64_t value;
};

struct Float : Field {
template <class T> inline Float(T _name, double val) :
Field(_name, SQLITE_FLOAT), value(val) {}
double value;
};

struct Text : Field {
template <class T> inline Text(T _name, size_t len, const char* val) :
Field(_name, SQLITE_TEXT), value(val, len) {}
std::string value;
};

struct Blob : Field {
template <class T> inline Blob(T _name, size_t len, const void* val) :
Field(_name, SQLITE_BLOB), length(len) {
value = (char*)malloc(len);
memcpy(value, val, len);
}
inline ~Blob() {
free(value);
}
int length;
char* value;
};

typedef Field Null;
}

typedef std::vector<Values::Field*> Row;
typedef std::vector<Row*> Rows;
typedef Row Parameters;



class Statement : public Nan::ObjectWrap {
public:
static Nan::Persistent<FunctionTemplate> constructor_template;
Expand Down

0 comments on commit 57ae1af

Please sign in to comment.