aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeModifyTable.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r--src/backend/executor/nodeModifyTable.c202
1 files changed, 140 insertions, 62 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 9351fbcf494..4abfe82f7fb 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -36,8 +36,7 @@
* RETURNING tuple after completing each row insert, update, or delete.
* It must be called again to continue the operation. Without RETURNING,
* we just loop within the node until all the work is done, then
- * return NULL. This avoids useless call/return overhead. (MERGE does
- * not support RETURNING.)
+ * return NULL. This avoids useless call/return overhead.
*/
#include "postgres.h"
@@ -85,9 +84,6 @@ typedef struct ModifyTableContext
*/
TupleTableSlot *planSlot;
- /* MERGE specific */
- MergeActionState *relaction; /* MERGE action in progress */
-
/*
* Information about the changes that were made concurrently to a tuple
* being updated or deleted
@@ -150,14 +146,15 @@ static TupleTableSlot *ExecMerge(ModifyTableContext *context,
HeapTuple oldtuple,
bool canSetTag);
static void ExecInitMerge(ModifyTableState *mtstate, EState *estate);
-static bool ExecMergeMatched(ModifyTableContext *context,
- ResultRelInfo *resultRelInfo,
- ItemPointer tupleid,
- HeapTuple oldtuple,
- bool canSetTag);
-static void ExecMergeNotMatched(ModifyTableContext *context,
- ResultRelInfo *resultRelInfo,
- bool canSetTag);
+static TupleTableSlot *ExecMergeMatched(ModifyTableContext *context,
+ ResultRelInfo *resultRelInfo,
+ ItemPointer tupleid,
+ HeapTuple oldtuple,
+ bool canSetTag,
+ bool *matched);
+static TupleTableSlot *ExecMergeNotMatched(ModifyTableContext *context,
+ ResultRelInfo *resultRelInfo,
+ bool canSetTag);
/*
@@ -977,7 +974,7 @@ ExecInsert(ModifyTableContext *context,
if (mtstate->operation == CMD_UPDATE)
wco_kind = WCO_RLS_UPDATE_CHECK;
else if (mtstate->operation == CMD_MERGE)
- wco_kind = (context->relaction->mas_action->commandType == CMD_UPDATE) ?
+ wco_kind = (mtstate->mt_merge_action->mas_action->commandType == CMD_UPDATE) ?
WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK;
else
wco_kind = WCO_RLS_INSERT_CHECK;
@@ -1831,7 +1828,7 @@ ExecCrossPartitionUpdate(ModifyTableContext *context,
* additional rechecking, and might end up executing a different
* action entirely).
*/
- if (context->relaction != NULL)
+ if (mtstate->operation == CMD_MERGE)
return *tmresult == TM_Ok;
else if (TupIsNull(epqslot))
return true;
@@ -2072,7 +2069,7 @@ lreplace:
* No luck, a retry is needed. If running MERGE, we do not do so
* here; instead let it handle that on its own rules.
*/
- if (context->relaction != NULL)
+ if (context->mtstate->operation == CMD_MERGE)
return result;
/*
@@ -2713,6 +2710,7 @@ static TupleTableSlot *
ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag)
{
+ TupleTableSlot *rslot = NULL;
bool matched;
/*-----
@@ -2761,19 +2759,18 @@ ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
*/
matched = tupleid != NULL || oldtuple != NULL;
if (matched)
- matched = ExecMergeMatched(context, resultRelInfo, tupleid, oldtuple,
- canSetTag);
+ rslot = ExecMergeMatched(context, resultRelInfo, tupleid, oldtuple,
+ canSetTag, &matched);
/*
- * Either we were dealing with a NOT MATCHED tuple or ExecMergeMatched()
- * returned "false", indicating the previously MATCHED tuple no longer
- * matches.
+ * Deal with the NOT MATCHED case (either a NOT MATCHED tuple from the
+ * join, or a previously MATCHED tuple for which ExecMergeMatched() set
+ * "matched" to false, indicating that it no longer matches).
*/
if (!matched)
- ExecMergeNotMatched(context, resultRelInfo, canSetTag);
+ rslot = ExecMergeNotMatched(context, resultRelInfo, canSetTag);
- /* No RETURNING support yet */
- return NULL;
+ return rslot;
}
/*
@@ -2785,8 +2782,8 @@ ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
* We start from the first WHEN MATCHED action and check if the WHEN quals
* pass, if any. If the WHEN quals for the first action do not pass, we
* check the second, then the third and so on. If we reach to the end, no
- * action is taken and we return true, indicating that no further action is
- * required for this tuple.
+ * action is taken and "matched" is set to true, indicating that no further
+ * action is required for this tuple.
*
* If we do find a qualifying action, then we attempt to execute the action.
*
@@ -2795,16 +2792,18 @@ ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
* with individual actions are evaluated by this routine via ExecQual, while
* EvalPlanQual checks for the join quals. If EvalPlanQual tells us that the
* updated tuple still passes the join quals, then we restart from the first
- * action to look for a qualifying action. Otherwise, we return false --
- * meaning that a NOT MATCHED action must now be executed for the current
- * source tuple.
+ * action to look for a qualifying action. Otherwise, "matched" is set to
+ * false -- meaning that a NOT MATCHED action must now be executed for the
+ * current source tuple.
*/
-static bool
+static TupleTableSlot *
ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
- ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag)
+ ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag,
+ bool *matched)
{
ModifyTableState *mtstate = context->mtstate;
- TupleTableSlot *newslot;
+ TupleTableSlot *newslot = NULL;
+ TupleTableSlot *rslot = NULL;
EState *estate = context->estate;
ExprContext *econtext = mtstate->ps.ps_ExprContext;
bool isNull;
@@ -2815,7 +2814,10 @@ ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
* If there are no WHEN MATCHED actions, we are done.
*/
if (resultRelInfo->ri_matchedMergeAction == NIL)
- return true;
+ {
+ *matched = true;
+ return NULL;
+ }
/*
* Make tuple and any needed join variables available to ExecQual and
@@ -2905,12 +2907,15 @@ lmerge_matched:
*/
newslot = ExecProject(relaction->mas_proj);
- context->relaction = relaction;
+ mtstate->mt_merge_action = relaction;
if (!ExecUpdatePrologue(context, resultRelInfo,
tupleid, NULL, newslot, &result))
{
if (result == TM_Ok)
- return true; /* "do nothing" */
+ {
+ *matched = true;
+ return NULL; /* "do nothing" */
+ }
break; /* concurrent update/delete */
}
@@ -2920,7 +2925,10 @@ lmerge_matched:
{
if (!ExecIRUpdateTriggers(estate, resultRelInfo,
oldtuple, newslot))
- return true; /* "do nothing" */
+ {
+ *matched = true;
+ return NULL; /* "do nothing" */
+ }
}
else
{
@@ -2933,12 +2941,15 @@ lmerge_matched:
* cross-partition update was done, then there's nothing
* else for us to do --- the UPDATE has been turned into a
* DELETE and an INSERT, and we must not perform any of
- * the usual post-update tasks.
+ * the usual post-update tasks. Also, the RETURNING tuple
+ * (if any) has been projected, so we can just return
+ * that.
*/
if (updateCxt.crossPartUpdate)
{
mtstate->mt_merge_updated += 1;
- return true;
+ *matched = true;
+ return context->cpUpdateReturningSlot;
}
}
@@ -2951,12 +2962,15 @@ lmerge_matched:
break;
case CMD_DELETE:
- context->relaction = relaction;
+ mtstate->mt_merge_action = relaction;
if (!ExecDeletePrologue(context, resultRelInfo, tupleid,
NULL, NULL, &result))
{
if (result == TM_Ok)
- return true; /* "do nothing" */
+ {
+ *matched = true;
+ return NULL; /* "do nothing" */
+ }
break; /* concurrent update/delete */
}
@@ -2966,7 +2980,10 @@ lmerge_matched:
{
if (!ExecIRDeleteTriggers(estate, resultRelInfo,
oldtuple))
- return true; /* "do nothing" */
+ {
+ *matched = true;
+ return NULL; /* "do nothing" */
+ }
}
else
result = ExecDeleteAct(context, resultRelInfo, tupleid,
@@ -3046,7 +3063,8 @@ lmerge_matched:
* If the tuple was already deleted, return to let caller
* handle it under NOT MATCHED clauses.
*/
- return false;
+ *matched = false;
+ return NULL;
case TM_Updated:
{
@@ -3092,13 +3110,19 @@ lmerge_matched:
* NOT MATCHED actions.
*/
if (TupIsNull(epqslot))
- return false;
+ {
+ *matched = false;
+ return NULL;
+ }
(void) ExecGetJunkAttribute(epqslot,
resultRelInfo->ri_RowIdAttNo,
&isNull);
if (isNull)
- return false;
+ {
+ *matched = false;
+ return NULL;
+ }
/*
* When a tuple was updated and migrated to
@@ -3133,7 +3157,8 @@ lmerge_matched:
* tuple already deleted; tell caller to run NOT
* MATCHED actions
*/
- return false;
+ *matched = false;
+ return NULL;
case TM_SelfModified:
@@ -3161,13 +3186,13 @@ lmerge_matched:
/* This shouldn't happen */
elog(ERROR, "attempted to update or delete invisible tuple");
- return false;
+ return NULL;
default:
/* see table_tuple_lock call in ExecDelete() */
elog(ERROR, "unexpected table_tuple_lock status: %u",
result);
- return false;
+ return NULL;
}
}
@@ -3179,6 +3204,31 @@ lmerge_matched:
break;
}
+ /* Process RETURNING if present */
+ if (resultRelInfo->ri_projectReturning)
+ {
+ switch (commandType)
+ {
+ case CMD_UPDATE:
+ rslot = ExecProcessReturning(resultRelInfo, newslot,
+ context->planSlot);
+ break;
+
+ case CMD_DELETE:
+ rslot = ExecProcessReturning(resultRelInfo,
+ resultRelInfo->ri_oldTupleSlot,
+ context->planSlot);
+ break;
+
+ case CMD_NOTHING:
+ break;
+
+ default:
+ elog(ERROR, "unrecognized commandType: %d",
+ (int) commandType);
+ }
+ }
+
/*
* We've activated one of the WHEN clauses, so we don't search
* further. This is required behaviour, not an optimization.
@@ -3189,19 +3239,22 @@ lmerge_matched:
/*
* Successfully executed an action or no qualifying action was found.
*/
- return true;
+ *matched = true;
+
+ return rslot;
}
/*
* Execute the first qualifying NOT MATCHED action.
*/
-static void
+static TupleTableSlot *
ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
bool canSetTag)
{
ModifyTableState *mtstate = context->mtstate;
ExprContext *econtext = mtstate->ps.ps_ExprContext;
List *actionStates = NIL;
+ TupleTableSlot *rslot = NULL;
ListCell *l;
/*
@@ -3251,10 +3304,10 @@ ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
* so we don't need to map the tuple here.
*/
newslot = ExecProject(action->mas_proj);
- context->relaction = action;
+ mtstate->mt_merge_action = action;
- (void) ExecInsert(context, mtstate->rootResultRelInfo, newslot,
- canSetTag, NULL, NULL);
+ rslot = ExecInsert(context, mtstate->rootResultRelInfo,
+ newslot, canSetTag, NULL, NULL);
mtstate->mt_merge_inserted += 1;
break;
case CMD_NOTHING:
@@ -3270,6 +3323,8 @@ ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
*/
break;
}
+
+ return rslot;
}
/*
@@ -3732,9 +3787,17 @@ ExecModifyTable(PlanState *pstate)
{
EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
- ExecMerge(&context, node->resultRelInfo, NULL, NULL,
- node->canSetTag);
- continue; /* no RETURNING support yet */
+ slot = ExecMerge(&context, node->resultRelInfo,
+ NULL, NULL, node->canSetTag);
+
+ /*
+ * If we got a RETURNING result, return it to the caller.
+ * We'll continue the work on next call.
+ */
+ if (slot)
+ return slot;
+
+ continue; /* continue with the next tuple */
}
elog(ERROR, "tableoid is NULL");
@@ -3811,9 +3874,17 @@ ExecModifyTable(PlanState *pstate)
{
EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
- ExecMerge(&context, node->resultRelInfo, NULL, NULL,
- node->canSetTag);
- continue; /* no RETURNING support yet */
+ slot = ExecMerge(&context, node->resultRelInfo,
+ NULL, NULL, node->canSetTag);
+
+ /*
+ * If we got a RETURNING result, return it to the
+ * caller. We'll continue the work on next call.
+ */
+ if (slot)
+ return slot;
+
+ continue; /* continue with the next tuple */
}
elog(ERROR, "ctid is NULL");
@@ -3860,9 +3931,17 @@ ExecModifyTable(PlanState *pstate)
{
EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
- ExecMerge(&context, node->resultRelInfo, NULL, NULL,
- node->canSetTag);
- continue; /* no RETURNING support yet */
+ slot = ExecMerge(&context, node->resultRelInfo,
+ NULL, NULL, node->canSetTag);
+
+ /*
+ * If we got a RETURNING result, return it to the
+ * caller. We'll continue the work on next call.
+ */
+ if (slot)
+ return slot;
+
+ continue; /* continue with the next tuple */
}
elog(ERROR, "wholerow is NULL");
@@ -3924,7 +4003,6 @@ ExecModifyTable(PlanState *pstate)
}
slot = ExecGetUpdateNewTuple(resultRelInfo, context.planSlot,
oldSlot);
- context.relaction = NULL;
/* Now apply the update. */
slot = ExecUpdate(&context, resultRelInfo, tupleid, oldtuple,