Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions 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 All @@ -138,7 +138,7 @@ Database.prototype.addListener = Database.prototype.on = function(type) {

Database.prototype.removeListener = function(type) {
var val = EventEmitter.prototype.removeListener.apply(this, arguments);
if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) {
if (supportedEvents.indexOf(type) >= 0) {
this.configure(type, false);
}
return val;
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": "https://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