Add test for temporal referential integrity

This commit adds an isolation test showing that temporal foreign keys do
not permit referential integrity violations under concurrency, like
fk-snapshot-2.  You can show that the test fails by passing false for
detectNewRows to ri_PerformCheck in ri_restrict.

Author: Paul Jungwirth <pj@illuminatedcomputing.com>
Reviewed-by: Rustam ALLAKOV <rustamallakov@gmail.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/CA+renyUp=xja80rBaB6NpY3RRdi750y046x28bo_xg29zKY72Q@mail.gmail.com
master
Álvaro Herrera 6 days ago
parent a2b4102a21
commit e8cec3d179
No known key found for this signature in database
GPG Key ID: 1C20ACB9D5C564AE
  1. 213
      src/test/isolation/expected/fk-snapshot-3.out
  2. 1
      src/test/isolation/isolation_schedule
  3. 82
      src/test/isolation/specs/fk-snapshot-3.spec

@ -0,0 +1,213 @@
Parsed test spec with 2 sessions
starting permutation: s1rr s2rr s2ins s1del s2c s1c
step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
step s1del: DELETE FROM parent WHERE id = '[1,2)'; <waiting ...>
step s2c: COMMIT;
step s1del: <... completed>
ERROR: update or delete on table "parent" violates foreign key constraint "child_parent_id_valid_at_fkey" on table "child"
step s1c: COMMIT;
starting permutation: s1rr s2rr s1del s2ins s1c s2c
step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s1del: DELETE FROM parent WHERE id = '[1,2)';
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
<waiting ...>
step s1c: COMMIT;
step s2ins: <... completed>
ERROR: could not serialize access due to concurrent update
step s2c: COMMIT;
starting permutation: s1rc s2rc s2ins s1del s2c s1c
step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
step s1del: DELETE FROM parent WHERE id = '[1,2)'; <waiting ...>
step s2c: COMMIT;
step s1del: <... completed>
ERROR: update or delete on table "parent" violates foreign key constraint "child_parent_id_valid_at_fkey" on table "child"
step s1c: COMMIT;
starting permutation: s1rc s2rc s1del s2ins s1c s2c
step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s1del: DELETE FROM parent WHERE id = '[1,2)';
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
<waiting ...>
step s1c: COMMIT;
step s2ins: <... completed>
ERROR: insert or update on table "child" violates foreign key constraint "child_parent_id_valid_at_fkey"
step s2c: COMMIT;
starting permutation: s1ser s2ser s2ins s1del s2c s1c
step s1ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s2ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
step s1del: DELETE FROM parent WHERE id = '[1,2)'; <waiting ...>
step s2c: COMMIT;
step s1del: <... completed>
ERROR: update or delete on table "parent" violates foreign key constraint "child_parent_id_valid_at_fkey" on table "child"
step s1c: COMMIT;
starting permutation: s1ser s2ser s1del s2ins s1c s2c
step s1ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s2ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s1del: DELETE FROM parent WHERE id = '[1,2)';
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
<waiting ...>
step s1c: COMMIT;
step s2ins: <... completed>
ERROR: could not serialize access due to concurrent update
step s2c: COMMIT;
starting permutation: s1rc s2rc s2ins s1upok s2c s1c
step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
step s1upok: UPDATE parent SET valid_at = '[2020-01-01,2026-01-01)' WHERE id = '[1,2)'; <waiting ...>
step s2c: COMMIT;
step s1upok: <... completed>
step s1c: COMMIT;
starting permutation: s1rc s2rc s1upok s2ins s1c s2c
step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s1upok: UPDATE parent SET valid_at = '[2020-01-01,2026-01-01)' WHERE id = '[1,2)';
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
<waiting ...>
step s1c: COMMIT;
step s2ins: <... completed>
step s2c: COMMIT;
starting permutation: s1rr s2rr s2ins s1upok s2c s1c
step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
step s1upok: UPDATE parent SET valid_at = '[2020-01-01,2026-01-01)' WHERE id = '[1,2)'; <waiting ...>
step s2c: COMMIT;
step s1upok: <... completed>
step s1c: COMMIT;
starting permutation: s1rr s2rr s1upok s2ins s1c s2c
step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s1upok: UPDATE parent SET valid_at = '[2020-01-01,2026-01-01)' WHERE id = '[1,2)';
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
<waiting ...>
step s1c: COMMIT;
step s2ins: <... completed>
ERROR: could not serialize access due to concurrent update
step s2c: COMMIT;
starting permutation: s1ser s2ser s2ins s1upok s2c s1c
step s1ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s2ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
step s1upok: UPDATE parent SET valid_at = '[2020-01-01,2026-01-01)' WHERE id = '[1,2)'; <waiting ...>
step s2c: COMMIT;
step s1upok: <... completed>
step s1c: COMMIT;
starting permutation: s1ser s2ser s1upok s2ins s1c s2c
step s1ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s2ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s1upok: UPDATE parent SET valid_at = '[2020-01-01,2026-01-01)' WHERE id = '[1,2)';
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
<waiting ...>
step s1c: COMMIT;
step s2ins: <... completed>
ERROR: could not serialize access due to concurrent update
step s2c: COMMIT;
starting permutation: s1rc s2rc s2ins s1upbad s2c s1c
step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
step s1upbad: UPDATE parent SET valid_at = '[2020-01-01,2024-01-01)' WHERE id = '[1,2)'; <waiting ...>
step s2c: COMMIT;
step s1upbad: <... completed>
ERROR: update or delete on table "parent" violates foreign key constraint "child_parent_id_valid_at_fkey" on table "child"
step s1c: COMMIT;
starting permutation: s1rc s2rc s1upbad s2ins s1c s2c
step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED;
step s1upbad: UPDATE parent SET valid_at = '[2020-01-01,2024-01-01)' WHERE id = '[1,2)';
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
<waiting ...>
step s1c: COMMIT;
step s2ins: <... completed>
ERROR: insert or update on table "child" violates foreign key constraint "child_parent_id_valid_at_fkey"
step s2c: COMMIT;
starting permutation: s1rr s2rr s2ins s1upbad s2c s1c
step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
step s1upbad: UPDATE parent SET valid_at = '[2020-01-01,2024-01-01)' WHERE id = '[1,2)'; <waiting ...>
step s2c: COMMIT;
step s1upbad: <... completed>
ERROR: update or delete on table "parent" violates foreign key constraint "child_parent_id_valid_at_fkey" on table "child"
step s1c: COMMIT;
starting permutation: s1rr s2rr s1upbad s2ins s1c s2c
step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step s1upbad: UPDATE parent SET valid_at = '[2020-01-01,2024-01-01)' WHERE id = '[1,2)';
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
<waiting ...>
step s1c: COMMIT;
step s2ins: <... completed>
ERROR: could not serialize access due to concurrent update
step s2c: COMMIT;
starting permutation: s1ser s2ser s2ins s1upbad s2c s1c
step s1ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s2ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
step s1upbad: UPDATE parent SET valid_at = '[2020-01-01,2024-01-01)' WHERE id = '[1,2)'; <waiting ...>
step s2c: COMMIT;
step s1upbad: <... completed>
ERROR: update or delete on table "parent" violates foreign key constraint "child_parent_id_valid_at_fkey" on table "child"
step s1c: COMMIT;
starting permutation: s1ser s2ser s1upbad s2ins s1c s2c
step s1ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s2ser: BEGIN ISOLATION LEVEL SERIALIZABLE;
step s1upbad: UPDATE parent SET valid_at = '[2020-01-01,2024-01-01)' WHERE id = '[1,2)';
step s2ins:
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
<waiting ...>
step s1c: COMMIT;
step s2ins: <... completed>
ERROR: could not serialize access due to concurrent update
step s2c: COMMIT;

