Skip to content

Commit 51a6348

Browse files
committed
add support for pre-update hook
1 parent ef7c284 commit 51a6348

File tree

7 files changed

+225
-57
lines changed

7 files changed

+225
-57
lines changed

deps/common-sqlite.gypi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
'variables': {
3-
'sqlite_version%':'3090100',
3+
'sqlite_version%':'3140100',
44
"toolset%":'',
55
},
66
'target_defaults': {
2.36 MB
Binary file not shown.

deps/sqlite3.gyp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
'SQLITE_THREADSAFE=1',
8282
'SQLITE_ENABLE_FTS3',
8383
'SQLITE_ENABLE_JSON1',
84-
'SQLITE_ENABLE_RTREE'
84+
'SQLITE_ENABLE_RTREE',
85+
'SQLITE_ENABLE_PREUPDATE_HOOK'
8586
],
8687
},
8788
'cflags_cc': [
@@ -93,7 +94,8 @@
9394
'SQLITE_THREADSAFE=1',
9495
'SQLITE_ENABLE_FTS3',
9596
'SQLITE_ENABLE_JSON1',
96-
'SQLITE_ENABLE_RTREE'
97+
'SQLITE_ENABLE_RTREE',
98+
'SQLITE_ENABLE_PREUPDATE_HOOK'
9799
],
98100
'export_dependent_settings': [
99101
'action_before_build',

lib/sqlite3.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ Statement.prototype.map = function() {
126126

127127
var isVerbose = false;
128128

129-
var supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete' ];
129+
var supportedEvents = [ 'trace', 'profile', 'insert', 'update', 'delete', 'preupdate' ];
130130

131131
Database.prototype.addListener = Database.prototype.on = function(type) {
132132
var val = EventEmitter.prototype.addListener.apply(this, arguments);

src/database.cc

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,11 @@ NAN_METHOD(Database::Configure) {
335335
Baton* baton = new Baton(db, handle);
336336
db->Schedule(RegisterProfileCallback, baton);
337337
}
338+
else if(Nan::Equals(info[0], Nan::New("preupdate").ToLocalChecked()).FromJust()) {
339+
Local<Function> handle;
340+
Baton* baton = new Baton(db, handle);
341+
db->Schedule(RegisterPreUpdateCallback, baton);
342+
}
338343
else if (Nan::Equals(info[0], Nan::New("busyTimeout").ToLocalChecked()).FromJust()) {
339344
if (!info[1]->IsInt32()) {
340345
return Nan::ThrowTypeError("Value must be an integer");
@@ -460,6 +465,146 @@ void Database::ProfileCallback(Database *db, ProfileInfo* info) {
460465
delete info;
461466
}
462467

468+
void Database::RegisterPreUpdateCallback(Baton* baton) {
469+
assert(baton->db->open);
470+
assert(baton->db->_handle);
471+
Database* db = baton->db;
472+
473+
if (db->preupdate_event == NULL) {
474+
// Add it.
475+
db->preupdate_event = new AsyncPreUpdate(db, PreUpdateCallback);
476+
sqlite3_preupdate_hook(db->_handle, PreUpdateCallback, db);
477+
}
478+
else {
479+
// Remove it.
480+
sqlite3_preupdate_hook(db->_handle, NULL, NULL);
481+
db->preupdate_event->finish();
482+
db->preupdate_event = NULL;
483+
}
484+
485+
delete baton;
486+
}
487+
488+
static void addRowValue(Row *row, sqlite3_value *val) {
489+
int type = sqlite3_value_type(val);
490+
const char *name = "placeholder";
491+
492+
switch (type) {
493+
case SQLITE_INTEGER: {
494+
row->push_back(new Values::Integer(name, sqlite3_value_int(val)));
495+
} break;
496+
case SQLITE_FLOAT: {
497+
row->push_back(new Values::Float(name, sqlite3_value_double(val)));
498+
} break;
499+
case SQLITE_TEXT: {
500+
const char* text = (const char*)sqlite3_value_text(val);
501+
int length = sqlite3_value_bytes(val);
502+
row->push_back(new Values::Text(name, length, text));
503+
} break;
504+
case SQLITE_BLOB: {
505+
const void* blob = sqlite3_value_blob(val);
506+
int length = sqlite3_value_bytes(val);
507+
row->push_back(new Values::Blob(name, length, blob));
508+
} break;
509+
case SQLITE_NULL: {
510+
row->push_back(new Values::Null(name));
511+
} break;
512+
default:
513+
assert(false);
514+
}
515+
516+
}
517+
518+
Local<Value> rowToArray(Row *row) {
519+
Nan::EscapableHandleScope scope;
520+
521+
if(row == NULL) {
522+
return Nan::Null();
523+
}
524+
525+
Local<Array> arr(Nan::New<Array>(row->size()));
526+
Row::const_iterator it = row->begin();
527+
Row::const_iterator end = row->end();
528+
for (int i = 0; it < end; ++it, i++) {
529+
Values::Field* field = *it;
530+
Local<Value> value;
531+
532+
switch (field->type) {
533+
case SQLITE_INTEGER: {
534+
value = Nan::New<Number>(((Values::Integer*)field)->value);
535+
} break;
536+
case SQLITE_FLOAT: {
537+
value = Nan::New<Number>(((Values::Float*)field)->value);
538+
} break;
539+
case SQLITE_TEXT: {
540+
value = Nan::New<String>(((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()).ToLocalChecked();
541+
} break;
542+
case SQLITE_BLOB: {
543+
value = Nan::CopyBuffer(((Values::Blob*)field)->value, ((Values::Blob*)field)->length).ToLocalChecked();
544+
} break;
545+
case SQLITE_NULL: {
546+
value = Nan::Null();
547+
} break;
548+
}
549+
550+
Nan::Set(arr, i, value);
551+
552+
DELETE_FIELD(field);
553+
}
554+
555+
return scope.Escape(arr);
556+
}
557+
558+
void Database::PreUpdateCallback(void* db_, sqlite3 *handle, int type, const char* database,
559+
const char* table, sqlite3_int64 key1, sqlite3_int64 key2) {
560+
Database* db = static_cast<Database*>(db_);
561+
562+
// Note: This function is called in the thread pool.
563+
PreUpdateInfo* info = new PreUpdateInfo();
564+
info->type = type;
565+
info->database = std::string(database);
566+
info->table = std::string(table);
567+
568+
int count = sqlite3_preupdate_count(db->_handle);
569+
570+
Row *oldValues = new Row();
571+
Row *newValues = new Row();
572+
for(int i=0; i<count; i++) {
573+
sqlite3_value *old, *new_;
574+
sqlite3_preupdate_old(db->_handle, i, &old);
575+
sqlite3_preupdate_new(db->_handle, i, &new_);
576+
577+
addRowValue(oldValues, old);
578+
addRowValue(newValues, new_);
579+
}
580+
581+
info->oldValues = oldValues;
582+
info->newValues = newValues;
583+
info->oldRowId = key1;
584+
info->newRowId = key2;
585+
586+
static_cast<Database*>(db)->preupdate_event->send(info);
587+
}
588+
589+
void Database::PreUpdateCallback(Database *db, PreUpdateInfo* info) {
590+
Nan::HandleScope scope;
591+
592+
Local<Value> argv[] = {
593+
Nan::New("preupdate").ToLocalChecked(),
594+
Nan::New(sqlite_authorizer_string(info->type)).ToLocalChecked(),
595+
Nan::New(info->database.c_str()).ToLocalChecked(),
596+
Nan::New(info->table.c_str()).ToLocalChecked(),
597+
rowToArray(info->oldValues),
598+
rowToArray(info->newValues),
599+
Nan::New<Number>(info->oldRowId),
600+
Nan::New<Number>(info->newRowId)
601+
};
602+
EMIT_EVENT(db->handle(), 8, argv);
603+
delete info->oldValues;
604+
delete info->newValues;
605+
delete info;
606+
}
607+
463608
void Database::RegisterUpdateCallback(Baton* baton) {
464609
assert(baton->db->open);
465610
assert(baton->db->_handle);
@@ -686,4 +831,8 @@ void Database::RemoveCallbacks() {
686831
debug_profile->finish();
687832
debug_profile = NULL;
688833
}
834+
if (preupdate_event) {
835+
preupdate_event->finish();
836+
preupdate_event = NULL;
837+
}
689838
}

src/database.h

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,57 @@ using namespace v8;
1515

1616
namespace node_sqlite3 {
1717

18+
namespace Values {
19+
struct Field {
20+
inline Field(unsigned short _index, unsigned short _type = SQLITE_NULL) :
21+
type(_type), index(_index) {}
22+
inline Field(const char* _name, unsigned short _type = SQLITE_NULL) :
23+
type(_type), index(0), name(_name) {}
24+
25+
unsigned short type;
26+
unsigned short index;
27+
std::string name;
28+
};
29+
30+
struct Integer : Field {
31+
template <class T> inline Integer(T _name, int64_t val) :
32+
Field(_name, SQLITE_INTEGER), value(val) {}
33+
int64_t value;
34+
};
35+
36+
struct Float : Field {
37+
template <class T> inline Float(T _name, double val) :
38+
Field(_name, SQLITE_FLOAT), value(val) {}
39+
double value;
40+
};
41+
42+
struct Text : Field {
43+
template <class T> inline Text(T _name, size_t len, const char* val) :
44+
Field(_name, SQLITE_TEXT), value(val, len) {}
45+
std::string value;
46+
};
47+
48+
struct Blob : Field {
49+
template <class T> inline Blob(T _name, size_t len, const void* val) :
50+
Field(_name, SQLITE_BLOB), length(len) {
51+
value = (char*)malloc(len);
52+
memcpy(value, val, len);
53+
}
54+
inline ~Blob() {
55+
free(value);
56+
}
57+
int length;
58+
char* value;
59+
};
60+
61+
typedef Field Null;
62+
}
63+
64+
typedef std::vector<Values::Field*> Row;
65+
typedef std::vector<Row*> Rows;
66+
typedef Row Parameters;
67+
68+
1869
class Database;
1970

2071

@@ -90,12 +141,23 @@ class Database : public Nan::ObjectWrap {
90141
sqlite3_int64 rowid;
91142
};
92143

144+
struct PreUpdateInfo {
145+
int type;
146+
std::string database;
147+
std::string table;
148+
sqlite3_int64 oldRowId;
149+
sqlite3_int64 newRowId;
150+
Row* oldValues;
151+
Row* newValues;
152+
};
153+
93154
bool IsOpen() { return open; }
94155
bool IsLocked() { return locked; }
95156

96157
typedef Async<std::string, Database> AsyncTrace;
97158
typedef Async<ProfileInfo, Database> AsyncProfile;
98159
typedef Async<UpdateInfo, Database> AsyncUpdate;
160+
typedef Async<PreUpdateInfo, Database> AsyncPreUpdate;
99161

100162
friend class Statement;
101163

@@ -109,7 +171,8 @@ class Database : public Nan::ObjectWrap {
109171
serialize(false),
110172
debug_trace(NULL),
111173
debug_profile(NULL),
112-
update_event(NULL) {
174+
update_event(NULL),
175+
preupdate_event(NULL) {
113176
}
114177

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

234+
static void RegisterPreUpdateCallback(Baton* baton);
235+
static void PreUpdateCallback(void* db, sqlite3 *handle, int type, const char* database,
236+
const char* table, sqlite3_int64 key1, sqlite3_int64 key2);
237+
static void PreUpdateCallback(Database* db, PreUpdateInfo* info);
238+
171239
void RemoveCallbacks();
172240

173241
protected:
@@ -185,6 +253,7 @@ class Database : public Nan::ObjectWrap {
185253
AsyncTrace* debug_trace;
186254
AsyncProfile* debug_profile;
187255
AsyncUpdate* update_event;
256+
AsyncPreUpdate* preupdate_event;
188257
};
189258

190259
}

src/statement.h

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,58 +19,6 @@ using namespace node;
1919

2020
namespace node_sqlite3 {
2121

22-
namespace Values {
23-
struct Field {
24-
inline Field(unsigned short _index, unsigned short _type = SQLITE_NULL) :
25-
type(_type), index(_index) {}
26-
inline Field(const char* _name, unsigned short _type = SQLITE_NULL) :
27-
type(_type), index(0), name(_name) {}
28-
29-
unsigned short type;
30-
unsigned short index;
31-
std::string name;
32-
};
33-
34-
struct Integer : Field {
35-
template <class T> inline Integer(T _name, int64_t val) :
36-
Field(_name, SQLITE_INTEGER), value(val) {}
37-
int64_t value;
38-
};
39-
40-
struct Float : Field {
41-
template <class T> inline Float(T _name, double val) :
42-
Field(_name, SQLITE_FLOAT), value(val) {}
43-
double value;
44-
};
45-
46-
struct Text : Field {
47-
template <class T> inline Text(T _name, size_t len, const char* val) :
48-
Field(_name, SQLITE_TEXT), value(val, len) {}
49-
std::string value;
50-
};
51-
52-
struct Blob : Field {
53-
template <class T> inline Blob(T _name, size_t len, const void* val) :
54-
Field(_name, SQLITE_BLOB), length(len) {
55-
value = (char*)malloc(len);
56-
memcpy(value, val, len);
57-
}
58-
inline ~Blob() {
59-
free(value);
60-
}
61-
int length;
62-
char* value;
63-
};
64-
65-
typedef Field Null;
66-
}
67-
68-
typedef std::vector<Values::Field*> Row;
69-
typedef std::vector<Row*> Rows;
70-
typedef Row Parameters;
71-
72-
73-
7422
class Statement : public Nan::ObjectWrap {
7523
public:
7624
static Nan::Persistent<FunctionTemplate> constructor_template;

0 commit comments

Comments
 (0)