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.c112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 7be0e7745af..61c4459f676 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -49,6 +49,7 @@
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
@@ -240,6 +241,89 @@ ExecCheckTIDVisible(EState *estate,
ExecClearTuple(tempSlot);
}
+/*
+ * Compute stored generated columns for a tuple
+ */
+void
+ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot)
+{
+ ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
+ Relation rel = resultRelInfo->ri_RelationDesc;
+ TupleDesc tupdesc = RelationGetDescr(rel);
+ int natts = tupdesc->natts;
+ MemoryContext oldContext;
+ Datum *values;
+ bool *nulls;
+ bool *replaces;
+ HeapTuple oldtuple, newtuple;
+ bool should_free;
+
+ Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
+
+ /*
+ * If first time through for this result relation, build expression
+ * nodetrees for rel's stored generation expressions. Keep them in the
+ * per-query memory context so they'll survive throughout the query.
+ */
+ if (resultRelInfo->ri_GeneratedExprs == NULL)
+ {
+ oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ resultRelInfo->ri_GeneratedExprs =
+ (ExprState **) palloc(natts * sizeof(ExprState *));
+
+ for (int i = 0; i < natts; i++)
+ {
+ if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
+ {
+ Expr *expr;
+
+ 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);
+ }
+ }
+
+ MemoryContextSwitchTo(oldContext);
+ }
+
+ oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
+
+ values = palloc(sizeof(*values) * natts);
+ nulls = palloc(sizeof(*nulls) * natts);
+ replaces = palloc0(sizeof(*replaces) * natts);
+
+ for (int i = 0; i < natts; i++)
+ {
+ if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
+ {
+ ExprContext *econtext;
+ Datum val;
+ bool isnull;
+
+ econtext = GetPerTupleExprContext(estate);
+ econtext->ecxt_scantuple = slot;
+
+ val = ExecEvalExpr(resultRelInfo->ri_GeneratedExprs[i], econtext, &isnull);
+
+ values[i] = val;
+ nulls[i] = isnull;
+ replaces[i] = true;
+ }
+ }
+
+ oldtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
+ newtuple = heap_modify_tuple(oldtuple, tupdesc, values, nulls, replaces);
+ ExecForceStoreHeapTuple(newtuple, slot);
+ if (should_free)
+ heap_freetuple(oldtuple);
+
+ MemoryContextSwitchTo(oldContext);
+}
+
/* ----------------------------------------------------------------
* ExecInsert
*
@@ -298,6 +382,13 @@ ExecInsert(ModifyTableState *mtstate,
else if (resultRelInfo->ri_FdwRoutine)
{
/*
+ * Compute stored generated columns
+ */
+ if (resultRelationDesc->rd_att->constr &&
+ resultRelationDesc->rd_att->constr->has_generated_stored)
+ ExecComputeStoredGenerated(estate, slot);
+
+ /*
* insert into foreign table: let the FDW do it
*/
slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
@@ -327,6 +418,13 @@ ExecInsert(ModifyTableState *mtstate,
slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
+ * Compute stored generated columns
+ */
+ if (resultRelationDesc->rd_att->constr &&
+ resultRelationDesc->rd_att->constr->has_generated_stored)
+ ExecComputeStoredGenerated(estate, slot);
+
+ /*
* Check any RLS WITH CHECK policies.
*
* Normally we should check INSERT policies. But if the insert is the
@@ -965,6 +1063,13 @@ ExecUpdate(ModifyTableState *mtstate,
else if (resultRelInfo->ri_FdwRoutine)
{
/*
+ * Compute stored generated columns
+ */
+ if (resultRelationDesc->rd_att->constr &&
+ resultRelationDesc->rd_att->constr->has_generated_stored)
+ ExecComputeStoredGenerated(estate, slot);
+
+ /*
* update in foreign table: let the FDW do it
*/
slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
@@ -995,6 +1100,13 @@ ExecUpdate(ModifyTableState *mtstate,
slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
+ * Compute stored generated columns
+ */
+ if (resultRelationDesc->rd_att->constr &&
+ resultRelationDesc->rd_att->constr->has_generated_stored)
+ ExecComputeStoredGenerated(estate, slot);
+
+ /*
* Check any RLS UPDATE WITH CHECK policies
*
* If we generate a new candidate tuple after EvalPlanQual testing, we