Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7d9a757

Browse files
committed
Fix concurrent update issues with MERGE.
If MERGE attempts an UPDATE or DELETE on a table with BEFORE ROWtriggers, or a cross-partition UPDATE (with or without triggers), anda concurrent UPDATE or DELETE happens, the merge code would fail.In some cases this would lead to a crash, while in others it wouldcause the wrong merge action to be executed, or no action at all. Theimmediate cause of the crash was the trigger code callingExecGetUpdateNewTuple() as part of the EPQ mechanism, which failsbecause during a merge ri_projectNew is NULL, since merge has its ownper-action projection information, which ExecGetUpdateNewTuple() knowsnothing about.Fix by arranging for the trigger code to exit early, returning theTM_Result and TM_FailureData information, if a concurrent modificationis detected, allowing the merge code to do the necessary EPQ handlingin its own way. Similarly, prevent the cross-partition update codefrom doing any EPQ processing for a merge, allowing the merge code towork out what it needs to do.This leads to a number of simplifications in nodeModifyTable.c. Mostnotably, the ModifyTableContext->GetUpdateNewTuple() callback is nolonger needed, and mergeGetUpdateNewTuple() can be deleted, sincethere is no longer any requirement for get-update-new-tuple during amerge. Similarly, ModifyTableContext->cpUpdateRetrySlot is no longerneeded. Thus ExecGetUpdateNewTuple() and the retry_slot handling ofExecCrossPartitionUpdate() can be restored to how they were in v14,before the merge code was added, and ExecMergeMatched() no longerneeds any special-case handling for cross-partition updates.While at it, tidy up ExecUpdateEpilogue() a bit, making it handlerecheckIndexes locally, rather than passing it in as a parameter,ensuring that it is freed properly. This dates back to when it wassplit off from ExecUpdate() to support merge.Per bug #17809 from Alexander Lakhin, and follow-up investigation ofbug #17792, also from Alexander Lakhin.Back-patch to v15, where MERGE was introduced, taking care to preservebackwards-compatibility of the trigger API in v15 for any extensionsthat might use it.Discussion:https://postgr.es/m/17809-9e6650bef133f0fe%40postgresql.orghttps://postgr.es/m/17792-0f89452029662c36%40postgresql.org
1 parent4493256 commit7d9a757

File tree

7 files changed

+693
-187
lines changed

7 files changed

+693
-187
lines changed

‎src/backend/commands/trigger.c

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ static bool GetTupleForTrigger(EState *estate,
8484
ItemPointertid,
8585
LockTupleModelockmode,
8686
TupleTableSlot*oldslot,
87-
TupleTableSlot**newSlot,
88-
TM_FailureData*tmfpd);
87+
TupleTableSlot**epqslot,
88+
TM_Result*tmresultp,
89+
TM_FailureData*tmfdp);
8990
staticboolTriggerEnabled(EState*estate,ResultRelInfo*relinfo,
9091
Trigger*trigger,TriggerEventevent,
9192
Bitmapset*modifiedCols,
@@ -2753,11 +2754,13 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
27532754
* back the concurrently updated tuple if any.
27542755
*/
27552756
bool
2756-
ExecBRDeleteTriggers(EState*estate,EPQState*epqstate,
2757-
ResultRelInfo*relinfo,
2758-
ItemPointertupleid,
2759-
HeapTuplefdw_trigtuple,
2760-
TupleTableSlot**epqslot)
2757+
ExecBRDeleteTriggersNew(EState*estate,EPQState*epqstate,
2758+
ResultRelInfo*relinfo,
2759+
ItemPointertupleid,
2760+
HeapTuplefdw_trigtuple,
2761+
TupleTableSlot**epqslot,
2762+
TM_Result*tmresult,
2763+
TM_FailureData*tmfd)
27612764
{
27622765
TupleTableSlot*slot=ExecGetTriggerOldSlot(estate,relinfo);
27632766
TriggerDesc*trigdesc=relinfo->ri_TrigDesc;
@@ -2774,7 +2777,7 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
27742777

27752778
if (!GetTupleForTrigger(estate,epqstate,relinfo,tupleid,
27762779
LockTupleExclusive,slot,&epqslot_candidate,
2777-
NULL))
2780+
tmresult,tmfd))
27782781
return false;
27792782

