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