@@ -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+
463613void 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}
0 commit comments