| # This test exercises behavior of foreign keys in the face of concurrent |
| # detach of partitions in the referenced table. |
| # (The cases where the detaching transaction is cancelled is interesting |
| # because the locking situation is completely different. I didn't verify |
| # that keeping both variants adds any extra coverage.) |
| # |
| # Note: When using "s1cancel", mark the target step (the one to be canceled) |
| # as blocking "s1cancel". This ensures consistent reporting regardless of |
| # whether "s1cancel" finishes before or after the other step reports failure. |
| # Also, ensure the step after "s1cancel" is also an s1 step (use "s1noop" if |
| # necessary). This ensures we won't move on to the next step until the cancel |
| # is complete. |
| |
| setup { |
| drop table if exists d4_primary, d4_primary1, d4_fk, d4_pid; |
| create table d4_primary (a int primary key) partition by list (a); |
| create table d4_primary1 partition of d4_primary for values in (1); |
| create table d4_primary2 partition of d4_primary for values in (2); |
| insert into d4_primary values (1); |
| insert into d4_primary values (2); |
| create table d4_fk (a int references d4_primary); |
| insert into d4_fk values (2); |
| create table d4_pid (pid int); |
| } |
| |
| session s1 |
| step s1b { begin; } |
| step s1brr { begin isolation level repeatable read; } |
| step s1s { select * from d4_primary; } |
| step s1cancel { select pg_cancel_backend(pid) from d4_pid; } |
| step s1noop { } |
| step s1insert { insert into d4_fk values (1); } |
| step s1c { commit; } |
| step s1declare { declare f cursor for select * from d4_primary; } |
| step s1declare2 { declare f cursor for select * from d4_fk where a = 2; } |
| step s1fetchall { fetch all from f; } |
| step s1fetchone { fetch 1 from f; } |
| step s1updcur { update d4_fk set a = 1 where current of f; } |
| step s1svpt { savepoint f; } |
| step s1rollback { rollback to f; } |
| |
| session s2 |
| step s2snitch { insert into d4_pid select pg_backend_pid(); } |
| step s2detach { alter table d4_primary detach partition d4_primary1 concurrently; } |
| |
| session s3 |
| step s3brr { begin isolation level repeatable read; } |
| step s3insert { insert into d4_fk values (1); } |
| step s3commit { commit; } |
| step s3vacfreeze { vacuum freeze pg_catalog.pg_inherits; } |
| |
| # Trying to insert into a partially detached partition is rejected |
| permutation s2snitch s1b s1s s2detach s1cancel(s2detach) s1insert s1c |
| permutation s2snitch s1b s1s s2detach s1insert s1c |
| # ... even under REPEATABLE READ mode. |
| permutation s2snitch s1brr s1s s2detach s1cancel(s2detach) s1insert s1c |
| permutation s2snitch s1brr s1s s2detach s1insert s1c |
| |
| # If you read the referenced table using a cursor, you can see a row that the |
| # RI query does not see. |
| permutation s2snitch s1b s1declare s2detach s1cancel(s2detach) s1fetchall s1insert s1c |
| permutation s2snitch s1b s1declare s2detach s1fetchall s1insert s1c |
| permutation s2snitch s1b s1declare s2detach s1cancel(s2detach) s1svpt s1insert s1rollback s1fetchall s1c |
| permutation s2snitch s1b s1declare s2detach s1svpt s1insert s1rollback s1fetchall s1c |
| permutation s2snitch s1b s2detach s1declare s1cancel(s2detach) s1fetchall s1insert s1c |
| permutation s2snitch s1b s2detach s1declare s1fetchall s1insert s1c |
| permutation s2snitch s1b s2detach s1declare s1cancel(s2detach) s1svpt s1insert s1rollback s1fetchall s1c |
| permutation s2snitch s1b s2detach s1declare s1svpt s1insert s1rollback s1fetchall s1c |
| |
| # Creating the referencing row using a cursor |
| permutation s2snitch s1brr s1declare2 s1fetchone s2detach s1cancel(s2detach) s1updcur s1c |
| permutation s2snitch s1brr s1declare2 s1fetchone s2detach s1updcur s1c |
| permutation s2snitch s1brr s1declare2 s1fetchone s1updcur s2detach s1c |
| |
| # Try reading the table from an independent session. |
| permutation s2snitch s1b s1s s2detach s3insert s1c |
| permutation s2snitch s1b s1s s2detach s3brr s3insert s3commit s1cancel(s2detach) s1c |
| permutation s2snitch s1b s1s s2detach s3brr s3insert s3commit s1c |
| |
| # Try one where we VACUUM FREEZE pg_inherits (to verify that xmin change is |
| # handled correctly). |
| permutation s2snitch s1brr s1s s2detach s1cancel(s2detach) s1noop s3vacfreeze s1s s1insert s1c |
| permutation s2snitch s1b s1s s2detach s1cancel(s2detach) s1noop s3vacfreeze s1s s1insert s1c |