7
7
* replace_empty_jointree
8
8
* pull_up_sublinks
9
9
* preprocess_function_rtes
10
+ * expand_virtual_generated_columns
10
11
* pull_up_subqueries
11
12
* flatten_simple_union_all
12
13
* do expression preprocessing (including flattening JOIN alias vars)
25
26
*/
26
27
#include "postgres.h"
27
28
29
+ #include "access/table.h"
28
30
#include "catalog/pg_type.h"
29
31
#include "funcapi.h"
30
32
#include "miscadmin.h"
39
41
#include "optimizer/tlist.h"
40
42
#include "parser/parse_relation.h"
41
43
#include "parser/parsetree.h"
44
+ #include "rewrite/rewriteHandler.h"
42
45
#include "rewrite/rewriteManip.h"
46
+ #include "utils/rel.h"
43
47
44
48
45
49
typedef struct nullingrel_info
@@ -58,6 +62,8 @@ typedef struct pullup_replace_vars_context
58
62
PlannerInfo * root ;
59
63
List * targetlist ; /* tlist of subquery being pulled up */
60
64
RangeTblEntry * target_rte ; /* RTE of subquery */
65
+ int result_relation ; /* the index of the result relation in the
66
+ * rewritten query */
61
67
Relids relids ; /* relids within subquery, as numbered after
62
68
* pullup (set only if target_rte->lateral) */
63
69
nullingrel_info * nullinfo ; /* per-RTE nullingrel info (set only if
@@ -916,6 +922,133 @@ preprocess_function_rtes(PlannerInfo *root)
916
922
}
917
923
}
918
924
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
+
919
1052
/*
920
1053
* pull_up_subqueries
921
1054
* 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,
1197
1330
*/
1198
1331
preprocess_function_rtes (subroot );
1199
1332
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
+
1200
1340
/*
1201
1341
* Recursively pull up the subquery's subqueries, so that
1202
1342
* pull_up_subqueries' processing is complete for its jointree and
@@ -1274,6 +1414,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
1274
1414
rvcontext .root = root ;
1275
1415
rvcontext .targetlist = subquery -> targetList ;
1276
1416
rvcontext .target_rte = rte ;
1417
+ rvcontext .result_relation = 0 ;
1277
1418
if (rte -> lateral )
1278
1419
{
1279
1420
rvcontext .relids = get_relids_in_jointree ((Node * ) subquery -> jointree ,
@@ -1834,6 +1975,7 @@ pull_up_simple_values(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
1834
1975
rvcontext .root = root ;
1835
1976
rvcontext .targetlist = tlist ;
1836
1977
rvcontext .target_rte = rte ;
1978
+ rvcontext .result_relation = 0 ;
1837
1979
rvcontext .relids = NULL ; /* can't be any lateral references here */
1838
1980
rvcontext .nullinfo = NULL ;
1839
1981
rvcontext .outer_hasSubLinks = & parse -> hasSubLinks ;
@@ -1993,6 +2135,7 @@ pull_up_constant_function(PlannerInfo *root, Node *jtnode,
1993
2135
NULL , /* resname */
1994
2136
false)); /* resjunk */
1995
2137
rvcontext .target_rte = rte ;
2138
+ rvcontext .result_relation = 0 ;
1996
2139
1997
2140
/*
1998
2141
* Since this function was reduced to a Const, it doesn't contain any
@@ -2490,6 +2633,10 @@ pullup_replace_vars_callback(Var *var,
2490
2633
bool need_phv ;
2491
2634
Node * newnode ;
2492
2635
2636
+ /* System columns are not replaced. */
2637
+ if (varattno < InvalidAttrNumber )
2638
+ return (Node * ) copyObject (var );
2639
+
2493
2640
/*
2494
2641
* We need a PlaceHolderVar if the Var-to-be-replaced has nonempty
2495
2642
* varnullingrels (unless we find below that the replacement expression is
@@ -2559,6 +2706,22 @@ pullup_replace_vars_callback(Var *var,
2559
2706
rowexpr -> location = var -> location ;
2560
2707
newnode = (Node * ) rowexpr ;
2561
2708
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
+
2562
2725
/*
2563
2726
* Insert PlaceHolderVar if needed. Notice that we are wrapping one
2564
2727
* PlaceHolderVar around the whole RowExpr, rather than putting one
@@ -2588,6 +2751,39 @@ pullup_replace_vars_callback(Var *var,
2588
2751
/* Make a copy of the tlist item to return */
2589
2752
newnode = (Node * ) copyObject (tle -> expr );
2590
2753
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
+
2591
2787
/* Insert PlaceHolderVar if needed */
2592
2788
if (need_phv )
2593
2789
{
0 commit comments