@@ -71,6 +71,10 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() {
7171 var SQLITE_BLOB = 4 ;
7272 // var - Encodings, used for registering functions.
7373 var SQLITE_UTF8 = 1 ;
74+ // var - Authorizer Action Codes used to identify change types in updateHook
75+ var SQLITE_INSERT = 18 ;
76+ var SQLITE_UPDATE = 23 ;
77+ var SQLITE_DELETE = 9 ;
7478 // var - cwrap function
7579 var sqlite3_open = cwrap ( "sqlite3_open" , "number" , [ "string" , "number" ] ) ;
7680 var sqlite3_close_v2 = cwrap ( "sqlite3_close_v2" , "number" , [ "number" ] ) ;
@@ -239,6 +243,12 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() {
239243 [ "number" ]
240244 ) ;
241245
246+ var sqlite3_update_hook = cwrap (
247+ "sqlite3_update_hook" ,
248+ "number" ,
249+ [ "number" , "number" , "number" ]
250+ ) ;
251+
242252 /**
243253 * @classdesc
244254 * Represents a prepared statement.
@@ -1114,6 +1124,12 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() {
11141124 } ) ;
11151125 Object . values ( this . functions ) . forEach ( removeFunction ) ;
11161126 this . functions = { } ;
1127+
1128+ if ( this . updateHookFunctionPtr ) {
1129+ removeFunction ( this . updateHookFunctionPtr ) ;
1130+ this . updateHookFunctionPtr = undefined ;
1131+ }
1132+
11171133 this . handleError ( sqlite3_close_v2 ( this . db ) ) ;
11181134 FS . unlink ( "/" + this . filename ) ;
11191135 this . db = null ;
@@ -1383,6 +1399,87 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() {
13831399 return this ;
13841400 } ;
13851401
1402+ /** Registers the update hook with SQLite
1403+ @param {function(operation, database, table, rowId) | null } callback
1404+ executed whenever a row in any rowid table is changed
1405+
1406+ For each changed row, the callback is called once with the change
1407+ ('insert', 'update' or 'delete'), the database name and table name
1408+ where the change happened and the rowid of the row that has been
1409+ changed.
1410+
1411+ rowid is cast to a plain number, if it exceeds Number.MAX_SAFE_INTEGER
1412+ an error will be thrown.
1413+
1414+ The callback MUST NOT modify the database in any way.
1415+
1416+ Only a single callback can be registered. Unregister the callback by
1417+ passing null.
1418+
1419+ Not called for some updates like ON REPLACE CONFLICT and TRUNCATE (a
1420+ DELETE FROM without a WHERE clause).
1421+
1422+ See sqlite docs on sqlite3_update_hook for more details.
1423+ */
1424+ Database . prototype [ "updateHook" ] = function updateHook ( callback ) {
1425+ if ( this . updateHookFunctionPtr ) {
1426+ // unregister and cleanup a previously registered update hook
1427+ sqlite3_update_hook ( this . db , 0 , 0 ) ;
1428+ removeFunction ( this . updateHookFunctionPtr ) ;
1429+ this . updateHookFunctionPtr = undefined ;
1430+ }
1431+
1432+ if ( ! callback ) {
1433+ // no new callback to register
1434+ return ;
1435+ }
1436+
1437+ // void(*)(void *,int ,char const *,char const *,sqlite3_int64)
1438+ function wrappedCallback (
1439+ ignored ,
1440+ operationCode ,
1441+ databaseNamePtr ,
1442+ tableNamePtr ,
1443+ rowIdBigInt
1444+ ) {
1445+ var operation ;
1446+
1447+ switch ( operationCode ) {
1448+ case SQLITE_INSERT :
1449+ operation = "insert" ;
1450+ break ;
1451+ case SQLITE_UPDATE :
1452+ operation = "update" ;
1453+ break ;
1454+ case SQLITE_DELETE :
1455+ operation = "delete" ;
1456+ break ;
1457+ default :
1458+ throw "unknown operationCode in updateHook callback: "
1459+ + operationCode ;
1460+ }
1461+
1462+ var databaseName = UTF8ToString ( databaseNamePtr ) ;
1463+ var tableName = UTF8ToString ( tableNamePtr ) ;
1464+
1465+ if ( rowIdBigInt > Number . MAX_SAFE_INTEGER ) {
1466+ throw "rowId too big to fit inside a Number" ;
1467+ }
1468+
1469+ var rowId = Number ( rowIdBigInt ) ;
1470+
1471+ callback ( operation , databaseName , tableName , rowId ) ;
1472+ }
1473+
1474+ this . updateHookFunctionPtr = addFunction ( wrappedCallback , "viiiij" ) ;
1475+
1476+ sqlite3_update_hook (
1477+ this . db ,
1478+ this . updateHookFunctionPtr ,
1479+ 0 // passed as the first arg to wrappedCallback
1480+ ) ;
1481+ } ;
1482+
13861483 // export Database to Module
13871484 Module . Database = Database ;
13881485} ;
0 commit comments