@ -37,6 +37,7 @@ test: fk-partitioned-1
test: fk-partitioned-2
test: fk-snapshot
test: fk-snapshot-2
test: fk-snapshot-3
test: subxid-overflow
test: eval-plan-qual
test: eval-plan-qual-trigger

@ -0,0 +1,82 @@
# RI Trigger test
#
# Test C-based temporal referential integrity enforcement.
# Under REPEATABLE READ we need some snapshot trickery in C,
# or we would permit things that violate referential integrity.
setup
{
CREATE TABLE parent (
id int4range NOT NULL,
valid_at daterange NOT NULL,
PRIMARY KEY (id, valid_at WITHOUT OVERLAPS));
CREATE TABLE child (
id int4range NOT NULL,
valid_at daterange NOT NULL,
parent_id int4range,
FOREIGN KEY (parent_id, PERIOD valid_at) REFERENCES parent);
INSERT INTO parent VALUES ('[1,2)', '[2020-01-01,2030-01-01)');
}
teardown { DROP TABLE parent, child; }
session s1
step s1rc { BEGIN ISOLATION LEVEL READ COMMITTED; }
step s1rr { BEGIN ISOLATION LEVEL REPEATABLE READ; }
step s1ser { BEGIN ISOLATION LEVEL SERIALIZABLE; }
step s1del { DELETE FROM parent WHERE id = '[1,2)'; }
step s1upok { UPDATE parent SET valid_at = '[2020-01-01,2026-01-01)' WHERE id = '[1,2)'; }
step s1upbad { UPDATE parent SET valid_at = '[2020-01-01,2024-01-01)' WHERE id = '[1,2)'; }
step s1c { COMMIT; }
session s2
step s2rc { BEGIN ISOLATION LEVEL READ COMMITTED; }
step s2rr { BEGIN ISOLATION LEVEL REPEATABLE READ; }
step s2ser { BEGIN ISOLATION LEVEL SERIALIZABLE; }
step s2ins {
INSERT INTO child VALUES ('[1,2)', '[2020-01-01,2025-01-01)', '[1,2)');
}
step s2c { COMMIT; }
# Violates referential integrity unless we use an up-to-date crosscheck snapshot:
permutation s1rr s2rr s2ins s1del s2c s1c
# Raises a can't-serialize exception
# when the INSERT trigger does SELECT FOR KEY SHARE:
permutation s1rr s2rr s1del s2ins s1c s2c
# Test the same scenarios in READ COMMITTED:
# A crosscheck snapshot is not required here.
permutation s1rc s2rc s2ins s1del s2c s1c
permutation s1rc s2rc s1del s2ins s1c s2c
# Test the same scenarios in SERIALIZABLE:
# We should report the FK violation:
permutation s1ser s2ser s2ins s1del s2c s1c
# We raise a concurrent update error
# which is good enough:
permutation s1ser s2ser s1del s2ins s1c s2c
# Also check updating the valid time (without violating RI):
# ...with READ COMMITED:
permutation s1rc s2rc s2ins s1upok s2c s1c
permutation s1rc s2rc s1upok s2ins s1c s2c
# ...with REPEATABLE READ:
permutation s1rr s2rr s2ins s1upok s2c s1c
permutation s1rr s2rr s1upok s2ins s1c s2c
# ...with SERIALIZABLE:
permutation s1ser s2ser s2ins s1upok s2c s1c
permutation s1ser s2ser s1upok s2ins s1c s2c
# Also check updating the valid time (while violating RI):
# ...with READ COMMITED:
permutation s1rc s2rc s2ins s1upbad s2c s1c
permutation s1rc s2rc s1upbad s2ins s1c s2c
# ...with REPEATABLE READ:
permutation s1rr s2rr s2ins s1upbad s2c s1c
permutation s1rr s2rr s1upbad s2ins s1c s2c
# ...with SERIALIZABLE:
permutation s1ser s2ser s2ins s1upbad s2c s1c
permutation s1ser s2ser s1upbad s2ins s1c s2c
Loading…
Cancel
Save