Assert when executing query on partitioned table

Started by Dmitry Koval11 months ago5 messages
#1Dmitry Koval
d.koval@postgrespro.ru
3 attachment(s)

Hi!

I got an Assert when executing an "INSERT ... ON CONFLICT ... UPDATE
..." query on partitioned table. Managed to reproduce this situation.

Reproduction order.
-------------------

1) Apply the patch
[v1-0001-Triggering-Assert-on-query-with-ON-CONFLICT.patch] to "master"
branch.

2) Build postgres with key "--enable-injection-points" + build an
extension "injection_points" (src/test/modules/injection_points).

3) Run isolation test onconflict.spec:
make check -C src/test/modules/injection_points

Assert is triggered in postgres with stack, see attached file [stack.txt].

Clarification.
--------------
In the query "INSERT ... ON CONFLICT ... UPDATE ..." when executing
INSERT, a conflict is triggered. But when trying to execute UPDATE, our
tuple has already been moved to another partition and Assert is triggered.

Fixing.
-------
I suggest replace Assert with an error message, see
[v1-0001-draft-of-fix.patch]. This is not a final fix as I am confused
by the comment for Assert: "we don't support an UPDATE of INSERT ON
CONFLICT for a partitioned table".
(Why "don't support an UPDATE"?
It's not forbidden by syntax or errors ...)

--
With best regards,
Dmitry Koval

Postgres Professional: http://postgrespro.com

Attachments:

stack.txttext/plain; charset=UTF-8; name=stack.txtDownload
v1-0001-Triggering-Assert-on-query-with-ON-CONFLICT.patchtext/plain; charset=UTF-8; name=v1-0001-Triggering-Assert-on-query-with-ON-CONFLICT.patchDownload
From 02b89799a2f139a3cab41b1df761103cf8627098 Mon Sep 17 00:00:00 2001
From: Koval Dmitry <d.koval@postgrespro.ru>
Date: Wed, 19 Feb 2025 13:09:33 +0300
Subject: [PATCH v1] Triggering Assert on query with ON CONFLICT

---
 src/backend/executor/nodeModifyTable.c        |  2 ++
 src/test/modules/injection_points/Makefile    | 11 +++----
 .../injection_points/specs/onconflict.spec    | 30 +++++++++++++++++++
 3 files changed, 38 insertions(+), 5 deletions(-)
 create mode 100644 src/test/modules/injection_points/specs/onconflict.spec

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index b0fe50075ad..01946fdb0d8 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -67,6 +67,7 @@
 #include "storage/lmgr.h"
 #include "utils/builtins.h"
 #include "utils/datum.h"
+#include "utils/injection_point.h"
 #include "utils/rel.h"
 #include "utils/snapmgr.h"
 
@@ -1128,6 +1129,7 @@ ExecInsert(ModifyTableContext *context,
 					 */
 					TupleTableSlot *returning = NULL;
 
+					INJECTION_POINT("before-on-conflict-update");
 					if (ExecOnConflictUpdate(context, resultRelInfo,
 											 &conflictTid, slot, canSetTag,
 											 &returning))
diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile
index e680991f8d4..b1915800760 100644
--- a/src/test/modules/injection_points/Makefile
+++ b/src/test/modules/injection_points/Makefile
@@ -11,15 +11,16 @@ EXTENSION = injection_points
 DATA = injection_points--1.0.sql
 PGFILEDESC = "injection_points - facility for injection points"
 
-REGRESS = injection_points hashagg reindex_conc
-REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
+#REGRESS = injection_points hashagg reindex_conc
+#REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
 
-ISOLATION = basic inplace syscache-update-pruned
+#ISOLATION = basic inplace syscache-update-pruned
+ISOLATION = onconflict
 
-TAP_TESTS = 1
+#TAP_TESTS = 1
 
 # The injection points are cluster-wide, so disable installcheck
-NO_INSTALLCHECK = 1
+#NO_INSTALLCHECK = 1
 
 export enable_injection_points
 
diff --git a/src/test/modules/injection_points/specs/onconflict.spec b/src/test/modules/injection_points/specs/onconflict.spec
new file mode 100644
index 00000000000..cdfe7942029
--- /dev/null
+++ b/src/test/modules/injection_points/specs/onconflict.spec
@@ -0,0 +1,30 @@
+setup
+{
+  CREATE EXTENSION injection_points;
+
+  CREATE TABLE t_int (i int PRIMARY KEY, v int, x int) PARTITION BY RANGE (i);
+  CREATE TABLE t_int_1 PARTITION OF t_int FOR VALUES FROM (1) TO (100);
+  CREATE TABLE t_int_2 PARTITION OF t_int FOR VALUES FROM (100) TO (200);
+
+  INSERT INTO t_int VALUES (1, 10, 100);
+}
+
+teardown
+{
+  DROP TABLE t_int;
+  DROP EXTENSION injection_points;
+}
+
+session s1
+step s1		{ BEGIN; }
+step s1ipa	{ SELECT injection_points_attach('before-on-conflict-update', 'wait'); }
+step s1i	{ INSERT INTO t_int VALUES (1, 11, 111) ON CONFLICT (i) DO UPDATE SET x = excluded.x; }
+step s1c	{ COMMIT; }
+
+session s2
+step s2		{ BEGIN; }
+step s2ipw	{ SELECT injection_points_wakeup('before-on-conflict-update'); }
+step s2u	{ UPDATE t_int SET i = i + 150 WHERE i = 1; }
+step s2c	{ COMMIT; }
+
+permutation s1 s1ipa s1i(s2c) s2 s2u s2ipw s2c
-- 
2.34.1

v1-0001-draft-of-fix.patchtext/plain; charset=UTF-8; name=v1-0001-draft-of-fix.patchDownload
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index b0fe50075ad..4b0ae8bd32c 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2792,7 +2792,10 @@ ExecOnConflictUpdate(ModifyTableContext *context,
 			 * be lock is moved to another partition due to concurrent update
 			 * of the partition key.
 			 */
-			Assert(!ItemPointerIndicatesMovedPartitions(&tmfd.ctid));
+			if (ItemPointerIndicatesMovedPartitions(&tmfd.ctid))
+				ereport(ERROR,
+						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+						 errmsg("tuple to be updated was already moved to another partition due to concurrent update")));
 
 			/*
 			 * Tell caller to try again from the very start.
#2Joseph Koshakow
koshy44@gmail.com
In reply to: Dmitry Koval (#1)
1 attachment(s)
Re: Assert when executing query on partitioned table

On Thu, Feb 20, 2025 at 6:14 AM Dmitry Koval <d.koval@postgrespro.ru> wrote:

I got an Assert when executing an "INSERT ... ON CONFLICT ... UPDATE
..." query on partitioned table. Managed to reproduce this situation.

I was able to reproduce the assert with your instructions.

I suggest replace Assert with an error message, see
[v1-0001-draft-of-fix.patch]. This is not a final fix as I am confused
by the comment for Assert: "we don't support an UPDATE of INSERT ON
CONFLICT for a partitioned table".
(Why "don't support an UPDATE"?
It's not forbidden by syntax or errors ...)

The assert was introduced commit f16241 [0]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=f16241bef7cc271bff60e23de2f827a10e50dde8. Specifically it was added as a
result of this discussion [1]/messages/by-id/CAAJ_b95PkwojoYfz0bzXU8OokcTVGzN6vYGCNVUukeUDrnF3dw@mail.gmail.com

On Wed, Nov 29, 2017 at 7:51 AM, Amit Kapila

<amit(dot)kapila16(at)gmail(dot)com> wrote:

On Tue, Nov 28, 2017 at 5:58 PM, amul sul <sulamul(at)gmail(dot)com>

wrote:

On Sat, Nov 25, 2017 at 11:39 AM, Amit Kapila

<amit(dot)kapila16(at)gmail(dot)com> wrote:

On Thu, Nov 23, 2017 at 5:18 PM, amul sul <sulamul(at)gmail(dot)com>

wrote:

On Sat, Nov 11, 2017 at 1:05 AM, Robert Haas

<robertmhaas(at)gmail(dot)com> wrote:

On Wed, Sep 27, 2017 at 7:07 AM, amul sul <sulamul(at)gmail(dot)com>

wrote:

1.
@@ -1480,6 +1493,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+ if

(!BlockNumberIsValid(BlockIdGetBlockNumber(&((hufd.ctid).ip_blkid))))

+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("tuple to be updated was already moved to an another
partition due to concurrent update")));

Why do you think we need this check in the OnConflictUpdate path? I
think we don't it here because we are going to relinquish this version
of the tuple and will start again and might fetch some other row
version. Also, we don't support Insert .. On Conflict Update with
partitioned tables, see[1], which is also an indication that at the
very least we don't need it now.

Agreed, even though this case will never going to be anytime soon
shouldn't we have a check for invalid block id? IMHO, we should have
this check and error report or assert, thoughts?

I feel adding code which can't be hit (even if it is error handling)
is not a good idea. I think having an Assert should be okay, but
please write comments to explain the reason for adding an Assert.

Agree, updated in the attached patch.

So if I understand correctly, at the time the assert was added, we in
fact did not support UPDATE of INSERT ON CONFLICT for a partitioned
table. However, since then we've added support but nobody removed or
altered the assert.

I'm not very familiar with this code, but from that discussion it
sounds like your solution of converting the assert to an error is
correct.

I don't fully understand why an error is needed though. This specific
case will return false which will signal the caller to retry the
insert. Just as an experiment I tried deleting the assert (attached
patch) and running your test. Everything behaved as expected and
nothing blew up. It also seems to pass a CI run just fine [2]https://github.com/jkosh44/postgres/runs/38431219796. It also
passes `make check && make check-world` locally.

Hopefully someone from the original thread can shed some light on
whether an error is needed or not.

[0]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=f16241bef7cc271bff60e23de2f827a10e50dde8
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=f16241bef7cc271bff60e23de2f827a10e50dde8
[1]: /messages/by-id/CAAJ_b95PkwojoYfz0bzXU8OokcTVGzN6vYGCNVUukeUDrnF3dw@mail.gmail.com
/messages/by-id/CAAJ_b95PkwojoYfz0bzXU8OokcTVGzN6vYGCNVUukeUDrnF3dw@mail.gmail.com
[2]: https://github.com/jkosh44/postgres/runs/38431219796

Thanks,
Joseph Koshakow

Attachments:

v1-0001-Remove-assert-for-update-on-conflict.patchtext/x-patch; charset=US-ASCII; name=v1-0001-Remove-assert-for-update-on-conflict.patchDownload
From 3959bc8d53b29d3b7ba79d9cf923f9da65d43d5b Mon Sep 17 00:00:00 2001
From: Joseph Koshakow <koshy44@gmail.com>
Date: Sat, 8 Mar 2025 15:39:47 -0500
Subject: [PATCH v1] Remove assert for update on conflict

This commit removes an assert that is no longer valid. The assert was
making an assumption that INSERT .. ON CONFLIT UPDATE .. was not
allowed on partitioned tables. This assumption is no longer valid and
the assert is possible to be triggered.
---
 src/backend/executor/nodeModifyTable.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index b0fe50075a..f33a273cfa 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2786,14 +2786,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.
 			 *
-- 
2.43.0

#3Joseph Koshakow
koshy44@gmail.com
In reply to: Joseph Koshakow (#2)
Re: Assert when executing query on partitioned table

On Sat, Mar 8, 2025 at 4:28 PM Joseph Koshakow <koshy44@gmail.com> wrote:

The assert was introduced commit f16241

So if I understand correctly, at the time the assert was added, we in
fact did not support UPDATE of INSERT ON CONFLICT for a partitioned
table. However, since then we've added support but nobody removed or
altered the assert.

I tried to validate this by checking out f16241, and running the following:

psql (11devel)
Type "help" for help.

test=# CREATE TABLE t_int (i int PRIMARY KEY, v int, x int) PARTITION
BY RANGE (i);
CREATE TABLE t_int_1 PARTITION OF t_int FOR VALUES FROM (1) TO (100);
CREATE TABLE t_int_2 PARTITION OF t_int FOR VALUES FROM (100) TO
(200);

INSERT INTO t_int VALUES (1, 10, 100);
CREATE TABLE
CREATE TABLE
CREATE TABLE
INSERT 0 1
test=# INSERT INTO t_int VALUES (1, 11, 111) ON CONFLICT (i) DO UPDATE
SET x = excluded.x;
INSERT 0 1

So it looks like when that assert was added, we *did* support
INSERT .. ON CONFLICT UPDATE for partitioned tables. So I'm even more
confused about the conversation and assert. You can even update the
partitions directly.

test=# INSERT INTO t_int_1 VALUES (1, 11, 111) ON CONFLICT (i) DO
UPDATE SET x = excluded.x;
INSERT 0 1
test=# INSERT INTO t_int_2 VALUES (150, 11, 111) ON CONFLICT (i) DO
UPDATE SET x = excluded.x;
INSERT 0 1

Thanks,
Joseph Koshakow

#4Dmitry Koval
d.koval@postgrespro.ru
In reply to: Joseph Koshakow (#3)
2 attachment(s)
Re: Assert when executing query on partitioned table

Hi!

Unfortunately, the issue is still relevant, so I'm updating the first
post and patches. I got an Assert when executing an "INSERT ... ON
CONFLICT ... UPDATE ..." query on partitioned table.

Reproduction order.
-------------------

1) Apply the patch
[v1-0001-Triggering-Assert-on-query-with-ON-CONFLICT.no_cfbot]
to "master" branch:

git am v1-0001-Triggering-Assert-on-query-with-ON-CONFLICT.no_cfbot

2) Build postgres with "--enable-injection-points --enable-cassert",
for example:

./configure --enable-injection-points --enable-debug --enable-cassert

/dev/null && make -j4 -s

3) Run isolation test onconflict.spec:

make check -C src/test/modules/injection_points

Assert is triggered in postgres, with part of stack:
...
#3 0x0000624cb170e05f in ExceptionalCondition (
conditionName=conditionName@entry=0x624cb17fcbf0
"!ItemPointerIndicatesMovedPartitions(&tmfd.ctid)",
fileName=fileName@entry=0x624cb1785ea7 "nodeModifyTable.c",
lineNumber=lineNumber@entry=2819) at assert.c:65
#4 0x0000624cb13f1412 in ExecOnConflictUpdate (returning=<synthetic
pointer>, canSetTag=true, excludedSlot=0x624cb2a61750,
conflictTid=0x7fff95cd827a, resultRelInfo=0x624cb2a62420,
context=0x7fff95cd8340) at nodeModifyTable.c:2819
...

Clarification.
--------------
In the query "INSERT ... ON CONFLICT ... UPDATE ..." when executing
INSERT, a conflict is triggered. But when trying to execute UPDATE, our
tuple has already been moved to another partition and Assert is
triggered. I think this is a correct situation and in this case we
should generate an error instead of Assert.

Fixing.
-------
Patch [v1-0001-Replace-invalid-Assert-with-ereport-ERROR.patch].
For testing need to apply
[v1-0001-Replace-invalid-Assert-with-ereport-ERROR.patch]:

git am v1-0001-Replace-invalid-Assert-with-ereport-ERROR.patch

rebuild postgres (2) and run test again (3),

--
With best regards,
Dmitry Koval

Postgres Professional: http://postgrespro.com

Attachments:

v1-0001-Triggering-Assert-on-query-with-ON-CONFLICT.no_cfbottext/plain; charset=UTF-8; name=v1-0001-Triggering-Assert-on-query-with-ON-CONFLICT.no_cfbotDownload
From 8780e66fc759872e06746c9c214dec52644b2e93 Mon Sep 17 00:00:00 2001
From: Koval Dmitry <d.koval@postgrespro.ru>
Date: Wed, 19 Feb 2025 13:09:33 +0300
Subject: [PATCH v1] Triggering Assert on query with ON CONFLICT

---
 src/backend/executor/nodeModifyTable.c        |  1 +
 src/test/modules/injection_points/Makefile    |  6 +++-
 .../injection_points/expected/onconflict.out  | 23 ++++++++++++++
 .../injection_points/specs/onconflict.spec    | 30 +++++++++++++++++++
 4 files changed, 59 insertions(+), 1 deletion(-)
 create mode 100644 src/test/modules/injection_points/expected/onconflict.out
 create mode 100644 src/test/modules/injection_points/specs/onconflict.spec

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 874b71e660..d131abcd67 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1148,6 +1148,7 @@ ExecInsert(ModifyTableContext *context,
 					 */
 					TupleTableSlot *returning = NULL;
 
+					INJECTION_POINT("before-on-conflict-update", NULL);
 					if (ExecOnConflictUpdate(context, resultRelInfo,
 											 &conflictTid, slot, canSetTag,
 											 &returning))
diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile
index bfdb3f5337..5420850b39 100644
--- a/src/test/modules/injection_points/Makefile
+++ b/src/test/modules/injection_points/Makefile
@@ -9,7 +9,8 @@ EXTENSION = injection_points
 DATA = injection_points--1.0.sql
 PGFILEDESC = "injection_points - facility for injection points"
 
-REGRESS = injection_points hashagg reindex_conc vacuum
+# Disable regress tests
+#REGRESS = injection_points hashagg reindex_conc vacuum
 REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
 
 ISOLATION = basic \
@@ -24,6 +25,9 @@ ISOLATION = basic \
 #	    reindex-concurrently-upsert-on-constraint \
 #	    reindex-concurrently-upsert-partitioned
 
+# Enable only one isolation test
+ISOLATION = onconflict
+
 # The injection points are cluster-wide, so disable installcheck
 NO_INSTALLCHECK = 1
 
diff --git a/src/test/modules/injection_points/expected/onconflict.out b/src/test/modules/injection_points/expected/onconflict.out
new file mode 100644
index 0000000000..ee89965592
--- /dev/null
+++ b/src/test/modules/injection_points/expected/onconflict.out
@@ -0,0 +1,23 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1 s1ipa s1i s2 s2u s2ipw s2c s1c
+step s1: BEGIN;
+step s1ipa: SELECT injection_points_attach('before-on-conflict-update', 'wait');
+injection_points_attach
+-----------------------
+                       
+(1 row)
+
+step s1i: INSERT INTO t_int VALUES (1, 11, 111) ON CONFLICT (i) DO UPDATE SET x = excluded.x; <waiting ...>
+step s2: BEGIN;
+step s2u: UPDATE t_int SET i = i + 150 WHERE i = 1;
+step s2ipw: SELECT injection_points_wakeup('before-on-conflict-update');
+injection_points_wakeup
+-----------------------
+                       
+(1 row)
+
+step s2c: COMMIT;
+step s1i: <... completed>
+ERROR:  tuple to be updated was already moved to another partition due to concurrent update
+step s1c: COMMIT;
diff --git a/src/test/modules/injection_points/specs/onconflict.spec b/src/test/modules/injection_points/specs/onconflict.spec
new file mode 100644
index 0000000000..495449264b
--- /dev/null
+++ b/src/test/modules/injection_points/specs/onconflict.spec
@@ -0,0 +1,30 @@
+setup
+{
+  CREATE EXTENSION injection_points;
+
+  CREATE TABLE t_int (i int PRIMARY KEY, v int, x int) PARTITION BY RANGE (i);
+  CREATE TABLE t_int_1 PARTITION OF t_int FOR VALUES FROM (1) TO (100);
+  CREATE TABLE t_int_2 PARTITION OF t_int FOR VALUES FROM (100) TO (200);
+
+  INSERT INTO t_int VALUES (1, 10, 100);
+}
+
+teardown
+{
+  DROP TABLE t_int;
+  DROP EXTENSION injection_points;
+}
+
+session s1
+step s1		{ BEGIN; }
+step s1ipa	{ SELECT injection_points_attach('before-on-conflict-update', 'wait'); }
+step s1i	{ INSERT INTO t_int VALUES (1, 11, 111) ON CONFLICT (i) DO UPDATE SET x = excluded.x; }
+step s1c	{ COMMIT; }
+
+session s2
+step s2		{ BEGIN; }
+step s2ipw	{ SELECT injection_points_wakeup('before-on-conflict-update'); }
+step s2u	{ UPDATE t_int SET i = i + 150 WHERE i = 1; }
+step s2c	{ COMMIT; }
+
+permutation s1 s1ipa s1i(s2c) s2 s2u s2ipw s2c s1c
-- 
2.43.0

v1-0001-Replace-invalid-Assert-with-ereport-ERROR.patchtext/plain; charset=UTF-8; name=v1-0001-Replace-invalid-Assert-with-ereport-ERROR.patchDownload
From 50fe893680665226982ffa67bbde6c2cf6017260 Mon Sep 17 00:00:00 2001
From: Koval Dmitry <d.koval@postgrespro.ru>
Date: Fri, 19 Dec 2025 01:51:07 +0300
Subject: [PATCH v1] Replace invalid Assert with ereport(ERROR, ...)

At the time Assert was added, the UPDATE of INSERT ON CONFLICT operation
was not supported for partitioned tables. However, support has been
added, so need to replace Assert with ereport(ERROR, ...).
---
 src/backend/executor/nodeModifyTable.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index d131abcd67..f51ffbfc1e 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2810,13 +2810,10 @@ 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));
