Skip to content

Commit ed16f6b

Browse files
committed
add support for preupdate hook
1 parent ef7c284 commit ed16f6b

File tree

7 files changed

+230
-57
lines changed

7 files changed

+230
-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: 154 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,151 @@ void Database::ProfileCallback(Database *db, ProfileInfo* info) {
460465
delete info;
461466
}
462467

468+
static void addRowValue(Row *row, sqlite3_value *val) {
469+
int type = sqlite3_value_type(val);
470+
const char *name = "placeholder";
471+
472+
switch (type) {
473+
case SQLITE_INTEGER: {
474+
row->push_back(new Values::Integer(name, sqlite3_value_int(val)));
475+
} break;
476+
case SQLITE_FLOAT: {
477+
row->push_back(new Values::Float(name, sqlite3_value_double(val)));
478+
} break;
479+
case SQLITE_TEXT: {
480+
const char* text = (const char*)sqlite3_value_text(val);
481+
int length = sqlite3_value_bytes(val);
482+
row->push_back(new Values::Text(name, length, text));
483+
} break;
484+
case SQLITE_BLOB: {
485+
const void* blob = sqlite3_value_blob(val);
486+
int length = sqlite3_value_bytes(val);
487+
row->push_back(new Values::Blob(name, length, blob));
488+
} break;
489+
case SQLITE_NULL: {
490+
row->push_back(new Values::Null(name));
491+
} break;
492+
default:
493+
assert(false);
494+
}
495+
496+
}
497+
498+
Local<Value> rowToArray(Row *row) {
499+
Nan::EscapableHandleScope scope;
500+
501+
if(row == NULL) {
502+
return Nan::Null();
503+
}
504+
505+
Local<Array> arr(Nan::New<Array>(row->size()));
506+
Row::const_iterator it = row->begin();
507+
Row::const_iterator end = row->end();
508+
for (int i = 0; it < end; ++it, i++) {
509+
Values::Field* field = *it;
510+
Local<Value> value;
511+
512+
switch (field->type) {
513+
case SQLITE_INTEGER: {
514+
value = Nan::New<Number>(((Values::Integer*)field)->value);
515+
} break;
516+
case SQLITE_FLOAT: {
517+
value = Nan::New<Number>(((Values::Float*)field)->value);
518+
} break;
519+
case SQLITE_TEXT: {
520+
value = Nan::New<String>(((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()).ToLocalChecked();
521+
} break;
522+
case SQLITE_BLOB: {
523+
value = Nan::CopyBuffer(((Values::Blob*)field)->value, ((Values::Blob*)field)->length).ToLocalChecked();
524+
} break;
525+
case SQLITE_NULL: {
526+
value = Nan::Null();
527+
} break;
528+
}
529+
530+
Nan::Set(arr, i, value);
531+
532+
DELETE_FIELD(field);
533+
}
534+
535+
return scope.Escape(arr);
536+
}
537+
538+
void Database::RegisterPreUpdateCallback(Baton* baton) {
539+
assert(baton->db->open);
540+
assert(baton->db->_handle);
541+
Database* db = baton->db;
542+
543+
if (db->preupdate_event == NULL) {
544+
// Add it.
545+
db->preupdate_event = new AsyncPreUpdate(db, PreUpdateCallback);
546+
sqlite3_preupdate_hook(db->_handle, PreUpdateCallback, db);
547+
}
548+
else {
549+
// Remove it.
550+
sqlite3_preupdate_hook(db->_handle, NULL, NULL);
551+
db->preupdate_event->finish();
552+
db->preupdate_event = NULL;
553+
}
554+
555+
delete baton;
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 = (type != SQLITE_INSERT) ? new Row() : NULL;
571+
Row *newValues = (type != SQLITE_DELETE) ? new Row() : NULL;
572+
for(int i=0; i<count; i++) {
573+
if(type != SQLITE_INSERT) {
574+
sqlite3_value *old;
575+
sqlite3_preupdate_old(db->_handle, i, &old);
576+
addRowValue(oldValues, old);
577+
}
578+
579+
if(type != SQLITE_DELETE) {
580+
sqlite3_value *new_;
581+
sqlite3_preupdate_new(db->_handle, i, &new_);
582+
addRowValue(newValues, new_);
583+
}
584+
}
585+
586+
info->oldValues = oldValues;
587+
info->newValues = newValues;
588+
info->oldRowId = key1;
589+
info->newRowId = key2;
590+
591+
static_cast<Database*>(db)->preupdate_event->send(info);
592+
}
593+
594+
void Database::PreUpdateCallback(Database *db, PreUpdateInfo* info) {
595+
Nan::HandleScope scope;
596+
597+
Local<Value> argv[] = {
598+
Nan::New("preupdate").ToLocalChecked(),
599+
Nan::New(sqlite_authorizer_string(info->type)).ToLocalChecked(),
600+
Nan::New(info->database.c_str()).ToLocalChecked(),
601+
Nan::New(info->table.c_str()).ToLocalChecked(),
602+
rowToArray(info->oldValues),
603+
rowToArray(info->newValues),
604+
Nan::New<Number>(info->oldRowId),
605+
Nan::New<Number>(info->newRowId)
606+
};
607+
EMIT_EVENT(db->handle(), 8, argv);
608+
delete info->oldValues;
609+
delete info->newValues;
610+
delete info;
611+
}
612+
463613
void Database::RegisterUpdateCallback(Baton* baton) {
464614
assert(baton->db->open);
465615
assert(baton->db->_handle);
@@ -686,4 +836,8 @@ void Database::RemoveCallbacks() {
686836
debug_profile->finish();
687837
debug_profile = NULL;
688838
}
839+
if (preupdate_event) {
840+
preupdate_event->finish();
841+
preupdate_event = NULL;
842+
}
689843
}

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)