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.c125
1 files changed, 75 insertions, 50 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 6f0543af832..6f44d71f16b 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -356,9 +356,14 @@ ExecCheckTIDVisible(EState *estate,
/*
* Initialize to compute stored generated columns for a tuple
*
- * This fills the resultRelInfo's ri_GeneratedExprs and ri_extraUpdatedCols
- * fields. (Currently, ri_extraUpdatedCols is consulted only in UPDATE,
- * but we might as well fill it for INSERT too.)
+ * This fills the resultRelInfo's ri_GeneratedExprsI/ri_NumGeneratedNeededI
+ * or ri_GeneratedExprsU/ri_NumGeneratedNeededU fields, depending on cmdtype.
+ * If cmdType == CMD_UPDATE, the ri_extraUpdatedCols field is filled too.
+ *
+ * Note: usually, a given query would need only one of ri_GeneratedExprsI and
+ * ri_GeneratedExprsU per result rel; but MERGE can need both, and so can
+ * cross-partition UPDATEs, since a partition might be the target of both
+ * UPDATE and INSERT actions.
*/
void
ExecInitStoredGenerated(ResultRelInfo *resultRelInfo,
@@ -368,12 +373,11 @@ ExecInitStoredGenerated(ResultRelInfo *resultRelInfo,
Relation rel = resultRelInfo->ri_RelationDesc;
TupleDesc tupdesc = RelationGetDescr(rel);
int natts = tupdesc->natts;
+ ExprState **ri_GeneratedExprs;
+ int ri_NumGeneratedNeeded;
Bitmapset *updatedCols;
MemoryContext oldContext;
- /* Don't call twice */
- Assert(resultRelInfo->ri_GeneratedExprs == NULL);
-
/* Nothing to do if no generated columns */
if (!(tupdesc->constr && tupdesc->constr->has_generated_stored))
return;
@@ -396,9 +400,8 @@ ExecInitStoredGenerated(ResultRelInfo *resultRelInfo,
*/
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
- resultRelInfo->ri_GeneratedExprs =
- (ExprState **) palloc0(natts * sizeof(ExprState *));
- resultRelInfo->ri_NumGeneratedNeeded = 0;
+ ri_GeneratedExprs = (ExprState **) palloc0(natts * sizeof(ExprState *));
+ ri_NumGeneratedNeeded = 0;
for (int i = 0; i < natts; i++)
{
@@ -427,16 +430,35 @@ ExecInitStoredGenerated(ResultRelInfo *resultRelInfo,
}
/* No luck, so prepare the expression for execution */
- resultRelInfo->ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
- resultRelInfo->ri_NumGeneratedNeeded++;
-
- /* And mark this column in resultRelInfo->ri_extraUpdatedCols */
- resultRelInfo->ri_extraUpdatedCols =
- bms_add_member(resultRelInfo->ri_extraUpdatedCols,
- i + 1 - FirstLowInvalidHeapAttributeNumber);
+ ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
+ ri_NumGeneratedNeeded++;
+
+ /* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
+ if (cmdtype == CMD_UPDATE)
+ resultRelInfo->ri_extraUpdatedCols =
+ bms_add_member(resultRelInfo->ri_extraUpdatedCols,
+ i + 1 - FirstLowInvalidHeapAttributeNumber);
}
}
+ /* Save in appropriate set of fields */
+ if (cmdtype == CMD_UPDATE)
+ {
+ /* Don't call twice */
+ Assert(resultRelInfo->ri_GeneratedExprsU == NULL);
+
+ resultRelInfo->ri_GeneratedExprsU = ri_GeneratedExprs;
+ resultRelInfo->ri_NumGeneratedNeededU = ri_NumGeneratedNeeded;
+ }
+ else
+ {
+ /* Don't call twice */
+ Assert(resultRelInfo->ri_GeneratedExprsI == NULL);
+
+ resultRelInfo->ri_GeneratedExprsI = ri_GeneratedExprs;
+ resultRelInfo->ri_NumGeneratedNeededI = ri_NumGeneratedNeeded;
+ }
+
MemoryContextSwitchTo(oldContext);
}
@@ -452,6 +474,7 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
TupleDesc tupdesc = RelationGetDescr(rel);
int natts = tupdesc->natts;
ExprContext *econtext = GetPerTupleExprContext(estate);
+ ExprState **ri_GeneratedExprs;
MemoryContext oldContext;
Datum *values;
bool *nulls;
@@ -460,20 +483,25 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
/*
- * For relations named directly in the query, ExecInitStoredGenerated
- * should have been called already; but this might not have happened yet
- * for a partition child rel. Also, it's convenient for outside callers
- * to not have to call ExecInitStoredGenerated explicitly.
- */
- if (resultRelInfo->ri_GeneratedExprs == NULL)
- ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
-
- /*
- * If no generated columns have been affected by this change, then skip
- * the rest.
+ * Initialize the expressions if we didn't already, and check whether we
+ * can exit early because nothing needs to be computed.
*/
- if (resultRelInfo->ri_NumGeneratedNeeded == 0)
- return;
+ if (cmdtype == CMD_UPDATE)
+ {
+ if (resultRelInfo->ri_GeneratedExprsU == NULL)
+ ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
+ if (resultRelInfo->ri_NumGeneratedNeededU == 0)
+ return;
+ ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
+ }
+ else
+ {
+ if (resultRelInfo->ri_GeneratedExprsI == NULL)
+ ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
+ /* Early exit is impossible given the prior Assert */
+ Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
+ ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
+ }
oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
@@ -487,7 +515,7 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
{
Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
- if (resultRelInfo->ri_GeneratedExprs[i])
+ if (ri_GeneratedExprs[i])
{
Datum val;
bool isnull;
@@ -496,7 +524,7 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
econtext->ecxt_scantuple = slot;
- val = ExecEvalExpr(resultRelInfo->ri_GeneratedExprs[i], econtext, &isnull);
+ val = ExecEvalExpr(ri_GeneratedExprs[i], econtext, &isnull);
/*
* We must make a copy of val as we have no guarantees about where
@@ -1910,9 +1938,10 @@ ExecUpdatePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
}
/*
- * ExecUpdatePrepareSlot -- subroutine for ExecUpdate
+ * ExecUpdatePrepareSlot -- subroutine for ExecUpdateAct
*
* Apply the final modifications to the tuple slot before the update.
+ * (This is split out because we also need it in the foreign-table code path.)
*/
static void
ExecUpdatePrepareSlot(ResultRelInfo *resultRelInfo,
@@ -1962,13 +1991,14 @@ ExecUpdateAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
updateCxt->crossPartUpdate = false;
/*
- * If we generate a new candidate tuple after EvalPlanQual testing, we
- * must loop back here and recheck any RLS policies and constraints. (We
- * don't need to redo triggers, however. If there are any BEFORE triggers
- * then trigger.c will have done table_tuple_lock to lock the correct
- * tuple, so there's no need to do them again.)
+ * If we move the tuple to a new partition, we loop back here to recompute
+ * GENERATED values (which are allowed to be different across partitions)
+ * and recheck any RLS policies and constraints. We do not fire any
+ * BEFORE triggers of the new partition, however.
*/
lreplace:
+ /* Fill in GENERATEd columns */
+ ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
/* ensure slot is independent, consider e.g. EPQ */
ExecMaterializeSlot(slot);
@@ -2268,6 +2298,7 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
}
else if (resultRelInfo->ri_FdwRoutine)
{
+ /* Fill in GENERATEd columns */
ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
/*
@@ -2290,9 +2321,13 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
}
else
{
- /* Fill in the slot appropriately */
- ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
-
+ /*
+ * If we generate a new candidate tuple after EvalPlanQual testing, we
+ * must loop back here to try again. (We don't need to redo triggers,
+ * however. If there are any BEFORE triggers then trigger.c will have
+ * done table_tuple_lock to lock the correct tuple, so there's no need
+ * to do them again.)
+ */
redo_act:
result = ExecUpdateAct(context, resultRelInfo, tupleid, oldtuple, slot,
canSetTag, &updateCxt);
@@ -2876,7 +2911,6 @@ lmerge_matched:
result = TM_Ok;
break;
}
- ExecUpdatePrepareSlot(resultRelInfo, newslot, context->estate);
result = ExecUpdateAct(context, resultRelInfo, tupleid, NULL,
newslot, false, &updateCxt);
if (result == TM_Ok && updateCxt.updated)
@@ -4135,15 +4169,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
elog(ERROR, "could not find junk wholerow column");
}
}
-
- /*
- * For INSERT/UPDATE/MERGE, prepare to evaluate any generated columns.
- * We must do this now, even if we never insert or update any rows,
- * because we have to fill resultRelInfo->ri_extraUpdatedCols for
- * possible use by the trigger machinery.
- */
- if (operation == CMD_INSERT || operation == CMD_UPDATE || operation == CMD_MERGE)
- ExecInitStoredGenerated(resultRelInfo, estate, operation);
}
/*