diff options
Diffstat (limited to 'src/backend/rewrite/rewriteHandler.c')
-rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 133 |
1 files changed, 128 insertions, 5 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 847edcfa90e..e996bdc0d21 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -96,6 +96,8 @@ static List *matchLocks(CmdType event, Relation relation, 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); /* @@ -986,7 +988,8 @@ rewriteTargetListIU(List *targetList, if (att_tup->attgenerated) { /* - * stored generated column will be fixed in executor + * virtual generated column stores a null value; stored generated + * column will be fixed in executor */ new_tle = NULL; } @@ -2187,6 +2190,10 @@ fireRIRrules(Query *parsetree, List *activeRIRs) * requires special recursion detection if the new quals have sublink * subqueries, and if we did it in the loop above query_tree_walker would * then recurse into those quals a second time. + * + * Finally, we expand any virtual generated columns. We do this after + * each table's RLS policies are applied because the RLS policies might + * also refer to the table's virtual generated columns. */ rt_index = 0; foreach(lc, parsetree->rtable) @@ -2200,10 +2207,11 @@ fireRIRrules(Query *parsetree, List *activeRIRs) ++rt_index; - /* Only normal relations can have RLS policies */ - if (rte->rtekind != RTE_RELATION || - (rte->relkind != RELKIND_RELATION && - rte->relkind != RELKIND_PARTITIONED_TABLE)) + /* + * Only normal relations can have RLS policies or virtual generated + * columns. + */ + if (rte->rtekind != RTE_RELATION) continue; rel = table_open(rte->relid, NoLock); @@ -2292,6 +2300,16 @@ fireRIRrules(Query *parsetree, List *activeRIRs) if (hasSubLinks) parsetree->hasSubLinks = true; + /* + * Expand any references to virtual generated columns of this table. + * Note that subqueries in virtual generated column expressions are + * not currently supported, so this cannot add any more sublinks. + */ + parsetree = (Query *) + expand_generated_columns_internal((Node *) parsetree, + rel, rt_index, rte, + parsetree->resultRelation); + table_close(rel, NoLock); } @@ -4407,6 +4425,111 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length) /* + * 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. + * + * Vars matching rt_index at the current query level are replaced by the + * virtual generated column expressions from rel, if there are any. + * + * 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. + */ +static Node * +expand_generated_columns_internal(Node *node, Relation rel, int rt_index, + RangeTblEntry *rte, int result_relation) +{ + TupleDesc tupdesc; + + tupdesc = RelationGetDescr(rel); + if (tupdesc->constr && tupdesc->constr->has_generated_virtual) + { + List *tlist = NIL; + + for (int i = 0; i < tupdesc->natts; i++) + { + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + + if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL) + { + Node *defexpr; + int attnum = i + 1; + Oid attcollid; + TargetEntry *te; + + defexpr = build_column_default(rel, attnum); + if (defexpr == NULL) + elog(ERROR, "no generation expression found for column number %d of table \"%s\"", + attnum, RelationGetRelationName(rel)); + + /* + * If the column definition has a collation and it is + * different from the collation of the generation expression, + * put a COLLATE clause around the expression. + */ + attcollid = attr->attcollation; + if (attcollid && attcollid != exprCollation(defexpr)) + { + CollateExpr *ce = makeNode(CollateExpr); + + ce->arg = (Expr *) defexpr; + ce->collOid = attcollid; + ce->location = -1; + + defexpr = (Node *) ce; + } + + ChangeVarNodes(defexpr, 1, rt_index, 0); + + te = makeTargetEntry((Expr *) defexpr, attnum, 0, false); + tlist = lappend(tlist, te); + } + } + + Assert(list_length(tlist) > 0); + + node = ReplaceVarsFromTargetList(node, rt_index, 0, rte, tlist, + result_relation, + REPLACEVARS_CHANGE_VARNO, rt_index, + NULL); + } + + return node; +} + +/* + * Expand virtual generated columns in an expression + * + * This is for expressions that are not part of a query, such as default + * expressions or index predicates. The rt_index is usually 1. + */ +Node * +expand_generated_columns_in_expr(Node *node, Relation rel, int rt_index) +{ + TupleDesc tupdesc = RelationGetDescr(rel); + + if (tupdesc->constr && tupdesc->constr->has_generated_virtual) + { + RangeTblEntry *rte; + + rte = makeNode(RangeTblEntry); + /* eref needs to be set, but the actual name doesn't matter */ + rte->eref = makeAlias(RelationGetRelationName(rel), NIL); + rte->rtekind = RTE_RELATION; + rte->relid = RelationGetRelid(rel); + + node = expand_generated_columns_internal(node, rel, rt_index, rte, 0); + } + + return node; +} + + +/* * QueryRewrite - * Primary entry point to the query rewriter. * Rewrite one query via query rewrite system, possibly returning 0 |