From ae9ed37ccfa88c6874ba9114953f5032cc85d086 Mon Sep 17 00:00:00 2001
From: luquijeffrey <lucas.jeffrey@anachronics.com>
Date: Tue, 19 May 2026 17:42:11 -0300
Subject: [PATCH 1/2] Add isolation test case for RI plan invalidation crash

---
 .../isolation/expected/ri-cascade-del.out     | 28 ++++++
 src/test/isolation/isolation_schedule         |  1 +
 src/test/isolation/specs/ri-cascade-del.spec  | 85 +++++++++++++++++++
 3 files changed, 114 insertions(+)
 create mode 100644 src/test/isolation/expected/ri-cascade-del.out
 create mode 100644 src/test/isolation/specs/ri-cascade-del.spec

diff --git a/src/test/isolation/expected/ri-cascade-del.out b/src/test/isolation/expected/ri-cascade-del.out
new file mode 100644
index 00000000000..051083b17f8
--- /dev/null
+++ b/src/test/isolation/expected/ri-cascade-del.out
@@ -0,0 +1,28 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s2_lock s1_delete s2_inval s2_unlock s1_commit
+step s2_lock: SELECT pg_advisory_lock(1);
+pg_advisory_lock
+----------------
+                
+(1 row)
+
+step s1_delete: DELETE FROM crash_reentrancia_tabla_autorederencial WHERE id = 1; <waiting ...>
+step s2_inval: 
+  DO $$                                    
+  BEGIN
+    FOR i IN 1..1000 LOOP
+      EXECUTE 'CREATE TEMPORARY TABLE t_temp_inval_(id INTEGER PRIMARY KEY)';
+      EXECUTE 'DROP TABLE t_temp_inval_';
+    END LOOP;
+  END;
+  $$;
+
+step s2_unlock: SELECT pg_advisory_unlock(1);
+pg_advisory_unlock
+------------------
+t                 
+(1 row)
+
+step s1_delete: <... completed>
+step s1_commit: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 1578ba191c8..39a0a1ee792 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -12,6 +12,7 @@ test: project-manager
 test: classroom-scheduling
 test: total-cash
 test: referential-integrity
+test: ri-cascade-del
 test: ri-trigger
 test: partial-index
 test: two-ids
diff --git a/src/test/isolation/specs/ri-cascade-del.spec b/src/test/isolation/specs/ri-cascade-del.spec
new file mode 100644
index 00000000000..aa8e090c7ad
--- /dev/null
+++ b/src/test/isolation/specs/ri-cascade-del.spec
@@ -0,0 +1,85 @@
+# Setup for referential integrity crash test
+setup
+{
+    CREATE TABLE crash_reentrancia_tabla_autorederencial (
+        id int PRIMARY KEY,
+        nombre text,
+        padre_id int REFERENCES crash_reentrancia_tabla_autorederencial(id) ON DELETE CASCADE
+    );
+
+    CREATE TABLE crash_reentrancia_segunda_tabla (
+        id    int PRIMARY KEY,
+        valor text
+    );
+
+    CREATE OR REPLACE FUNCTION crash_reentrancia_before_delete()
+    RETURNS trigger AS $$
+    DECLARE
+        v_valor text;
+    BEGIN
+        IF OLD.id % 2 = 1 THEN 
+            RETURN OLD;
+        END IF;
+
+        -- Wait for S2 to finish flooding the invalidation message queue
+        IF OLD.id = 2 THEN
+            PERFORM pg_advisory_lock(1);
+            PERFORM pg_advisory_unlock(1);
+        END IF;
+
+        IF OLD.id > 4 THEN
+            -- This opens the table and forces processing of pending inval messages
+            SELECT valor INTO v_valor FROM crash_reentrancia_segunda_tabla WHERE id = OLD.id;
+        END IF;
+        
+        DELETE FROM crash_reentrancia_tabla_autorederencial WHERE padre_id = OLD.id;
+        RETURN OLD;
+    END;
+    $$ LANGUAGE plpgsql;
+
+    CREATE TRIGGER trg_crash_reentrancia_before_delete
+        BEFORE DELETE ON crash_reentrancia_tabla_autorederencial
+        FOR EACH ROW EXECUTE FUNCTION crash_reentrancia_before_delete();
+
+    INSERT INTO crash_reentrancia_tabla_autorederencial VALUES (1, 'A', NULL);
+    INSERT INTO crash_reentrancia_tabla_autorederencial VALUES (2, 'B', 1);
+    INSERT INTO crash_reentrancia_tabla_autorederencial VALUES (3, 'C', 2);
+    INSERT INTO crash_reentrancia_tabla_autorederencial VALUES (4, 'D', 3);
+    INSERT INTO crash_reentrancia_tabla_autorederencial VALUES (5, 'E', 4);
+    INSERT INTO crash_reentrancia_tabla_autorederencial VALUES (6, 'F', 5);
+
+    INSERT INTO crash_reentrancia_segunda_tabla VALUES 
+        (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f');
+}
+
+teardown
+{
+    DROP TRIGGER trg_crash_reentrancia_before_delete ON crash_reentrancia_tabla_autorederencial;
+    DROP FUNCTION crash_reentrancia_before_delete CASCADE;
+    DROP TABLE crash_reentrancia_tabla_autorederencial CASCADE;
+    DROP TABLE crash_reentrancia_segunda_tabla CASCADE;
+}
+
+session s1
+setup { BEGIN; }
+step s1_delete { DELETE FROM crash_reentrancia_tabla_autorederencial WHERE id = 1; }
+step s1_commit { COMMIT; }
+
+session s2
+step s2_lock { SELECT pg_advisory_lock(1); }
+step s2_inval {
+  DO $$                                    
+  BEGIN
+    FOR i IN 1..1000 LOOP
+      EXECUTE 'CREATE TEMPORARY TABLE t_temp_inval_(id INTEGER PRIMARY KEY)';
+      EXECUTE 'DROP TABLE t_temp_inval_';
+    END LOOP;
+  END;
+  $$;
+}
+step s2_unlock { SELECT pg_advisory_unlock(1); }
+
+# Execution permutation
+# S2 locks -> S1 blocks on S2 -> S2 forces inval queue overflow -> S2 unlocks
+# S1 awakens -> S1 forces table_open -> invalidation processed -> segfault!
+permutation s2_lock s1_delete s2_inval s2_unlock s1_commit
-- 
2.34.1

