diff options
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 173 |
1 files changed, 53 insertions, 120 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 6f44d71f16b..e8d655868ae 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -88,15 +88,6 @@ typedef struct ModifyTableContext */ TupleTableSlot *planSlot; - /* - * During EvalPlanQual, project and return the new version of the new - * tuple - */ - TupleTableSlot *(*GetUpdateNewTuple) (ResultRelInfo *resultRelInfo, - TupleTableSlot *epqslot, - TupleTableSlot *oldSlot, - MergeActionState *relaction); - /* MERGE specific */ MergeActionState *relaction; /* MERGE action in progress */ @@ -107,12 +98,6 @@ typedef struct ModifyTableContext TM_FailureData tmfd; /* - * The tuple produced by EvalPlanQual to retry from, if a cross-partition - * UPDATE requires it - */ - TupleTableSlot *cpUpdateRetrySlot; - - /* * The tuple projected by the INSERT's RETURNING clause, when doing a * cross-partition UPDATE */ @@ -162,10 +147,6 @@ static TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate, ResultRelInfo *targetRelInfo, TupleTableSlot *slot, ResultRelInfo **partRelInfo); -static TupleTableSlot *internalGetUpdateNewTuple(ResultRelInfo *relinfo, - TupleTableSlot *planSlot, - TupleTableSlot *oldSlot, - MergeActionState *relaction); static TupleTableSlot *ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo, @@ -179,10 +160,6 @@ static bool ExecMergeMatched(ModifyTableContext *context, static void ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo, bool canSetTag); -static TupleTableSlot *mergeGetUpdateNewTuple(ResultRelInfo *relinfo, - TupleTableSlot *planSlot, - TupleTableSlot *oldSlot, - MergeActionState *relaction); /* @@ -738,26 +715,14 @@ ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot) { + ProjectionInfo *newProj = relinfo->ri_projectNew; + ExprContext *econtext; + /* Use a few extra Asserts to protect against outside callers */ Assert(relinfo->ri_projectNewInfoValid); Assert(planSlot != NULL && !TTS_EMPTY(planSlot)); Assert(oldSlot != NULL && !TTS_EMPTY(oldSlot)); - return internalGetUpdateNewTuple(relinfo, planSlot, oldSlot, NULL); -} - -/* - * Callback for ModifyTableState->GetUpdateNewTuple for use by regular UPDATE. - */ -static TupleTableSlot * -internalGetUpdateNewTuple(ResultRelInfo *relinfo, - TupleTableSlot *planSlot, - TupleTableSlot *oldSlot, - MergeActionState *relaction) -{ - ProjectionInfo *newProj = relinfo->ri_projectNew; - ExprContext *econtext; - econtext = newProj->pi_exprContext; econtext->ecxt_outertuple = planSlot; econtext->ecxt_scantuple = oldSlot; @@ -1336,8 +1301,11 @@ ExecPendingInserts(EState *estate) static bool ExecDeletePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, - TupleTableSlot **epqreturnslot) + TupleTableSlot **epqreturnslot, TM_Result *result) { + if (result) + *result = TM_Ok; + /* BEFORE ROW DELETE triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_delete_before_row) @@ -1348,7 +1316,7 @@ ExecDeletePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, return ExecBRDeleteTriggers(context->estate, context->epqstate, resultRelInfo, tupleid, oldtuple, - epqreturnslot); + epqreturnslot, result, &context->tmfd); } return true; @@ -1465,7 +1433,7 @@ ExecDelete(ModifyTableContext *context, * done if it says we are. */ if (!ExecDeletePrologue(context, resultRelInfo, tupleid, oldtuple, - epqreturnslot)) + epqreturnslot, NULL)) return NULL; /* INSTEAD OF ROW DELETE Triggers */ @@ -1746,8 +1714,10 @@ ldelete: * * False is returned if the tuple we're trying to move is found to have been * concurrently updated. In that case, the caller must check if the updated - * tuple (in updateCxt->cpUpdateRetrySlot) still needs to be re-routed, and - * call this function again or perform a regular update accordingly. + * tuple that's returned in *retry_slot still needs to be re-routed, and call + * this function again or perform a regular update accordingly. For MERGE, + * the updated tuple is not returned in *retry_slot; it has its own retry + * logic. */ static bool ExecCrossPartitionUpdate(ModifyTableContext *context, @@ -1756,6 +1726,7 @@ ExecCrossPartitionUpdate(ModifyTableContext *context, TupleTableSlot *slot, bool canSetTag, UpdateContext *updateCxt, + TupleTableSlot **retry_slot, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel) { @@ -1766,7 +1737,7 @@ ExecCrossPartitionUpdate(ModifyTableContext *context, TupleTableSlot *epqslot = NULL; context->cpUpdateReturningSlot = NULL; - context->cpUpdateRetrySlot = NULL; + *retry_slot = NULL; /* * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row @@ -1845,9 +1816,13 @@ ExecCrossPartitionUpdate(ModifyTableContext *context, * another transaction has concurrently updated the same row, it * re-fetches the row, skips the delete, and epqslot is set to the * re-fetched tuple slot. In that case, we need to do all the checks - * again. + * again. For MERGE, we leave everything to the caller (it must do + * additional rechecking, and might end up executing a different + * action entirely). */ - if (TupIsNull(epqslot)) + if (context->relaction != NULL) + return false; + else if (TupIsNull(epqslot)) return true; else { @@ -1864,9 +1839,8 @@ ExecCrossPartitionUpdate(ModifyTableContext *context, oldSlot)) elog(ERROR, "failed to fetch tuple being updated"); /* and project the new tuple to retry the UPDATE with */ - context->cpUpdateRetrySlot = - context->GetUpdateNewTuple(resultRelInfo, epqslot, oldSlot, - context->relaction); + *retry_slot = ExecGetUpdateNewTuple(resultRelInfo, epqslot, + oldSlot); return false; } } @@ -1907,10 +1881,14 @@ ExecCrossPartitionUpdate(ModifyTableContext *context, */ static bool ExecUpdatePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, - ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot) + ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, + TM_Result *result) { Relation resultRelationDesc = resultRelInfo->ri_RelationDesc; + if (result) + *result = TM_Ok; + ExecMaterializeSlot(slot); /* @@ -1931,7 +1909,7 @@ ExecUpdatePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, return ExecBRUpdateTriggers(context->estate, context->epqstate, resultRelInfo, tupleid, oldtuple, slot, - &context->tmfd); + result, &context->tmfd); } return true; @@ -2032,7 +2010,8 @@ lreplace: */ if (partition_constraint_failed) { - TupleTableSlot *inserted_tuple; + TupleTableSlot *inserted_tuple, + *retry_slot; ResultRelInfo *insert_destrel = NULL; /* @@ -2044,6 +2023,7 @@ lreplace: if (ExecCrossPartitionUpdate(context, resultRelInfo, tupleid, oldtuple, slot, canSetTag, updateCxt, + &retry_slot, &inserted_tuple, &insert_destrel)) { @@ -2088,7 +2068,7 @@ lreplace: * ExecCrossPartitionUpdate installed an updated version of the new * tuple in the retry slot; start over. */ - slot = context->cpUpdateRetrySlot; + slot = retry_slot; goto lreplace; } @@ -2132,10 +2112,10 @@ lreplace: static void ExecUpdateEpilogue(ModifyTableContext *context, UpdateContext *updateCxt, ResultRelInfo *resultRelInfo, ItemPointer tupleid, - HeapTuple oldtuple, TupleTableSlot *slot, - List *recheckIndexes) + HeapTuple oldtuple, TupleTableSlot *slot) { ModifyTableState *mtstate = context->mtstate; + List *recheckIndexes = NIL; /* insert index entries for tuple if necessary */ if (resultRelInfo->ri_NumIndices > 0 && updateCxt->updateIndexes) @@ -2154,6 +2134,8 @@ ExecUpdateEpilogue(ModifyTableContext *context, UpdateContext *updateCxt, mtstate->mt_transition_capture, false); + list_free(recheckIndexes); + /* * Check any WITH CHECK OPTION constraints from parent views. We are * required to do this after testing all constraints and uniqueness @@ -2272,7 +2254,6 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo, EState *estate = context->estate; Relation resultRelationDesc = resultRelInfo->ri_RelationDesc; UpdateContext updateCxt = {0}; - List *recheckIndexes = NIL; TM_Result result; /* @@ -2285,7 +2266,7 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo, * Prepare for the update. This includes BEFORE ROW triggers, so we're * done if it says we are. */ - if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot)) + if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot, NULL)) return NULL; /* INSTEAD OF ROW UPDATE Triggers */ @@ -2486,9 +2467,7 @@ redo_act: (estate->es_processed)++; ExecUpdateEpilogue(context, &updateCxt, resultRelInfo, tupleid, oldtuple, - slot, recheckIndexes); - - list_free(recheckIndexes); + slot); /* Process RETURNING if present */ if (resultRelInfo->ri_projectReturning) @@ -2855,7 +2834,6 @@ lmerge_matched: { MergeActionState *relaction = (MergeActionState *) lfirst(l); CmdType commandType = relaction->mas_action->commandType; - List *recheckIndexes = NIL; TM_Result result; UpdateContext updateCxt = {0}; @@ -2902,13 +2880,10 @@ lmerge_matched: newslot = ExecProject(relaction->mas_proj); context->relaction = relaction; - context->GetUpdateNewTuple = mergeGetUpdateNewTuple; - context->cpUpdateRetrySlot = NULL; - if (!ExecUpdatePrologue(context, resultRelInfo, - tupleid, NULL, newslot)) + tupleid, NULL, newslot, &result)) { - result = TM_Ok; + /* Blocked by trigger, or concurrent update/delete */ break; } result = ExecUpdateAct(context, resultRelInfo, tupleid, NULL, @@ -2916,18 +2891,17 @@ lmerge_matched: if (result == TM_Ok && updateCxt.updated) { ExecUpdateEpilogue(context, &updateCxt, resultRelInfo, - tupleid, NULL, newslot, recheckIndexes); + tupleid, NULL, newslot); mtstate->mt_merge_updated += 1; } - break; case CMD_DELETE: context->relaction = relaction; if (!ExecDeletePrologue(context, resultRelInfo, tupleid, - NULL, NULL)) + NULL, NULL, &result)) { - result = TM_Ok; + /* Blocked by trigger, or concurrent update/delete */ break; } result = ExecDeleteAct(context, resultRelInfo, tupleid, false); @@ -2994,34 +2968,13 @@ lmerge_matched: /* * The target tuple was concurrently updated by some other - * transaction. - */ - - /* - * During an UPDATE, if cpUpdateRetrySlot is set, then - * ExecCrossPartitionUpdate() must have detected that the - * tuple was concurrently updated, so we restart the - * search for an appropriate WHEN MATCHED clause to - * process the updated tuple. - * - * In this case, ExecDelete() would already have performed - * EvalPlanQual() on the latest version of the tuple, - * which in turn would already have been loaded into - * ri_oldTupleSlot, so no need to do either of those - * things. - */ - if (commandType == CMD_UPDATE && - !TupIsNull(context->cpUpdateRetrySlot)) - goto lmerge_matched; - - /* - * Otherwise, we run the EvalPlanQual() with the new - * version of the tuple. If EvalPlanQual() does not return - * a tuple, then we switch to the NOT MATCHED list of - * actions. If it does return a tuple and the join qual is - * still satisfied, then we just need to recheck the - * MATCHED actions, starting from the top, and execute the - * first qualifying action. + * transaction. Run EvalPlanQual() with the new version of + * the tuple. If it does not return a tuple, then we + * switch to the NOT MATCHED list of actions. If it does + * return a tuple and the join qual is still satisfied, + * then we just need to recheck the MATCHED actions, + * starting from the top, and execute the first qualifying + * action. */ resultRelationDesc = resultRelInfo->ri_RelationDesc; lockmode = ExecUpdateLockMode(estate, resultRelInfo); @@ -3397,25 +3350,6 @@ ExecInitMergeTupleSlots(ModifyTableState *mtstate, } /* - * Callback for ModifyTableContext->GetUpdateNewTuple for use by MERGE. It - * computes the updated tuple by projecting from the current merge action's - * projection. - */ -static TupleTableSlot * -mergeGetUpdateNewTuple(ResultRelInfo *relinfo, - TupleTableSlot *planSlot, - TupleTableSlot *oldSlot, - MergeActionState *relaction) -{ - ExprContext *econtext = relaction->mas_proj->pi_exprContext; - - econtext->ecxt_scantuple = oldSlot; - econtext->ecxt_innertuple = planSlot; - - return ExecProject(relaction->mas_proj); -} - -/* * Process BEFORE EACH STATEMENT triggers */ static void @@ -3870,9 +3804,8 @@ ExecModifyTable(PlanState *pstate) oldSlot)) elog(ERROR, "failed to fetch tuple being updated"); } - slot = internalGetUpdateNewTuple(resultRelInfo, context.planSlot, - oldSlot, NULL); - context.GetUpdateNewTuple = internalGetUpdateNewTuple; + slot = ExecGetUpdateNewTuple(resultRelInfo, context.planSlot, + oldSlot); context.relaction = NULL; /* Now apply the update. */ |