77 * replace_empty_jointree
88 * pull_up_sublinks
99 * preprocess_function_rtes
10+ * expand_virtual_generated_columns
1011 * pull_up_subqueries
1112 * flatten_simple_union_all
1213 * do expression preprocessing (including flattening JOIN alias vars)
2526 */
2627#include "postgres.h"
2728
29+ #include "access/table.h"
2830#include "catalog/pg_type.h"
2931#include "funcapi.h"
3032#include "miscadmin.h"
3941#include "optimizer/tlist.h"
4042#include "parser/parse_relation.h"
4143#include "parser/parsetree.h"
44+ #include "rewrite/rewriteHandler.h"
4245#include "rewrite/rewriteManip.h"
46+ #include "utils/rel.h"
4347
4448
4549typedef struct nullingrel_info
@@ -58,6 +62,8 @@ typedef struct pullup_replace_vars_context
5862 PlannerInfo * root ;
5963 List * targetlist ; /* tlist of subquery being pulled up */
6064 RangeTblEntry * target_rte ; /* RTE of subquery */
65+ int result_relation ; /* the index of the result relation in the
66+ * rewritten query */
6167 Relids relids ; /* relids within subquery, as numbered after
6268 * pullup (set only if target_rte->lateral) */
6369 nullingrel_info * nullinfo ; /* per-RTE nullingrel info (set only if
@@ -916,6 +922,133 @@ preprocess_function_rtes(PlannerInfo *root)
916922 }
917923}
918924
925+ /*
926+ * expand_virtual_generated_columns
927+ * Expand all virtual generated column references in a query.
928+ *
929+ * This scans the rangetable for relations with virtual generated columns, and
930+ * replaces all Var nodes in the query that reference these columns with the
931+ * generation expressions. Note that we do not descend into subqueries; that
932+ * is taken care of when the subqueries are planned.
933+ *
934+ * This has to be done after we have pulled up any SubLinks within the query's
935+ * quals; otherwise any virtual generated column references within the SubLinks
936+ * that should be transformed into joins wouldn't get expanded.
937+ *
938+ * Returns a modified copy of the query tree, if any relations with virtual
939+ * generated columns are present.
940+ */
941+ Query *
942+ expand_virtual_generated_columns (PlannerInfo * root )
943+ {
944+ Query * parse = root -> parse ;
945+ int rt_index ;
946+ ListCell * lc ;
947+
948+ rt_index = 0 ;
949+ foreach (lc , parse -> rtable )
950+ {
951+ RangeTblEntry * rte = (RangeTblEntry * ) lfirst (lc );
952+ Relation rel ;
953+ TupleDesc tupdesc ;
954+
955+ ++ rt_index ;
956+
957+ /*
958+ * Only normal relations can have virtual generated columns.
959+ */
960+ if (rte -> rtekind != RTE_RELATION )
961+ continue ;
962+
963+ rel = table_open (rte -> relid , NoLock );
964+
965+ tupdesc = RelationGetDescr (rel );
966+ if (tupdesc -> constr && tupdesc -> constr -> has_generated_virtual )
967+ {
968+ List * tlist = NIL ;
969+ pullup_replace_vars_context rvcontext ;
970+
971+ for (int i = 0 ; i < tupdesc -> natts ; i ++ )
972+ {
973+ Form_pg_attribute attr = TupleDescAttr (tupdesc , i );
974+ TargetEntry * tle ;
975+
976+ if (attr -> attgenerated == ATTRIBUTE_GENERATED_VIRTUAL )
977+ {
978+ Node * defexpr ;
979+
980+ defexpr = build_generation_expression (rel , i + 1 );
981+ ChangeVarNodes (defexpr , 1 , rt_index , 0 );
982+
983+ tle = makeTargetEntry ((Expr * ) defexpr , i + 1 , 0 , false);
984+ tlist = lappend (tlist , tle );
985+ }
986+ else
987+ {
988+ Var * var ;
989+
990+ var = makeVar (rt_index ,
991+ i + 1 ,
992+ attr -> atttypid ,
993+ attr -> atttypmod ,
994+ attr -> attcollation ,
995+ 0 );
996+
997+ tle = makeTargetEntry ((Expr * ) var , i + 1 , 0 , false);
998+ tlist = lappend (tlist , tle );
999+ }
1000+ }
1001+
1002+ Assert (list_length (tlist ) > 0 );
1003+ Assert (!rte -> lateral );
1004+
1005+ /*
1006+ * The relation's targetlist items are now in the appropriate form
1007+ * to insert into the query, except that we may need to wrap them
1008+ * in PlaceHolderVars. Set up required context data for
1009+ * pullup_replace_vars.
1010+ */
1011+ rvcontext .root = root ;
1012+ rvcontext .targetlist = tlist ;
1013+ rvcontext .target_rte = rte ;
1014+ rvcontext .result_relation = parse -> resultRelation ;
1015+ /* won't need these values */
1016+ rvcontext .relids = NULL ;
1017+ rvcontext .nullinfo = NULL ;
1018+ /* pass NULL for outer_hasSubLinks */
1019+ rvcontext .outer_hasSubLinks = NULL ;
1020+ rvcontext .varno = rt_index ;
1021+ /* this flag will be set below, if needed */
1022+ rvcontext .wrap_non_vars = false;
1023+ /* initialize cache array with indexes 0 .. length(tlist) */
1024+ rvcontext .rv_cache = palloc0 ((list_length (tlist ) + 1 ) *
1025+ sizeof (Node * ));
1026+
1027+ /*
1028+ * If the query uses grouping sets, we need a PlaceHolderVar for
1029+ * anything that's not a simple Var. Again, this ensures that
1030+ * expressions retain their separate identity so that they will
1031+ * match grouping set columns when appropriate. (It'd be
1032+ * sufficient to wrap values used in grouping set columns, and do
1033+ * so only in non-aggregated portions of the tlist and havingQual,
1034+ * but that would require a lot of infrastructure that
1035+ * pullup_replace_vars hasn't currently got.)
1036+ */
1037+ if (parse -> groupingSets )
1038+ rvcontext .wrap_non_vars = true;
1039+
1040+ /*
1041+ * Apply pullup variable replacement throughout the query tree.
1042+ */
1043+ parse = (Query * ) pullup_replace_vars ((Node * ) parse , & rvcontext );
1044+ }
1045+
1046+ table_close (rel , NoLock );
1047+ }
1048+
1049+ return parse ;
1050+ }
1051+
9191052/*
9201053 * pull_up_subqueries
9211054 * Look for subqueries in the rangetable that can be pulled up into
@@ -1197,6 +1330,13 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
11971330 */
11981331 preprocess_function_rtes (subroot );
11991332
1333+ /*
1334+ * Scan the rangetable for relations with virtual generated columns, and
1335+ * replace all Var nodes in the query that reference these columns with
1336+ * the generation expressions.
1337+ */
1338+ subquery = subroot -> parse = expand_virtual_generated_columns (subroot );
1339+
12001340 /*
12011341 * Recursively pull up the subquery's subqueries, so that
12021342 * pull_up_subqueries' processing is complete for its jointree and
@@ -1274,6 +1414,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
12741414 rvcontext .root = root ;
12751415 rvcontext .targetlist = subquery -> targetList ;
12761416 rvcontext .target_rte = rte ;
1417+ rvcontext .result_relation = 0 ;
12771418 if (rte -> lateral )
12781419 {
12791420 rvcontext .relids = get_relids_in_jointree ((Node * ) subquery -> jointree ,
@@ -1834,6 +1975,7 @@ pull_up_simple_values(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
18341975 rvcontext .root = root ;
18351976 rvcontext .targetlist = tlist ;
18361977 rvcontext .target_rte = rte ;
1978+ rvcontext .result_relation = 0 ;
18371979 rvcontext .relids = NULL ; /* can't be any lateral references here */
18381980 rvcontext .nullinfo = NULL ;
18391981 rvcontext .outer_hasSubLinks = & parse -> hasSubLinks ;
@@ -1993,6 +2135,7 @@ pull_up_constant_function(PlannerInfo *root, Node *jtnode,
19932135 NULL , /* resname */
19942136 false)); /* resjunk */
19952137 rvcontext .target_rte = rte ;
2138+ rvcontext .result_relation = 0 ;
19962139
19972140 /*
19982141 * Since this function was reduced to a Const, it doesn't contain any
@@ -2490,6 +2633,10 @@ pullup_replace_vars_callback(Var *var,
24902633 bool need_phv ;
24912634 Node * newnode ;
24922635
2636+ /* System columns are not replaced. */
2637+ if (varattno < InvalidAttrNumber )
2638+ return (Node * ) copyObject (var );
2639+
24932640 /*
24942641 * We need a PlaceHolderVar if the Var-to-be-replaced has nonempty
24952642 * varnullingrels (unless we find below that the replacement expression is
@@ -2559,6 +2706,22 @@ pullup_replace_vars_callback(Var *var,
25592706 rowexpr -> location = var -> location ;
25602707 newnode = (Node * ) rowexpr ;
25612708
2709+ /* Handle any OLD/NEW RETURNING list Vars */
2710+ if (var -> varreturningtype != VAR_RETURNING_DEFAULT )
2711+ {
2712+ /*
2713+ * Wrap the RowExpr in a ReturningExpr node, so that the executor
2714+ * returns NULL if the OLD/NEW row does not exist.
2715+ */
2716+ ReturningExpr * rexpr = makeNode (ReturningExpr );
2717+
2718+ rexpr -> retlevelsup = 0 ;
2719+ rexpr -> retold = (var -> varreturningtype == VAR_RETURNING_OLD );
2720+ rexpr -> retexpr = (Expr * ) newnode ;
2721+
2722+ newnode = (Node * ) rexpr ;
2723+ }
2724+
25622725 /*
25632726 * Insert PlaceHolderVar if needed. Notice that we are wrapping one
25642727 * PlaceHolderVar around the whole RowExpr, rather than putting one
@@ -2588,6 +2751,39 @@ pullup_replace_vars_callback(Var *var,
25882751 /* Make a copy of the tlist item to return */
25892752 newnode = (Node * ) copyObject (tle -> expr );
25902753
2754+ /* Handle any OLD/NEW RETURNING list Vars */
2755+ if (var -> varreturningtype != VAR_RETURNING_DEFAULT )
2756+ {
2757+ /*
2758+ * Copy varreturningtype onto any Vars in the tlist item that
2759+ * refer to result_relation (which had better be non-zero).
2760+ */
2761+ if (rcon -> result_relation == 0 )
2762+ elog (ERROR , "variable returning old/new found outside RETURNING list" );
2763+
2764+ SetVarReturningType ((Node * ) newnode , rcon -> result_relation ,
2765+ 0 , var -> varreturningtype );
2766+
2767+ /*
2768+ * If the replacement expression in the targetlist is not simply a
2769+ * Var referencing result_relation, wrap it in a ReturningExpr
2770+ * node, so that the executor returns NULL if the OLD/NEW row does
2771+ * not exist.
2772+ */
2773+ if (!IsA (newnode , Var ) ||
2774+ ((Var * ) newnode )-> varno != rcon -> result_relation ||
2775+ ((Var * ) newnode )-> varlevelsup != 0 )
2776+ {
2777+ ReturningExpr * rexpr = makeNode (ReturningExpr );
2778+
2779+ rexpr -> retlevelsup = 0 ;
2780+ rexpr -> retold = (var -> varreturningtype == VAR_RETURNING_OLD );
2781+ rexpr -> retexpr = (Expr * ) newnode ;
2782+
2783+ newnode = (Node * ) rexpr ;
2784+ }
2785+ }
2786+
25912787 /* Insert PlaceHolderVar if needed */
25922788 if (need_phv )
25932789 {
0 commit comments