Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1e65c1b

Browse files
pjungwirCommitfest Bot
authored and
Commitfest Bot
committed
Fill testing gap for possible referential integrity violation
This commit adds a missing isolation test for (non-PERIOD) foreign keys. With REPEATABLE READ, one transaction can insert a referencing row while another deletes the referenced row, and both see a valid state. But after they have committed, the table violates referential integrity. If the INSERT precedes the DELETE, we use a crosscheck snapshot to see the just-added row, so that the DELETE can raise a foreign key error. You can see the table violate referential integrity if you change ri_restrict to pass false for detectNewRows to ri_PerformCheck. A crosscheck snapshot is not needed when the DELETE comes first, because the INSERT's trigger takes a FOR KEY SHARE lock that sees the row now marked for deletion, waits for that transaction to commit, and raises a serialization error. I added a test for that too though. We already have a similar test (in ri-triggers.spec) for SERIALIZABLE snapshot isolation showing that you can implement foreign keys with just pl/pgSQL, but that test does nothing to validate ri_triggers.c. We also have tests (in fk-snapshot.spec) for other concurrency scenarios, but not this one: we test concurrently deleting both the referencing and referenced row, when the constraint activates a cascade/set null action. But those tests don't exercise ri_restrict, and the consequence of omitting a crosscheck comparison is different: a serialization failure, not a referential integrity violation.
1 parent 03c53a7 commit 1e65c1b

File tree

3 files changed

+51
-0
lines changed

3 files changed

+51
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Parsed test spec with 2 sessions
2+
3+
starting permutation: s2ins s1del s2c s1c
4+
step s2ins: INSERT INTO child VALUES (1, 1);
5+
step s1del: DELETE FROM parent WHERE parent_id = 1; <waiting ...>
6+
step s2c: COMMIT;
7+
step s1del: <... completed>
8+
ERROR: update or delete on table "parent" violates foreign key constraint "child_parent_id_fkey" on table "child"
9+
step s1c: COMMIT;
10+
11+
starting permutation: s1del s2ins s1c s2c
12+
step s1del: DELETE FROM parent WHERE parent_id = 1;
13+
step s2ins: INSERT INTO child VALUES (1, 1); <waiting ...>
14+
step s1c: COMMIT;
15+
step s2ins: <... completed>
16+
ERROR: could not serialize access due to concurrent update
17+
step s2c: COMMIT;

src/test/isolation/isolation_schedule

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ test: fk-deadlock2
3535
test: fk-partitioned-1
3636
test: fk-partitioned-2
3737
test: fk-snapshot
38+
test: fk-snapshot-2
3839
test: subxid-overflow
3940
test: eval-plan-qual
4041
test: eval-plan-qual-trigger
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# RI Trigger test
2+
#
3+
# Test C-based referential integrity enforcement.
4+
# Under REPEATABLE READ we need some snapshot trickery in C,
5+
# or we would permit things that violate referential integrity.
6+
7+
setup
8+
{
9+
CREATE TABLE parent (parent_id SERIAL NOT NULL PRIMARY KEY);
10+
CREATE TABLE child (
11+
child_id SERIAL NOT NULL PRIMARY KEY,
12+
parent_id INTEGER REFERENCES parent);
13+
INSERT INTO parent VALUES(1);
14+
}
15+
16+
teardown { DROP TABLE parent, child; }
17+
18+
session s1
19+
setup { BEGIN ISOLATION LEVEL REPEATABLE READ; }
20+
step s1del { DELETE FROM parent WHERE parent_id = 1; }
21+
step s1c { COMMIT; }
22+
23+
session s2
24+
setup { BEGIN ISOLATION LEVEL REPEATABLE READ; }
25+
step s2ins { INSERT INTO child VALUES (1, 1); }
26+
step s2c { COMMIT; }
27+
28+
# Violates referential integrity unless we use an up-to-date crosscheck snapshot:
29+
permutation s2ins s1del s2c s1c
30+
31+
# Raises a can't-serialize exception
32+
# when the INSERT trigger does SELECT FOR KEY SHARE:
33+
permutation s1del s2ins s1c s2c

0 commit comments

Comments
 (0)