diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 7d7411a7056..ffd4834ae8d 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2809,14 +2809,6 @@ ExecOnConflictUpdate(ModifyTableContext *context, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("could not serialize access due to concurrent update"))); - /* - * As long as we don't support an UPDATE of INSERT ON CONFLICT for - * a partitioned table we shouldn't reach to a case where tuple to - * be lock is moved to another partition due to concurrent update - * of the partition key. - */ - Assert(!ItemPointerIndicatesMovedPartitions(&tmfd.ctid)); - /* * Tell caller to try again from the very start. * @@ -2834,7 +2826,6 @@ ExecOnConflictUpdate(ModifyTableContext *context, errmsg("could not serialize access due to concurrent delete"))); /* see TM_Updated case */ - Assert(!ItemPointerIndicatesMovedPartitions(&tmfd.ctid)); ExecClearTuple(existing); return false; diff --git a/src/test/isolation/expected/insert-conflict-do-update-4.out b/src/test/isolation/expected/insert-conflict-do-update-4.out new file mode 100644 index 00000000000..6e96e0d12da --- /dev/null +++ b/src/test/isolation/expected/insert-conflict-do-update-4.out @@ -0,0 +1,63 @@ +Parsed test spec with 2 sessions + +starting permutation: lock2 insert1 update2a c2 select1 c1 +step lock2: SELECT * FROM upsert WHERE i = 1 FOR UPDATE; +i| j| k +-+--+--- +1|10|100 +(1 row) + +step insert1: INSERT INTO upsert VALUES (1, 11, 111) + ON CONFLICT (i) DO UPDATE SET k = EXCLUDED.k; +step update2a: UPDATE upsert SET i = i + 10 WHERE i = 1; +step c2: COMMIT; +step insert1: <... completed> +step select1: SELECT * FROM upsert; + i| j| k +--+--+--- +11|10|100 + 1|11|111 +(2 rows) + +step c1: COMMIT; + +starting permutation: lock2 insert1 update2b c2 select1 c1 +step lock2: SELECT * FROM upsert WHERE i = 1 FOR UPDATE; +i| j| k +-+--+--- +1|10|100 +(1 row) + +step insert1: INSERT INTO upsert VALUES (1, 11, 111) + ON CONFLICT (i) DO UPDATE SET k = EXCLUDED.k; +step update2b: UPDATE upsert SET i = i + 150 WHERE i = 1; +step c2: COMMIT; +step insert1: <... completed> +step select1: SELECT * FROM upsert; + i| j| k +---+--+--- + 1|11|111 +151|10|100 +(2 rows) + +step c1: COMMIT; + +starting permutation: lock2 insert1 delete2 c2 select1 c1 +step lock2: SELECT * FROM upsert WHERE i = 1 FOR UPDATE; +i| j| k +-+--+--- +1|10|100 +(1 row) + +step insert1: INSERT INTO upsert VALUES (1, 11, 111) + ON CONFLICT (i) DO UPDATE SET k = EXCLUDED.k; +step delete2: DELETE FROM upsert WHERE i = 1; +step c2: COMMIT; +step insert1: <... completed> +step select1: SELECT * FROM upsert; +i| j| k +-+--+--- +1|11|111 +(1 row) + +step c1: COMMIT; diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule index 01ff1c6586f..6a4d3532e03 100644 --- a/src/test/isolation/isolation_schedule +++ b/src/test/isolation/isolation_schedule @@ -52,6 +52,7 @@ test: insert-conflict-do-nothing-2 test: insert-conflict-do-update test: insert-conflict-do-update-2 test: insert-conflict-do-update-3 +test: insert-conflict-do-update-4 test: insert-conflict-specconflict test: merge-insert-update test: merge-delete diff --git a/src/test/isolation/specs/insert-conflict-do-update-4.spec b/src/test/isolation/specs/insert-conflict-do-update-4.spec new file mode 100644 index 00000000000..6297b1d1d6c --- /dev/null +++ b/src/test/isolation/specs/insert-conflict-do-update-4.spec @@ -0,0 +1,42 @@ +# INSERT...ON CONFLICT DO UPDATE test with partitioned table +# +# We use SELECT FOR UPDATE to block the INSERT at the point where +# it has found an existing tuple and is attempting to update it. +# Then we can execute a conflicting update and verify the results. +# Of course, this only works in READ COMMITTED mode, else we'd get an error. + +setup +{ + CREATE TABLE upsert (i int PRIMARY KEY, j int, k int) PARTITION BY RANGE (i); + CREATE TABLE upsert_1 PARTITION OF upsert FOR VALUES FROM (1) TO (100); + CREATE TABLE upsert_2 PARTITION OF upsert FOR VALUES FROM (100) TO (200); + + INSERT INTO upsert VALUES (1, 10, 100); +} + +teardown +{ + DROP TABLE upsert; +} + +session s1 +setup { BEGIN ISOLATION LEVEL READ COMMITTED; } +step insert1 { INSERT INTO upsert VALUES (1, 11, 111) + ON CONFLICT (i) DO UPDATE SET k = EXCLUDED.k; } +step select1 { SELECT * FROM upsert; } +step c1 { COMMIT; } + +session s2 +setup { BEGIN ISOLATION LEVEL READ COMMITTED; } +step lock2 { SELECT * FROM upsert WHERE i = 1 FOR UPDATE; } +step update2a { UPDATE upsert SET i = i + 10 WHERE i = 1; } +step update2b { UPDATE upsert SET i = i + 150 WHERE i = 1; } +step delete2 { DELETE FROM upsert WHERE i = 1; } +step c2 { COMMIT; } + +# Test case where concurrent update moves the target row within the partition +permutation lock2 insert1 update2a c2 select1 c1 +# Test case where concurrent update moves the target row to another partition +permutation lock2 insert1 update2b c2 select1 c1 +# Test case where target row is concurrently deleted +permutation lock2 insert1 delete2 c2 select1 c1