]> granicus.if.org Git - postgresql/blob - src/backend/optimizer/util/placeholder.c
Fix estimate_num_groups() to not fail on PlaceHolderVars, per report from
[postgresql] / src / backend / optimizer / util / placeholder.c
1 /*-------------------------------------------------------------------------
2  *
3  * placeholder.c
4  *        PlaceHolderVar and PlaceHolderInfo manipulation routines
5  *
6  *
7  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $PostgreSQL: pgsql/src/backend/optimizer/util/placeholder.c,v 1.4 2009/04/19 19:46:33 tgl Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include "nodes/nodeFuncs.h"
19 #include "optimizer/pathnode.h"
20 #include "optimizer/placeholder.h"
21 #include "optimizer/planmain.h"
22 #include "optimizer/var.h"
23 #include "utils/lsyscache.h"
24
25
26 /*
27  * make_placeholder_expr
28  *              Make a PlaceHolderVar for the given expression.
29  *
30  * phrels is the syntactic location (as a set of baserels) to attribute
31  * to the expression.
32  */
33 PlaceHolderVar *
34 make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
35 {
36         PlaceHolderVar *phv = makeNode(PlaceHolderVar);
37
38         phv->phexpr = expr;
39         phv->phrels = phrels;
40         phv->phid = ++(root->glob->lastPHId);
41         phv->phlevelsup = 0;
42
43         return phv;
44 }
45
46 /*
47  * find_placeholder_info
48  *              Fetch the PlaceHolderInfo for the given PHV; create it if not found
49  *
50  * Note: this should only be called after query_planner() has started.
51  */
52 PlaceHolderInfo *
53 find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
54 {
55         PlaceHolderInfo *phinfo;
56         ListCell   *lc;
57
58         /* if this ever isn't true, we'd need to be able to look in parent lists */
59         Assert(phv->phlevelsup == 0);
60
61         foreach(lc, root->placeholder_list)
62         {
63                 phinfo = (PlaceHolderInfo *) lfirst(lc);
64                 if (phinfo->phid == phv->phid)
65                         return phinfo;
66         }
67
68         /* Not found, so create it */
69         phinfo = makeNode(PlaceHolderInfo);
70
71         phinfo->phid = phv->phid;
72         phinfo->ph_var = copyObject(phv);
73         phinfo->ph_eval_at = pull_varnos((Node *) phv);
74         /* ph_eval_at may change later, see fix_placeholder_eval_levels */
75         phinfo->ph_needed = NULL;               /* initially it's unused */
76         /* for the moment, estimate width using just the datatype info */
77         phinfo->ph_width = get_typavgwidth(exprType((Node *) phv->phexpr),
78                                                                            exprTypmod((Node *) phv->phexpr));
79
80         root->placeholder_list = lappend(root->placeholder_list, phinfo);
81
82         return phinfo;
83 }
84
85 /*
86  * fix_placeholder_eval_levels
87  *              Adjust the target evaluation levels for placeholders
88  *
89  * The initial eval_at level set by find_placeholder_info was the set of
90  * rels used in the placeholder's expression (or the whole subselect if
91  * the expr is variable-free).  If the subselect contains any outer joins
92  * that can null any of those rels, we must delay evaluation to above those
93  * joins.
94  *
95  * In future we might want to put additional policy/heuristics here to
96  * try to determine an optimal evaluation level.  The current rules will
97  * result in evaluation at the lowest possible level.
98  */
99 void
100 fix_placeholder_eval_levels(PlannerInfo *root)
101 {
102         ListCell   *lc1;
103
104         foreach(lc1, root->placeholder_list)
105         {
106                 PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc1);
107                 Relids          syn_level = phinfo->ph_var->phrels;
108                 Relids          eval_at = phinfo->ph_eval_at;
109                 bool            found_some;
110                 ListCell   *lc2;
111
112                 /*
113                  * Check for delays due to lower outer joins.  This is the same logic
114                  * as in check_outerjoin_delay in initsplan.c, except that we don't
115                  * want to modify the delay_upper_joins flags; that was all handled
116                  * already during distribute_qual_to_rels.
117                  */
118                 do
119                 {
120                         found_some = false;
121                         foreach(lc2, root->join_info_list)
122                         {
123                                 SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc2);
124
125                                 /* disregard joins not within the expr's sub-select */
126                                 if (!bms_is_subset(sjinfo->syn_lefthand, syn_level) ||
127                                         !bms_is_subset(sjinfo->syn_righthand, syn_level))
128                                         continue;
129
130                                 /* do we reference any nullable rels of this OJ? */
131                                 if (bms_overlap(eval_at, sjinfo->min_righthand) ||
132                                         (sjinfo->jointype == JOIN_FULL &&
133                                          bms_overlap(eval_at, sjinfo->min_lefthand)))
134                                 {
135                                         /* yes; have we included all its rels in eval_at? */
136                                         if (!bms_is_subset(sjinfo->min_lefthand, eval_at) ||
137                                                 !bms_is_subset(sjinfo->min_righthand, eval_at))
138                                         {
139                                                 /* no, so add them in */
140                                                 eval_at = bms_add_members(eval_at,
141                                                                                                   sjinfo->min_lefthand);
142                                                 eval_at = bms_add_members(eval_at,
143                                                                                                   sjinfo->min_righthand);
144                                                 /* we'll need another iteration */
145                                                 found_some = true;
146                                         }
147                                 }
148                         }
149                 } while (found_some);
150
151                 phinfo->ph_eval_at = eval_at;
152
153                 /*
154                  * Now that we know where to evaluate the placeholder, make sure that
155                  * any vars or placeholders it uses will be available at that join
156                  * level.  NOTE: this could cause more PlaceHolderInfos to be added
157                  * to placeholder_list.  That is okay because we'll process them
158                  * before falling out of the foreach loop.  Also, it could cause
159                  * the ph_needed sets of existing list entries to expand, which
160                  * is also okay because this loop doesn't examine those.
161                  */
162                 if (bms_membership(eval_at) == BMS_MULTIPLE)
163                 {
164                         List       *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
165                                                                                            PVC_INCLUDE_PLACEHOLDERS);
166
167                         add_vars_to_targetlist(root, vars, eval_at);
168                         list_free(vars);
169                 }
170         }
171
172         /*
173          * Now, if any placeholder can be computed at a base rel and is needed
174          * above it, add it to that rel's targetlist.  (This is essentially the
175          * same logic as in add_placeholders_to_joinrel, but we can't do that part
176          * until joinrels are formed.)  We have to do this as a separate step
177          * because the ph_needed values aren't stable until the previous loop
178          * finishes.
179          */
180         foreach(lc1, root->placeholder_list)
181         {
182                 PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc1);
183                 Relids          eval_at = phinfo->ph_eval_at;
184
185                 if (bms_membership(eval_at) == BMS_SINGLETON)
186                 {
187                         int                     varno = bms_singleton_member(eval_at);
188                         RelOptInfo *rel = find_base_rel(root, varno);
189
190                         if (bms_nonempty_difference(phinfo->ph_needed, rel->relids))
191                                 rel->reltargetlist = lappend(rel->reltargetlist,
192                                                                                          copyObject(phinfo->ph_var));
193                 }
194         }
195 }
196
197 /*
198  * add_placeholders_to_joinrel
199  *              Add any required PlaceHolderVars to a join rel's targetlist.
200  *
201  * A join rel should emit a PlaceHolderVar if (a) the PHV is needed above
202  * this join level and (b) the PHV can be computed at or below this level.
203  * At this time we do not need to distinguish whether the PHV will be
204  * computed here or copied up from below.
205  */
206 void
207 add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel)
208 {
209         Relids          relids = joinrel->relids;
210         ListCell   *lc;
211
212         foreach(lc, root->placeholder_list)
213         {
214                 PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
215
216                 /* Is it still needed above this joinrel? */
217                 if (bms_nonempty_difference(phinfo->ph_needed, relids))
218                 {
219                         /* Is it computable here? */
220                         if (bms_is_subset(phinfo->ph_eval_at, relids))
221                         {
222                                 /* Yup, add it to the output */
223                                 joinrel->reltargetlist = lappend(joinrel->reltargetlist,
224                                                                                                  phinfo->ph_var);
225                                 joinrel->width += phinfo->ph_width;
226                         }
227                 }
228         }
229 }