diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 40a8ec1abd..e79ede4cb8 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -3222,7 +3222,7 @@ CopyFrom(CopyState cstate) /* Compute stored generated columns */ if (resultRelInfo->ri_RelationDesc->rd_att->constr && resultRelInfo->ri_RelationDesc->rd_att->constr->has_generated_stored) - ExecComputeStoredGenerated(estate, myslot); + ExecComputeStoredGenerated(estate, myslot, CMD_INSERT); /* * If the target is a plain table, check the constraints of diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index 30cba89da7..7194becfd9 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -419,7 +419,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot) /* Compute stored generated columns */ if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) - ExecComputeStoredGenerated(estate, slot); + ExecComputeStoredGenerated(estate, slot, CMD_INSERT); /* Check the constraints of the tuple */ if (rel->rd_att->constr) @@ -485,7 +485,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, /* Compute stored generated columns */ if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) - ExecComputeStoredGenerated(estate, slot); + ExecComputeStoredGenerated(estate, slot, CMD_UPDATE); /* Check the constraints of the tuple */ if (rel->rd_att->constr) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 59d1a31c97..d71c0a4322 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -246,7 +246,7 @@ ExecCheckTIDVisible(EState *estate, * Compute stored generated columns for a tuple */ void -ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot) +ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot, CmdType cmdtype) { ResultRelInfo *resultRelInfo = estate->es_result_relation_info; Relation rel = resultRelInfo->ri_RelationDesc; @@ -269,6 +269,7 @@ ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot) resultRelInfo->ri_GeneratedExprs = (ExprState **) palloc(natts * sizeof(ExprState *)); + resultRelInfo->ri_NumGeneratedNeeded = 0; for (int i = 0; i < natts; i++) { @@ -276,18 +277,41 @@ ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot) { Expr *expr; + /* + * If it's an update and the current column was not marked as + * being updated, then we can skip the computation. But if + * there is a BEFORE ROW UPDATE trigger, we cannot skip + * because the trigger might affect additional columns. + */ + if (cmdtype == CMD_UPDATE && + !(rel->trigdesc && rel->trigdesc->trig_update_before_row) && + !bms_is_member(i + 1 - FirstLowInvalidHeapAttributeNumber, + exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, estate)->extraUpdatedCols)) + { + resultRelInfo->ri_GeneratedExprs[i] = NULL; + continue; + } + expr = (Expr *) build_column_default(rel, i + 1); if (expr == NULL) elog(ERROR, "no generation expression found for column number %d of table \"%s\"", i + 1, RelationGetRelationName(rel)); resultRelInfo->ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate); + resultRelInfo->ri_NumGeneratedNeeded++; } } MemoryContextSwitchTo(oldContext); } + /* + * If no generated columns have been affected by this change, then skip + * the rest. + */ + if (resultRelInfo->ri_NumGeneratedNeeded == 0) + return; + oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); values = palloc(sizeof(*values) * natts); @@ -300,7 +324,8 @@ ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot) { Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (attr->attgenerated == ATTRIBUTE_GENERATED_STORED) + if (attr->attgenerated == ATTRIBUTE_GENERATED_STORED && + resultRelInfo->ri_GeneratedExprs[i]) { ExprContext *econtext; Datum val; @@ -392,7 +417,7 @@ ExecInsert(ModifyTableState *mtstate, */ if (resultRelationDesc->rd_att->constr && resultRelationDesc->rd_att->constr->has_generated_stored) - ExecComputeStoredGenerated(estate, slot); + ExecComputeStoredGenerated(estate, slot, CMD_INSERT); /* * insert into foreign table: let the FDW do it @@ -427,7 +452,7 @@ ExecInsert(ModifyTableState *mtstate, */ if (resultRelationDesc->rd_att->constr && resultRelationDesc->rd_att->constr->has_generated_stored) - ExecComputeStoredGenerated(estate, slot); + ExecComputeStoredGenerated(estate, slot, CMD_INSERT); /* * Check any RLS WITH CHECK policies. @@ -1088,7 +1113,7 @@ ExecUpdate(ModifyTableState *mtstate, */ if (resultRelationDesc->rd_att->constr && resultRelationDesc->rd_att->constr->has_generated_stored) - ExecComputeStoredGenerated(estate, slot); + ExecComputeStoredGenerated(estate, slot, CMD_UPDATE); /* * update in foreign table: let the FDW do it @@ -1125,7 +1150,7 @@ ExecUpdate(ModifyTableState *mtstate, */ if (resultRelationDesc->rd_att->constr && resultRelationDesc->rd_att->constr->has_generated_stored) - ExecComputeStoredGenerated(estate, slot); + ExecComputeStoredGenerated(estate, slot, CMD_UPDATE); /* * Check any RLS UPDATE WITH CHECK policies diff --git a/src/include/executor/nodeModifyTable.h b/src/include/executor/nodeModifyTable.h index 0495cae051..4ec4ebdabc 100644 --- a/src/include/executor/nodeModifyTable.h +++ b/src/include/executor/nodeModifyTable.h @@ -15,7 +15,7 @@ #include "nodes/execnodes.h" -extern void ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot); +extern void ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot, CmdType cmdtype); extern ModifyTableState *ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags); extern void ExecEndModifyTable(ModifyTableState *node); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 5d5b38b879..cd3ddf781f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -457,6 +457,9 @@ typedef struct ResultRelInfo /* array of stored generated columns expr states */ ExprState **ri_GeneratedExprs; + /* number of stored generated columns we need to compute */ + int ri_NumGeneratedNeeded; + /* for removing junk attributes from tuples */ JunkFilter *ri_junkFilter;