diff options
author | Dean Rasheed <dean.a.rasheed@gmail.com> | 2023-12-21 12:55:22 +0000 |
---|---|---|
committer | Dean Rasheed <dean.a.rasheed@gmail.com> | 2023-12-21 12:55:22 +0000 |
commit | a0ff37173d7d412d53bc299fc611068be3bc7060 (patch) | |
tree | fb4785d3bad909871712263096d6a356d7a613b2 /src/backend/executor/nodeModifyTable.c | |
parent | e557db106ef69413edb75c362191084ee73a0f55 (diff) | |
download | postgresql-a0ff37173d7d412d53bc299fc611068be3bc7060.tar.gz postgresql-a0ff37173d7d412d53bc299fc611068be3bc7060.zip |
Fix BEFORE ROW trigger handling in cross-partition MERGE update.
Fix a bug during MERGE if a cross-partition update is attempted on a
partitioned table with a BEFORE DELETE ROW trigger that returns NULL,
to prevent the update. This would cause an error to be thrown, or an
assert failure in an assert-enabled build.
This was an oversight in 9321c79c86, which failed to properly
distinguish a DELETE prevented by a trigger from one prevented by a
concurrent update. Fix by having ExecDelete() return the TM_Result
status to ExecCrossPartitionUpdate(), so that it can distinguish the
two cases, and make ExecCrossPartitionUpdate() return the TM_Result
status to ExecUpdateAct(), so that it can return the correct status
from a concurrent update.
In addition, ensure that the command tag is correctly updated by
having ExecMergeMatched() pass canSetTag to ExecUpdateAct(), rather
than passing false, so that it updates the command tag if it does a
cross-partition update, making this code path in ExecMergeMatched()
consistent with ExecUpdate().
Per bug #18238 from Alexander Lakhin. Back-patch to v15, where MERGE
was introduced.
Dean Rasheed, reviewed by Richard Guo and Jian He.
Discussion: https://postgr.es/m/18238-2f2bdc7f720180b9%40postgresql.org
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 20 |
1 files changed, 12 insertions, 8 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index b16fbe9e22a..ee7e666171d 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1425,6 +1425,7 @@ ExecDelete(ModifyTableContext *context, bool processReturning, bool changingPart, bool canSetTag, + TM_Result *tmresult, bool *tupleDeleted, TupleTableSlot **epqreturnslot) { @@ -1441,7 +1442,7 @@ ExecDelete(ModifyTableContext *context, * done if it says we are. */ if (!ExecDeletePrologue(context, resultRelInfo, tupleid, oldtuple, - epqreturnslot, NULL)) + epqreturnslot, tmresult)) return NULL; /* INSTEAD OF ROW DELETE Triggers */ @@ -1496,6 +1497,9 @@ ExecDelete(ModifyTableContext *context, ldelete: result = ExecDeleteAct(context, resultRelInfo, tupleid, changingPart); + if (tmresult) + *tmresult = result; + switch (result) { case TM_SelfModified: @@ -1734,6 +1738,7 @@ ExecCrossPartitionUpdate(ModifyTableContext *context, TupleTableSlot *slot, bool canSetTag, UpdateContext *updateCxt, + TM_Result *tmresult, TupleTableSlot **retry_slot, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel) @@ -1797,7 +1802,7 @@ ExecCrossPartitionUpdate(ModifyTableContext *context, false, /* processReturning */ true, /* changingPart */ false, /* canSetTag */ - &tuple_deleted, &epqslot); + tmresult, &tuple_deleted, &epqslot); /* * For some reason if DELETE didn't happen (e.g. trigger prevented it, or @@ -1829,7 +1834,7 @@ ExecCrossPartitionUpdate(ModifyTableContext *context, * action entirely). */ if (context->relaction != NULL) - return false; + return *tmresult == TM_Ok; else if (TupIsNull(epqslot)) return true; else @@ -2031,6 +2036,7 @@ lreplace: if (ExecCrossPartitionUpdate(context, resultRelInfo, tupleid, oldtuple, slot, canSetTag, updateCxt, + &result, &retry_slot, &inserted_tuple, &insert_destrel)) @@ -2070,7 +2076,7 @@ lreplace: * here; instead let it handle that on its own rules. */ if (context->relaction != NULL) - return TM_Updated; + return result; /* * ExecCrossPartitionUpdate installed an updated version of the new @@ -2898,7 +2904,7 @@ lmerge_matched: break; /* concurrent update/delete */ } result = ExecUpdateAct(context, resultRelInfo, tupleid, NULL, - newslot, false, &updateCxt); + newslot, canSetTag, &updateCxt); /* * As in ExecUpdate(), if ExecUpdateAct() reports that a @@ -2910,8 +2916,6 @@ lmerge_matched: if (updateCxt.crossPartUpdate) { mtstate->mt_merge_updated += 1; - if (canSetTag) - (estate->es_processed)++; return true; } @@ -3843,7 +3847,7 @@ ExecModifyTable(PlanState *pstate) case CMD_DELETE: slot = ExecDelete(&context, resultRelInfo, tupleid, oldtuple, - true, false, node->canSetTag, NULL, NULL); + true, false, node->canSetTag, NULL, NULL, NULL); break; case CMD_MERGE: |