27802783
/*
@@ -2837,6 +2840,21 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
28372840
returnresult;
28382841
}
28392842

2843+
/*
2844+
* ABI-compatible wrapper to emulate old version of the above function.
2845+
* Do not call this version in new code.
2846+
*/
2847+
bool
2848+
ExecBRDeleteTriggers(EState*estate,EPQState*epqstate,
2849+
ResultRelInfo*relinfo,
2850+
ItemPointertupleid,
2851+
HeapTuplefdw_trigtuple,
2852+
TupleTableSlot**epqslot)
2853+
{
2854+
returnExecBRDeleteTriggersNew(estate,epqstate,relinfo,tupleid,
2855+
fdw_trigtuple,epqslot,NULL,NULL);
2856+
}
2857+
28402858
/*
28412859
* Note: is_crosspart_update must be true if the DELETE is being performed
28422860
* as part of a cross-partition update.
@@ -2865,6 +2883,7 @@ ExecARDeleteTriggers(EState *estate,
28652883
LockTupleExclusive,
28662884
slot,
28672885
NULL,
2886+
NULL,
28682887
NULL);
28692888
else
28702889
ExecForceStoreHeapTuple(fdw_trigtuple,slot, false);
@@ -3001,12 +3020,13 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
30013020
}
30023021

30033022
bool
3004-
ExecBRUpdateTriggers(EState*estate,EPQState*epqstate,
3005-
ResultRelInfo*relinfo,
3006-
ItemPointertupleid,
3007-
HeapTuplefdw_trigtuple,
3008-
TupleTableSlot*newslot,
3009-
TM_FailureData*tmfd)
3023+
ExecBRUpdateTriggersNew(EState*estate,EPQState*epqstate,
3024+
ResultRelInfo*relinfo,
3025+
ItemPointertupleid,
3026+
HeapTuplefdw_trigtuple,
3027+
TupleTableSlot*newslot,
3028+
TM_Result*tmresult,
3029+
TM_FailureData*tmfd)
30103030
{
30113031
TriggerDesc*trigdesc=relinfo->ri_TrigDesc;
30123032
TupleTableSlot*oldslot=ExecGetTriggerOldSlot(estate,relinfo);
@@ -3030,7 +3050,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
30303050
/* get a copy of the on-disk tuple we are planning to update */
30313051
if (!GetTupleForTrigger(estate,epqstate,relinfo,tupleid,
30323052
lockmode,oldslot,&epqslot_candidate,
3033-
tmfd))
3053+
tmresult,tmfd))
30343054
return false;/* cancel the update action */
30353055

30363056
/*
@@ -3134,6 +3154,22 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
31343154
return true;
31353155
}
31363156

3157+
/*
3158+
* ABI-compatible wrapper to emulate old version of the above function.
3159+
* Do not call this version in new code.
3160+
*/
3161+
bool
3162+
ExecBRUpdateTriggers(EState*estate,EPQState*epqstate,
3163+
ResultRelInfo*relinfo,
3164+
ItemPointertupleid,
3165+
HeapTuplefdw_trigtuple,
3166+
TupleTableSlot*newslot,
3167+
TM_FailureData*tmfd)
3168+
{
3169+
returnExecBRUpdateTriggersNew(estate,epqstate,relinfo,tupleid,
3170+
fdw_trigtuple,newslot,NULL,tmfd);
3171+
}
3172+
31373173
/*
31383174
* Note: 'src_partinfo' and 'dst_partinfo', when non-NULL, refer to the source
31393175
* and destination partitions, respectively, of a cross-partition update of
@@ -3185,6 +3221,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
31853221
LockTupleExclusive,
31863222
oldslot,
31873223
NULL,
3224+
NULL,
31883225
NULL);
31893226
elseif (fdw_trigtuple!=NULL)
31903227
ExecForceStoreHeapTuple(fdw_trigtuple,oldslot, false);
@@ -3340,6 +3377,7 @@ GetTupleForTrigger(EState *estate,
33403377
LockTupleModelockmode,
33413378
TupleTableSlot*oldslot,
33423379
TupleTableSlot**epqslot,
3380+
TM_Result*tmresultp,
33433381
TM_FailureData*tmfdp)
33443382
{
33453383
Relationrelation=relinfo->ri_RelationDesc;
@@ -3367,6 +3405,8 @@ GetTupleForTrigger(EState *estate,
33673405
&tmfd);
33683406

33693407
/* Let the caller know about the status of this operation */
3408+
if (tmresultp)
3409+
*tmresultp=test;
33703410
if (tmfdp)
33713411
*tmfdp=tmfd;
33723412

@@ -3394,6 +3434,18 @@ GetTupleForTrigger(EState *estate,
33943434
caseTM_Ok:
33953435
if (tmfd.traversed)
33963436
{
3437+
/*
3438+
* Recheck the tuple using EPQ. For MERGE, we leave this
3439+
* to the caller (it must do additional rechecking, and
3440+
* might end up executing a different action entirely).
3441+
*/
3442+
if (estate->es_plannedstmt->commandType==CMD_MERGE)
3443+
{
3444+
if (tmresultp)
3445+
*tmresultp=TM_Updated;
3446+
return false;
3447+
}
3448+
33973449
*epqslot=EvalPlanQual(epqstate,
33983450
relation,
33993451
relinfo->ri_RangeTableIndex,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp