int varno, Query *parsetree, bool *hasUpdate);
static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist);
-static Node *expand_generated_columns_internal(Node *node, Relation rel, int rt_index,
- RangeTblEntry *rte, int result_relation);
+static List *get_generated_columns(Relation rel, int rt_index, bool include_stored);
/*
if ((event == CMD_INSERT || event == CMD_UPDATE) &&
sub_action->commandType != CMD_UTILITY)
{
+ RangeTblEntry *new_rte = rt_fetch(new_varno, sub_action->rtable);
+ Relation new_rel;
+ List *gen_cols;
+
+ /*
+ * The target list does not contain entries for generated columns
+ * (they are removed by rewriteTargetListIU), so we must build entries
+ * for them here, so that new.gen_col can be rewritten correctly.
+ */
+ new_rel = relation_open(new_rte->relid, NoLock);
+ gen_cols = get_generated_columns(new_rel, new_varno, true);
+ relation_close(new_rel, NoLock);
+
+ /*
+ * The generated column expressions refer to new.attribute, so they
+ * must be rewritten before they can be used as replacements.
+ */
+ gen_cols = (List *)
+ ReplaceVarsFromTargetList((Node *) gen_cols,
+ new_varno,
+ 0,
+ new_rte,
+ parsetree->targetList,
+ sub_action->resultRelation,
+ (event == CMD_UPDATE) ?
+ REPLACEVARS_CHANGE_VARNO :
+ REPLACEVARS_SUBSTITUTE_NULL,
+ current_varno,
+ &sub_action->hasSubLinks);
+
+ /*
+ * Now rewrite new.attribute in sub_action, using both the target list
+ * and the rewritten generated column expressions.
+ */
sub_action = (Query *)
ReplaceVarsFromTargetList((Node *) sub_action,
new_varno,
0,
- rt_fetch(new_varno, sub_action->rtable),
- parsetree->targetList,
+ new_rte,
+ list_concat(gen_cols, parsetree->targetList),
sub_action->resultRelation,
(event == CMD_UPDATE) ?
REPLACEVARS_CHANGE_VARNO :
ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
/* Fix references to NEW */
if (event == CMD_INSERT || event == CMD_UPDATE)
+ {
+ RangeTblEntry *rte = rt_fetch(rt_index, parsetree->rtable);
+ Relation rel;
+ List *gen_cols;
+
+ /*
+ * As in rewriteRuleAction, build entries for generated columns so
+ * that new.gen_col in the rule qualification can be rewritten
+ * correctly.
+ */
+ rel = relation_open(rte->relid, NoLock);
+ gen_cols = get_generated_columns(rel, PRS2_NEW_VARNO, true);
+ relation_close(rel, NoLock);
+
+ /*
+ * The generated column expressions refer to new.attribute, so they
+ * must be rewritten before they can be used as replacements.
+ */
+ gen_cols = (List *)
+ ReplaceVarsFromTargetList((Node *) gen_cols,
+ PRS2_NEW_VARNO,
+ 0,
+ rte,
+ parsetree->targetList,
+ parsetree->resultRelation,
+ (event == CMD_UPDATE) ?
+ REPLACEVARS_CHANGE_VARNO :
+ REPLACEVARS_SUBSTITUTE_NULL,
+ rt_index,
+ &parsetree->hasSubLinks);
+
new_qual = ReplaceVarsFromTargetList(new_qual,
PRS2_NEW_VARNO,
0,
- rt_fetch(rt_index,
- parsetree->rtable),
- parsetree->targetList,
+ rte,
+ list_concat(gen_cols,
+ parsetree->targetList),
parsetree->resultRelation,
(event == CMD_UPDATE) ?
REPLACEVARS_CHANGE_VARNO :
REPLACEVARS_SUBSTITUTE_NULL,
rt_index,
&parsetree->hasSubLinks);
+ }
/* And attach the fixed qual */
AddInvertedQual(parsetree, new_qual);
/*
- * Expand virtual generated columns
- *
- * If the table contains virtual generated columns, build a target list
- * containing the expanded expressions and use ReplaceVarsFromTargetList() to
- * do the replacements.
+ * Get a table's generated columns
*
- * Vars matching rt_index at the current query level are replaced by the
- * virtual generated column expressions from rel, if there are any.
+ * If include_stored is true, both stored and virtual generated columns are
+ * returned. Otherwise, only virtual generated columns are returned.
*
- * The caller must also provide rte, the RTE describing the target relation,
- * in order to handle any whole-row Vars referencing the target, and
- * result_relation, the index of the result relation, if this is part of an
- * INSERT/UPDATE/DELETE/MERGE query.
+ * Returns a list of TargetEntry, one for each generated column, containing
+ * the attribute numbers and generation expressions.
*/
-static Node *
-expand_generated_columns_internal(Node *node, Relation rel, int rt_index,
- RangeTblEntry *rte, int result_relation)
+static List *
+get_generated_columns(Relation rel, int rt_index, bool include_stored)
{
+ List *gen_cols = NIL;
TupleDesc tupdesc;
tupdesc = RelationGetDescr(rel);
- if (tupdesc->constr && tupdesc->constr->has_generated_virtual)
+ if (tupdesc->constr &&
+ (tupdesc->constr->has_generated_virtual ||
+ (include_stored && tupdesc->constr->has_generated_stored)))
{
- List *tlist = NIL;
-
for (int i = 0; i < tupdesc->natts; i++)
{
Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
- if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
+ if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ||
+ (include_stored && attr->attgenerated == ATTRIBUTE_GENERATED_STORED))
{
Node *defexpr;
TargetEntry *te;
ChangeVarNodes(defexpr, 1, rt_index, 0);
te = makeTargetEntry((Expr *) defexpr, i + 1, 0, false);
- tlist = lappend(tlist, te);
+ gen_cols = lappend(gen_cols, te);
}
}
-
- Assert(list_length(tlist) > 0);
-
- node = ReplaceVarsFromTargetList(node, rt_index, 0, rte, tlist,
- result_relation,
- REPLACEVARS_CHANGE_VARNO, rt_index,
- NULL);
}
- return node;
+ return gen_cols;
}
/*
if (tupdesc->constr && tupdesc->constr->has_generated_virtual)
{
RangeTblEntry *rte;
+ List *vcols;
rte = makeNode(RangeTblEntry);
/* eref needs to be set, but the actual name doesn't matter */
rte->rtekind = RTE_RELATION;
rte->relid = RelationGetRelid(rel);
- node = expand_generated_columns_internal(node, rel, rt_index, rte, 0);
+ vcols = get_generated_columns(rel, rt_index, false);
+
+ if (vcols)
+ {
+ /*
+ * Passing NULL for outer_hasSubLinks is safe because generation
+ * expressions cannot contain SubLinks, so the replacement cannot
+ * introduce any.
+ */
+ node = ReplaceVarsFromTargetList(node, rt_index, 0, rte, vcols, 0,
+ REPLACEVARS_CHANGE_VARNO, rt_index,
+ NULL);
+ }
}
return node;
}
/*
- * Build the generation expression for the virtual generated column.
+ * Build the generation expression for a generated column.
*
* Error out if there is no generation expression found for the given column.
*/
Node *defexpr;
Oid attcollid;
- Assert(rd_att->constr && rd_att->constr->has_generated_virtual);
- Assert(att_tup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL);
+ Assert(rd_att->constr &&
+ (rd_att->constr->has_generated_virtual ||
+ rd_att->constr->has_generated_stored));
+ Assert(att_tup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ||
+ att_tup->attgenerated == ATTRIBUTE_GENERATED_STORED);
defexpr = build_column_default(rel, attrno);
if (defexpr == NULL)