1 /*-------------------------------------------------------------------------
4 * PlaceHolderVar and PlaceHolderInfo manipulation routines
7 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
14 *-------------------------------------------------------------------------
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"
27 * make_placeholder_expr
28 * Make a PlaceHolderVar for the given expression.
30 * phrels is the syntactic location (as a set of baserels) to attribute
34 make_placeholder_expr(PlannerInfo
*root
, Expr
*expr
, Relids phrels
)
36 PlaceHolderVar
*phv
= makeNode(PlaceHolderVar
);
40 phv
->phid
= ++(root
->glob
->lastPHId
);
47 * find_placeholder_info
48 * Fetch the PlaceHolderInfo for the given PHV; create it if not found
50 * Note: this should only be called after query_planner() has started.
53 find_placeholder_info(PlannerInfo
*root
, PlaceHolderVar
*phv
)
55 PlaceHolderInfo
*phinfo
;
58 /* if this ever isn't true, we'd need to be able to look in parent lists */
59 Assert(phv
->phlevelsup
== 0);
61 foreach(lc
, root
->placeholder_list
)
63 phinfo
= (PlaceHolderInfo
*) lfirst(lc
);
64 if (phinfo
->phid
== phv
->phid
)
68 /* Not found, so create it */
69 phinfo
= makeNode(PlaceHolderInfo
);
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
));
80 root
->placeholder_list
= lappend(root
->placeholder_list
, phinfo
);
86 * fix_placeholder_eval_levels
87 * Adjust the target evaluation levels for placeholders
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
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.
100 fix_placeholder_eval_levels(PlannerInfo
*root
)
104 foreach(lc1
, root
->placeholder_list
)
106 PlaceHolderInfo
*phinfo
= (PlaceHolderInfo
*) lfirst(lc1
);
107 Relids syn_level
= phinfo
->ph_var
->phrels
;
108 Relids eval_at
= phinfo
->ph_eval_at
;
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.
121 foreach(lc2
, root
->join_info_list
)
123 SpecialJoinInfo
*sjinfo
= (SpecialJoinInfo
*) lfirst(lc2
);
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
))
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
)))
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
))
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 */
149 } while (found_some
);
151 phinfo
->ph_eval_at
= eval_at
;
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 to
157 * placeholder_list. That is okay because we'll process them before
158 * falling out of the foreach loop. Also, it could cause the
159 * ph_needed sets of existing list entries to expand, which is also
160 * okay because this loop doesn't examine those.
162 if (bms_membership(eval_at
) == BMS_MULTIPLE
)
164 List
*vars
= pull_var_clause((Node
*) phinfo
->ph_var
->phexpr
,
165 PVC_INCLUDE_PLACEHOLDERS
);
167 add_vars_to_targetlist(root
, vars
, eval_at
);
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
180 foreach(lc1
, root
->placeholder_list
)
182 PlaceHolderInfo
*phinfo
= (PlaceHolderInfo
*) lfirst(lc1
);
183 Relids eval_at
= phinfo
->ph_eval_at
;
185 if (bms_membership(eval_at
) == BMS_SINGLETON
)
187 int varno
= bms_singleton_member(eval_at
);
188 RelOptInfo
*rel
= find_base_rel(root
, varno
);
190 if (bms_nonempty_difference(phinfo
->ph_needed
, rel
->relids
))
191 rel
->reltargetlist
= lappend(rel
->reltargetlist
,
192 copyObject(phinfo
->ph_var
));
198 * add_placeholders_to_joinrel
199 * Add any required PlaceHolderVars to a join rel's targetlist.
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.
207 add_placeholders_to_joinrel(PlannerInfo
*root
, RelOptInfo
*joinrel
)
209 Relids relids
= joinrel
->relids
;
212 foreach(lc
, root
->placeholder_list
)
214 PlaceHolderInfo
*phinfo
= (PlaceHolderInfo
*) lfirst(lc
);
216 /* Is it still needed above this joinrel? */
217 if (bms_nonempty_difference(phinfo
->ph_needed
, relids
))
219 /* Is it computable here? */
220 if (bms_is_subset(phinfo
->ph_eval_at
, relids
))
222 /* Yup, add it to the output */
223 joinrel
->reltargetlist
= lappend(joinrel
->reltargetlist
,
225 joinrel
->width
+= phinfo
->ph_width
;