From bebe230b05f860ea89b88f3c5a7fb615fffc6fa8 Mon Sep 17 00:00:00 2001 From: Jussi Saurio Date: Mon, 20 Oct 2025 13:59:02 +0300 Subject: [PATCH] Regression test: deferred FK violations are checked before commit --- .../query_processing/test_transactions.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/integration/query_processing/test_transactions.rs b/tests/integration/query_processing/test_transactions.rs index 36df15f9d..89f102ae5 100644 --- a/tests/integration/query_processing/test_transactions.rs +++ b/tests/integration/query_processing/test_transactions.rs @@ -208,6 +208,37 @@ fn test_constraint_error_aborts_transaction() { assert_eq!(row, vec![Value::Integer(0)]); } +#[test] +/// Regression test for https://github.com/tursodatabase/turso/issues/3784 where dirty pages +/// were flushed to WAL _before_ deferred FK violations were checked. This resulted in the +/// violations being persisted to the database, even though the transaction was aborted. +/// This test ensures that dirty pages are not flushed to WAL until after deferred violations are checked. +fn test_deferred_fk_violation_rollback_in_autocommit() { + let tmp_db = TempDatabase::new("test_deferred_fk_violation_rollback.db", true); + let conn = tmp_db.connect_limbo(); + + // Enable foreign keys + conn.execute("PRAGMA foreign_keys = ON").unwrap(); + + // Create parent and child tables with deferred FK constraint + conn.execute("CREATE TABLE parent(a PRIMARY KEY)").unwrap(); + conn.execute("CREATE TABLE child(a, b, FOREIGN KEY(b) REFERENCES parent(a) DEFERRABLE INITIALLY DEFERRED)") + .unwrap(); + + // This insert should fail because parent(1) doesn't exist + // and the deferred FK violation should be caught at statement end in autocommit mode + let result = conn.execute("INSERT INTO child VALUES(1,1)"); + assert!(matches!(result, Err(LimboError::Constraint(_)))); + + // Do a truncating checkpoint + conn.execute("PRAGMA wal_checkpoint(TRUNCATE)").unwrap(); + + // Verify that the child table is empty (the insert was rolled back) + let stmt = conn.query("SELECT COUNT(*) FROM child").unwrap().unwrap(); + let row = helper_read_single_row(stmt); + assert_eq!(row, vec![Value::Integer(0)]); +} + #[test] fn test_mvcc_transactions_autocommit() { let tmp_db = TempDatabase::new_with_opts(