@@ -1057,24 +1057,23 @@ END;
10571057 });
10581058
10591059 group ('raw tables' , () {
1060+ const rawUsersTable = {
1061+ 'name' : 'users' ,
1062+ 'put' : {
1063+ 'sql' : 'INSERT OR REPLACE INTO users (id, name) VALUES (?, ?);' ,
1064+ 'params' : [
1065+ 'Id' ,
1066+ {'Column' : 'name' }
1067+ ],
1068+ },
1069+ 'delete' : {
1070+ 'sql' : 'DELETE FROM users WHERE id = ?' ,
1071+ 'params' : ['Id' ],
1072+ },
1073+ 'clear' : 'DELETE FROM users;' ,
1074+ };
10601075 const schema = {
1061- 'raw_tables' : [
1062- {
1063- 'name' : 'users' ,
1064- 'put' : {
1065- 'sql' : 'INSERT OR REPLACE INTO users (id, name) VALUES (?, ?);' ,
1066- 'params' : [
1067- 'Id' ,
1068- {'Column' : 'name' }
1069- ],
1070- },
1071- 'delete' : {
1072- 'sql' : 'DELETE FROM users WHERE id = ?' ,
1073- 'params' : ['Id' ],
1074- },
1075- 'clear' : 'DELETE FROM users;' ,
1076- }
1077- ],
1076+ 'raw_tables' : [rawUsersTable],
10781077 'tables' : [],
10791078 };
10801079
@@ -1239,6 +1238,120 @@ END;
12391238 db.execute ('SELECT powersync_clear(0)' );
12401239 expect (db.select ('SELECT * FROM users' ), hasLength (0 ));
12411240 });
1241+
1242+ test ('can use foreign key constraints' , () {
1243+ db.execute ('pragma foreign_keys = on' );
1244+ setupRawTables ();
1245+ db.execute ('''
1246+ CREATE TABLE user_reference(
1247+ id TEXT NOT NULL PRIMARY KEY,
1248+ user TEXT NOT NULL REFERENCES users (id)
1249+ ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
1250+ );
1251+
1252+ CREATE TRIGGER users_ref_delete
1253+ AFTER DELETE ON user_reference
1254+ FOR EACH ROW
1255+ BEGIN
1256+ INSERT INTO powersync_crud (op, id, type) VALUES ('DELETE', OLD.id, 'user_reference');
1257+ END;
1258+ ''' );
1259+
1260+ invokeControl (
1261+ 'start' ,
1262+ json.encode ({
1263+ 'schema' : {
1264+ 'raw_tables' : [
1265+ rawUsersTable,
1266+ {
1267+ 'name' : 'user_reference' ,
1268+ 'put' : {
1269+ 'sql' :
1270+ 'INSERT OR REPLACE INTO user_reference (id, user) VALUES (?, ?);' ,
1271+ 'params' : [
1272+ 'Id' ,
1273+ {'Column' : 'user' }
1274+ ],
1275+ },
1276+ 'delete' : {
1277+ 'sql' : 'DELETE FROM user_reference WHERE id = ?' ,
1278+ 'params' : ['Id' ],
1279+ },
1280+ 'clear' : 'DELETE FROM user_reference;' ,
1281+ }
1282+ ],
1283+ 'tables' : [],
1284+ }
1285+ }),
1286+ );
1287+
1288+ // Insert - send reference before user.
1289+ pushCheckpoint (buckets: [bucketDescription ('a' )]);
1290+ pushSyncData (
1291+ 'a' ,
1292+ '1' ,
1293+ 'my_ref' ,
1294+ 'PUT' ,
1295+ {'user' : 'my_user' },
1296+ objectType: 'user_reference' ,
1297+ );
1298+ pushSyncData (
1299+ 'a' ,
1300+ '2' ,
1301+ 'my_user' ,
1302+ 'PUT' ,
1303+ {'name' : 'First user' },
1304+ objectType: 'users' ,
1305+ );
1306+ pushCheckpointComplete ();
1307+
1308+ expect (db.select ('select * from user_reference' ), isNotEmpty);
1309+
1310+ {
1311+ // Ensure deleting users creates a ps_crud entry for deleting references.
1312+ db.execute ('BEGIN' );
1313+
1314+ db.execute ('DELETE FROM users' );
1315+ final crud = db.select ('SELECT * FROM ps_crud' );
1316+ expect (crud, [
1317+ {
1318+ 'id' : 1 ,
1319+ 'tx_id' : 2 ,
1320+ 'data' : '{"op":"DELETE","id":"my_ref","type":"user_reference"}' ,
1321+ },
1322+ {
1323+ 'id' : 2 ,
1324+ 'tx_id' : 2 ,
1325+ 'data' : '{"op":"DELETE","id":"my_user","type":"users"}' ,
1326+ }
1327+ ]);
1328+
1329+ // Rollback local changes to test deleting from server.
1330+ db.execute ('ROLLBACK' );
1331+ }
1332+
1333+ // Delete - delete user before reference.
1334+ pushCheckpoint (buckets: [bucketDescription ('a' )]);
1335+ pushSyncData (
1336+ 'a' ,
1337+ '3' ,
1338+ 'my_user' ,
1339+ 'REMOVE' ,
1340+ null ,
1341+ objectType: 'users' ,
1342+ );
1343+ pushSyncData (
1344+ 'a' ,
1345+ '3' ,
1346+ 'my_ref' ,
1347+ 'REMOVE' ,
1348+ null ,
1349+ objectType: 'user_reference' ,
1350+ );
1351+ pushCheckpointComplete ();
1352+
1353+ expect (db.select ('select * from user_reference' ), isEmpty);
1354+ });
12421355 });
12431356}
12441357
0 commit comments