+			if (ItemPointerIndicatesMovedPartitions(&tmfd.ctid))
+				ereport(ERROR,
+						errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+						errmsg("tuple to be updated was already moved to another partition due to concurrent update"));
 
 			/*
 			 * Tell caller to try again from the very start.
-- 
2.43.0

#5Dmitry Koval
d.koval@postgrespro.ru
In reply to: Dmitry Koval (#4)
1 attachment(s)
Re: Assert when executing query on partitioned table

Hi!

I want to send the patch to Commitfest, so the version changed:
[1]: > [2] (for cfbot).

--
With best regards,
Dmitry Koval

Postgres Professional: http://postgrespro.com

Attachments:

v2-0001-Replace-invalid-Assert-with-ereport-ERROR.patchtext/plain; charset=UTF-8; name=v2-0001-Replace-invalid-Assert-with-ereport-ERROR.patchDownload
From a450b0c085bde7020c9f8056bd45adb2340afc6a Mon Sep 17 00:00:00 2001
From: Koval Dmitry <d.koval@postgrespro.ru>
Date: Fri, 19 Dec 2025 01:51:07 +0300
Subject: [PATCH v2] Replace invalid Assert with ereport(ERROR, ...)

At the time Assert was added, the UPDATE of INSERT ON CONFLICT operation
was not supported for partitioned tables. However, support has been
added, so need to replace Assert with ereport(ERROR, ...).
---
 src/backend/executor/nodeModifyTable.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index d131abcd67..f51ffbfc1e 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2810,13 +2810,10 @@ 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));
+			if (ItemPointerIndicatesMovedPartitions(&tmfd.ctid))
+				ereport(ERROR,
+						errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+						errmsg("tuple to be updated was already moved to another partition due to concurrent update"));
 
 			/*
 			 * Tell caller to try again from the very start.
-- 
2.43.0