@@ -1079,24 +1079,23 @@ END;
10791079 });
10801080
10811081 group ('raw tables' , () {
1082+ const rawUsersTable = {
1083+ 'name' : 'users' ,
1084+ 'put' : {
1085+ 'sql' : 'INSERT OR REPLACE INTO users (id, name) VALUES (?, ?);' ,
1086+ 'params' : [
1087+ 'Id' ,
1088+ {'Column' : 'name' }
1089+ ],
1090+ },
1091+ 'delete' : {
1092+ 'sql' : 'DELETE FROM users WHERE id = ?' ,
1093+ 'params' : ['Id' ],
1094+ },
1095+ 'clear' : 'DELETE FROM users;' ,
1096+ };
10821097 const schema = {
1083- 'raw_tables' : [
1084- {
1085- 'name' : 'users' ,
1086- 'put' : {
1087- 'sql' : 'INSERT OR REPLACE INTO users (id, name) VALUES (?, ?);' ,
1088- 'params' : [
1089- 'Id' ,
1090- {'Column' : 'name' }
1091- ],
1092- },
1093- 'delete' : {
1094- 'sql' : 'DELETE FROM users WHERE id = ?' ,
1095- 'params' : ['Id' ],
1096- },
1097- 'clear' : 'DELETE FROM users;' ,
1098- }
1099- ],
1098+ 'raw_tables' : [rawUsersTable],
11001099 'tables' : [],
11011100 };
11021101
@@ -1261,6 +1260,120 @@ END;
12611260 db.execute ('SELECT powersync_clear(0)' );
12621261 expect (db.select ('SELECT * FROM users' ), hasLength (0 ));
12631262 });
1263+
1264+ test ('can use foreign key constraints' , () {
1265+ db.execute ('pragma foreign_keys = on' );
1266+ setupRawTables ();
1267+ db.execute ('''
1268+ CREATE TABLE user_reference(
1269+ id TEXT NOT NULL PRIMARY KEY,
1270+ user TEXT NOT NULL REFERENCES users (id)
1271+ ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
1272+ );
1273+
1274+ CREATE TRIGGER users_ref_delete
1275+ AFTER DELETE ON user_reference
1276+ FOR EACH ROW
1277+ BEGIN
1278+ INSERT INTO powersync_crud (op, id, type) VALUES ('DELETE', OLD.id, 'user_reference');
1279+ END;
1280+ ''' );
1281+
1282+ invokeControl (
1283+ 'start' ,
1284+ json.encode ({
1285+ 'schema' : {
1286+ 'raw_tables' : [
1287+ rawUsersTable,
1288+ {
1289+ 'name' : 'user_reference' ,
1290+ 'put' : {
1291+ 'sql' :
1292+ 'INSERT OR REPLACE INTO user_reference (id, user) VALUES (?, ?);' ,
1293+ 'params' : [
1294+ 'Id' ,
1295+ {'Column' : 'user' }
1296+ ],
1297+ },
1298+ 'delete' : {
1299+ 'sql' : 'DELETE FROM user_reference WHERE id = ?' ,
1300+ 'params' : ['Id' ],
1301+ },
1302+ 'clear' : 'DELETE FROM user_reference;' ,
1303+ }
1304+ ],
1305+ 'tables' : [],
1306+ }
1307+ }),
1308+ );
1309+
1310+ // Insert - send reference before user.
1311+ pushCheckpoint (buckets: [bucketDescription ('a' )]);
1312+ pushSyncData (
1313+ 'a' ,
1314+ '1' ,
1315+ 'my_ref' ,
1316+ 'PUT' ,
1317+ {'user' : 'my_user' },
1318+ objectType: 'user_reference' ,
1319+ );
1320+ pushSyncData (
1321+ 'a' ,
1322+ '2' ,
1323+ 'my_user' ,
1324+ 'PUT' ,
1325+ {'name' : 'First user' },
1326+ objectType: 'users' ,
1327+ );
1328+ pushCheckpointComplete ();
1329+
1330+ expect (db.select ('select * from user_reference' ), isNotEmpty);
1331+
1332+ {
1333+ // Ensure deleting users creates a ps_crud entry for deleting references.
1334+ db.execute ('BEGIN' );
1335+
1336+ db.execute ('DELETE FROM users' );
1337+ final crud = db.select ('SELECT * FROM ps_crud' );
1338+ expect (crud, [
1339+ {
1340+ 'id' : 1 ,
1341+ 'tx_id' : 2 ,
1342+ 'data' : '{"op":"DELETE","id":"my_ref","type":"user_reference"}' ,
1343+ },
1344+ {
1345+ 'id' : 2 ,
1346+ 'tx_id' : 2 ,
1347+ 'data' : '{"op":"DELETE","id":"my_user","type":"users"}' ,
1348+ }
1349+ ]);
1350+
1351+ // Rollback local changes to test deleting from server.
1352+ db.execute ('ROLLBACK' );
1353+ }
1354+
1355+ // Delete - delete user before reference.
1356+ pushCheckpoint (buckets: [bucketDescription ('a' )]);
1357+ pushSyncData (
1358+ 'a' ,
1359+ '3' ,
1360+ 'my_user' ,
1361+ 'REMOVE' ,
1362+ null ,
1363+ objectType: 'users' ,
1364+ );
1365+ pushSyncData (
1366+ 'a' ,
1367+ '3' ,
1368+ 'my_ref' ,
1369+ 'REMOVE' ,
1370+ null ,
1371+ objectType: 'user_reference' ,
1372+ );
1373+ pushCheckpointComplete ();
1374+
1375+ expect (db.select ('select * from user_reference' ), isEmpty);
1376+ });
12641377 });
12651378}
12661379
0 commit comments