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
{