From ea414e6b16be36f28b4150bf1e9605f89b7edb15 Mon Sep 17 00:00:00 2001 From: amitlan Date: Thu, 18 Mar 2021 14:01:27 +0900 Subject: [PATCH v1] Tweak handling of serialization failures during cascaded RI update/delete The current coding for signaling a serializability failure during cascaded update or delete of a row not visible to the start-of-the- transaction snapshot involves a hack whereby the table AM forcefully returns a status code that basically tells the executor that the row has been concurrently updated such that the executor can issue an error if needed. However, the blocks of code that generate other information to return about the failing row cannot do so in all cases of the aforementioned hack, which can manifest as Assert failures in those blocks. To fix, simply error out immediately in the table AM itself rather than in the caller, which avoids the gymnastics of returning the correct failure information. This also adds an isolation tests for some affected cases. --- src/backend/access/heap/heapam.c | 13 +++++---- .../isolation/expected/fk-serializable.out | 19 ++++++++++++ src/test/isolation/isolation_schedule | 1 + src/test/isolation/specs/fk-serializable.spec | 29 +++++++++++++++++++ 4 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 src/test/isolation/expected/fk-serializable.out create mode 100644 src/test/isolation/specs/fk-serializable.spec diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 7cb87f4a3b..baf6e2444e 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -2925,8 +2925,11 @@ l1: if (crosscheck != InvalidSnapshot && result == TM_Ok) { /* Perform additional check for transaction-snapshot mode RI updates */ + Assert(IsolationUsesXactSnapshot()); if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer)) - result = TM_Updated; + ereport(ERROR, + (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), + errmsg("could not serialize access due to concurrent update"))); } if (result != TM_Ok) @@ -3554,11 +3557,11 @@ l2: if (crosscheck != InvalidSnapshot && result == TM_Ok) { /* Perform additional check for transaction-snapshot mode RI updates */ + Assert(IsolationUsesXactSnapshot()); if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer)) - { - result = TM_Updated; - Assert(!ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid)); - } + ereport(ERROR, + (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), + errmsg("could not serialize access due to concurrent update"))); } if (result != TM_Ok) diff --git a/src/test/isolation/expected/fk-serializable.out b/src/test/isolation/expected/fk-serializable.out new file mode 100644 index 0000000000..b7bc22a973 --- /dev/null +++ b/src/test/isolation/expected/fk-serializable.out @@ -0,0 +1,19 @@ +Parsed test spec with 2 sessions + +starting permutation: s1spk s2ifk s1dpk s1c +step s1spk: TABLE fk; +a + +step s2ifk: INSERT INTO fk VALUES (1); +step s1dpk: DELETE FROM pk; +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; + +starting permutation: s1spk s2ifk s1upk s1c +step s1spk: TABLE fk; +a + +step s2ifk: INSERT INTO fk VALUES (1); +step s1upk: UPDATE pk SET a = a + 1; +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule index 5d6b79e66e..f97e45f0dc 100644 --- a/src/test/isolation/isolation_schedule +++ b/src/test/isolation/isolation_schedule @@ -29,6 +29,7 @@ test: fk-deadlock test: fk-deadlock2 test: fk-partitioned-1 test: fk-partitioned-2 +test: fk-serializable test: eval-plan-qual test: eval-plan-qual-trigger test: lock-update-delete diff --git a/src/test/isolation/specs/fk-serializable.spec b/src/test/isolation/specs/fk-serializable.spec new file mode 100644 index 0000000000..dfca06fe3d --- /dev/null +++ b/src/test/isolation/specs/fk-serializable.spec @@ -0,0 +1,29 @@ + + +setup +{ + CREATE TABLE pk (a int PRIMARY KEY); + CREATE TABLE fk (a int REFERENCES pk ON DELETE CASCADE ON UPDATE CASCADE); + INSERT INTO pk VALUES (1); +} + +teardown +{ + DROP TABLE fk, pk; +} + +session "s1" + +setup { BEGIN ISOLATION LEVEL SERIALIZABLE; } +step "s1spk" { TABLE fk; } +step "s1dpk" { DELETE FROM pk; } +step "s1upk" { UPDATE pk SET a = a + 1; } +step "s1c" { COMMIT; } + + +session "s2" + +step "s2ifk" { INSERT INTO fk VALUES (1); } + +permutation "s1spk" "s2ifk" "s1dpk" "s1c" +permutation "s1spk" "s2ifk" "s1upk" "s1c" -- 2.24.1