summaryrefslogtreecommitdiff
path: root/src/backend/commands/trigger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r--src/backend/commands/trigger.c134
1 files changed, 96 insertions, 38 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index ea5cc10919d..ad28abe085f 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -79,6 +79,7 @@ static bool GetTupleForTrigger(EState *estate,
ItemPointer tid,
LockTupleMode lockmode,
TupleTableSlot *oldslot,
+ bool do_epq_recheck,
TupleTableSlot **epqslot,
TM_Result *tmresultp,
TM_FailureData *tmfdp);
@@ -2679,13 +2680,14 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
* back the concurrently updated tuple if any.
*/
bool
-ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
- ResultRelInfo *relinfo,
- ItemPointer tupleid,
- HeapTuple fdw_trigtuple,
- TupleTableSlot **epqslot,
- TM_Result *tmresult,
- TM_FailureData *tmfd)
+ExecBRDeleteTriggersNew(EState *estate, EPQState *epqstate,
+ ResultRelInfo *relinfo,
+ ItemPointer tupleid,
+ HeapTuple fdw_trigtuple,
+ TupleTableSlot **epqslot,
+ TM_Result *tmresult,
+ TM_FailureData *tmfd,
+ bool is_merge_delete)
{
TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
@@ -2700,9 +2702,17 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
{
TupleTableSlot *epqslot_candidate = NULL;
+ /*
+ * Get a copy of the on-disk tuple we are planning to delete. In
+ * general, if the tuple has been concurrently updated, we should
+ * recheck it using EPQ. However, if this is a MERGE DELETE action,
+ * we skip this EPQ recheck and leave it to the caller (it must do
+ * additional rechecking, and might end up executing a different
+ * action entirely).
+ */
if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
- LockTupleExclusive, slot, &epqslot_candidate,
- tmresult, tmfd))
+ LockTupleExclusive, slot, !is_merge_delete,
+ &epqslot_candidate, tmresult, tmfd))
return false;
/*
@@ -2766,6 +2776,24 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
}
/*
+ * ABI-compatible wrapper to emulate old version of the above function.
+ * Do not call this version in new code.
+ */
+bool
+ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
+ ResultRelInfo *relinfo,
+ ItemPointer tupleid,
+ HeapTuple fdw_trigtuple,
+ TupleTableSlot **epqslot,
+ TM_Result *tmresult,
+ TM_FailureData *tmfd)
+{
+ return ExecBRDeleteTriggersNew(estate, epqstate, relinfo, tupleid,
+ fdw_trigtuple, epqslot, tmresult, tmfd,
+ false);
+}
+
+/*
* Note: is_crosspart_update must be true if the DELETE is being performed
* as part of a cross-partition update.
*/
@@ -2792,6 +2820,7 @@ ExecARDeleteTriggers(EState *estate,
tupleid,
LockTupleExclusive,
slot,
+ false,
NULL,
NULL,
NULL);
@@ -2930,13 +2959,14 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
}
bool
-ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
- ResultRelInfo *relinfo,
- ItemPointer tupleid,
- HeapTuple fdw_trigtuple,
- TupleTableSlot *newslot,
- TM_Result *tmresult,
- TM_FailureData *tmfd)
+ExecBRUpdateTriggersNew(EState *estate, EPQState *epqstate,
+ ResultRelInfo *relinfo,
+ ItemPointer tupleid,
+ HeapTuple fdw_trigtuple,
+ TupleTableSlot *newslot,
+ TM_Result *tmresult,
+ TM_FailureData *tmfd,
+ bool is_merge_update)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
@@ -2957,10 +2987,17 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
{
TupleTableSlot *epqslot_candidate = NULL;
- /* get a copy of the on-disk tuple we are planning to update */
+ /*
+ * Get a copy of the on-disk tuple we are planning to update. In
+ * general, if the tuple has been concurrently updated, we should
+ * recheck it using EPQ. However, if this is a MERGE UPDATE action,
+ * we skip this EPQ recheck and leave it to the caller (it must do
+ * additional rechecking, and might end up executing a different
+ * action entirely).
+ */
if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
- lockmode, oldslot, &epqslot_candidate,
- tmresult, tmfd))
+ lockmode, oldslot, !is_merge_update,
+ &epqslot_candidate, tmresult, tmfd))
return false; /* cancel the update action */
/*
@@ -3083,6 +3120,24 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
}
/*
+ * ABI-compatible wrapper to emulate old version of the above function.
+ * Do not call this version in new code.
+ */
+bool
+ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
+ ResultRelInfo *relinfo,
+ ItemPointer tupleid,
+ HeapTuple fdw_trigtuple,
+ TupleTableSlot *newslot,
+ TM_Result *tmresult,
+ TM_FailureData *tmfd)
+{
+ return ExecBRUpdateTriggersNew(estate, epqstate, relinfo, tupleid,
+ fdw_trigtuple, newslot, tmresult, tmfd,
+ false);
+}
+
+/*
* Note: 'src_partinfo' and 'dst_partinfo', when non-NULL, refer to the source
* and destination partitions, respectively, of a cross-partition update of
* the root partitioned table mentioned in the query, given by 'relinfo'.
@@ -3132,6 +3187,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
tupleid,
LockTupleExclusive,
oldslot,
+ false,
NULL,
NULL,
NULL);
@@ -3288,6 +3344,7 @@ GetTupleForTrigger(EState *estate,
ItemPointer tid,
LockTupleMode lockmode,
TupleTableSlot *oldslot,
+ bool do_epq_recheck,
TupleTableSlot **epqslot,
TM_Result *tmresultp,
TM_FailureData *tmfdp)
@@ -3347,29 +3404,30 @@ GetTupleForTrigger(EState *estate,
if (tmfd.traversed)
{
/*
- * Recheck the tuple using EPQ. For MERGE, we leave this
- * to the caller (it must do additional rechecking, and
- * might end up executing a different action entirely).
+ * Recheck the tuple using EPQ, if requested. Otherwise,
+ * just return that it was concurrently updated.
*/
- if (estate->es_plannedstmt->commandType == CMD_MERGE)
+ if (do_epq_recheck)
{
- if (tmresultp)
- *tmresultp = TM_Updated;
- return false;
+ *epqslot = EvalPlanQual(epqstate,
+ relation,
+ relinfo->ri_RangeTableIndex,
+ oldslot);
+
+ /*
+ * If PlanQual failed for updated tuple - we must not
+ * process this tuple!
+ */
+ if (TupIsNull(*epqslot))
+ {
+ *epqslot = NULL;
+ return false;
+ }
}
-
- *epqslot = EvalPlanQual(epqstate,
- relation,
- relinfo->ri_RangeTableIndex,
- oldslot);
-
- /*
- * If PlanQual failed for updated tuple - we must not
- * process this tuple!
- */
- if (TupIsNull(*epqslot))
+ else
{
- *epqslot = NULL;
+ if (tmresultp)
+ *tmresultp = TM_Updated;
return false;
}
}