@@ -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,142 @@ 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+ Local<Array> arr (Nan::New<Array>(row->size ()));
522+ Row::const_iterator it = row->begin ();
523+ Row::const_iterator end = row->end ();
524+ for (int i = 0 ; it < end; ++it, i++) {
525+ Values::Field* field = *it;
526+ Local<Value> value;
527+
528+ switch (field->type ) {
529+ case SQLITE_INTEGER: {
530+ value = Nan::New<Number>(((Values::Integer*)field)->value );
531+ } break ;
532+ case SQLITE_FLOAT: {
533+ value = Nan::New<Number>(((Values::Float*)field)->value );
534+ } break ;
535+ case SQLITE_TEXT: {
536+ value = Nan::New<String>(((Values::Text*)field)->value .c_str (), ((Values::Text*)field)->value .size ()).ToLocalChecked ();
537+ } break ;
538+ case SQLITE_BLOB: {
539+ value = Nan::CopyBuffer (((Values::Blob*)field)->value , ((Values::Blob*)field)->length ).ToLocalChecked ();
540+ } break ;
541+ case SQLITE_NULL: {
542+ value = Nan::Null ();
543+ } break ;
544+ }
545+
546+ Nan::Set (arr, i, value);
547+
548+ DELETE_FIELD (field);
549+ }
550+
551+ return scope.Escape (arr);
552+ }
553+
554+ void Database::PreUpdateCallback (void * db_, sqlite3 *handle, int type, const char * database,
555+ const char * table, sqlite3_int64 key1, sqlite3_int64 key2) {
556+ Database* db = static_cast <Database*>(db_);
557+
558+ // Note: This function is called in the thread pool.
559+ PreUpdateInfo* info = new PreUpdateInfo ();
560+ info->type = type;
561+ info->database = std::string (database);
562+ info->table = std::string (table);
563+
564+ int count = sqlite3_preupdate_count (db->_handle );
565+
566+ Row *oldValues = new Row ();
567+ Row *newValues = new Row ();
568+ for (int i=0 ; i<count; i++) {
569+ sqlite3_value *old, *new_;
570+ sqlite3_preupdate_old (db->_handle , i, &old);
571+ sqlite3_preupdate_new (db->_handle , i, &new_);
572+
573+ addRowValue (oldValues, old);
574+ addRowValue (newValues, new_);
575+ }
576+
577+ info->oldValues = oldValues;
578+ info->newValues = newValues;
579+ info->oldRowId = key1;
580+ info->newRowId = key2;
581+
582+ static_cast <Database*>(db)->preupdate_event ->send (info);
583+ }
584+
585+ void Database::PreUpdateCallback (Database *db, PreUpdateInfo* info) {
586+ Nan::HandleScope scope;
587+
588+ Local<Value> argv[] = {
589+ Nan::New (" preupdate" ).ToLocalChecked (),
590+ Nan::New (sqlite_authorizer_string (info->type )).ToLocalChecked (),
591+ Nan::New (info->database .c_str ()).ToLocalChecked (),
592+ Nan::New (info->table .c_str ()).ToLocalChecked (),
593+ rowToArray (info->oldValues ),
594+ rowToArray (info->newValues ),
595+ Nan::New<Number>(info->oldRowId ),
596+ Nan::New<Number>(info->newRowId )
597+ };
598+ EMIT_EVENT (db->handle (), 8 , argv);
599+ delete info->oldValues ;
600+ delete info->newValues ;
601+ delete info;
602+ }
603+
463604void Database::RegisterUpdateCallback (Baton* baton) {
464605 assert (baton->db ->open );
465606 assert (baton->db ->_handle );
@@ -686,4 +827,8 @@ void Database::RemoveCallbacks() {
686827 debug_profile->finish ();
687828 debug_profile = NULL ;
688829 }
830+ if (preupdate_event) {
831+ preupdate_event->finish ();
832+ preupdate_event = NULL ;
833+ }
689834}
0 commit comments