*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.64 2009/02/27 23:30:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.65 2009/04/28 21:31:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Relids available_rels, Node **jtlink);
static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
RangeTblEntry *rte,
- bool below_outer_join,
- bool append_rel_member);
+ JoinExpr *lowest_outer_join,
+ AppendRelInfo *containing_appendrel);
static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode,
RangeTblEntry *rte);
static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root,
static List *insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
int varno, bool wrap_non_vars);
static bool is_safe_append_member(Query *subquery);
-static void resolvenew_in_jointree(Node *jtnode, int varno,
- RangeTblEntry *rte, List *subtlist);
+static void resolvenew_in_jointree(Node *jtnode, int varno, RangeTblEntry *rte,
+ List *subtlist, List *subtlist_with_phvs,
+ JoinExpr *lowest_outer_join);
static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
static void reduce_outer_joins_pass2(Node *jtnode,
reduce_outer_joins_state *state,
* Also, subqueries that are simple UNION ALL structures can be
* converted into "append relations".
*
- * below_outer_join is true if this jointree node is within the nullable
- * side of an outer join. This forces use of the PlaceHolderVar mechanism
- * for non-nullable targetlist items.
+ * If this jointree node is within the nullable side of an outer join, then
+ * lowest_outer_join references the lowest such JoinExpr node; otherwise it
+ * is NULL. This forces use of the PlaceHolderVar mechanism for references
+ * to non-nullable targetlist items, but only for references above that join.
*
- * append_rel_member is true if we are looking at a member subquery of
- * an append relation. This forces use of the PlaceHolderVar mechanism
- * for all non-Var targetlist items, and puts some additional restrictions
- * on what can be pulled up.
+ * If we are looking at a member subquery of an append relation,
+ * containing_appendrel describes that relation; else it is NULL.
+ * This forces use of the PlaceHolderVar mechanism for all non-Var targetlist
+ * items, and puts some additional restrictions on what can be pulled up.
*
* A tricky aspect of this code is that if we pull up a subquery we have
* to replace Vars that reference the subquery's outputs throughout the
* subquery RangeTblRef entries will be replaced. Also, we can't turn
* ResolveNew loose on the whole jointree, because it'll return a mutated
* copy of the tree; we have to invoke it just on the quals, instead.
+ * This behavior is what makes it reasonable to pass lowest_outer_join as a
+ * pointer rather than some more-indirect way of identifying the lowest OJ.
+ * Likewise, we don't replace append_rel_list members but only their
+ * substructure, so the containing_appendrel reference is safe to use.
*/
Node *
pull_up_subqueries(PlannerInfo *root, Node *jtnode,
- bool below_outer_join, bool append_rel_member)
+ JoinExpr *lowest_outer_join,
+ AppendRelInfo *containing_appendrel)
{
if (jtnode == NULL)
return NULL;
*/
if (rte->rtekind == RTE_SUBQUERY &&
is_simple_subquery(rte->subquery) &&
- (!append_rel_member || is_safe_append_member(rte->subquery)))
+ (containing_appendrel == NULL ||
+ is_safe_append_member(rte->subquery)))
return pull_up_simple_subquery(root, jtnode, rte,
- below_outer_join,
- append_rel_member);
+ lowest_outer_join,
+ containing_appendrel);
/*
* Alternatively, is it a simple UNION ALL subquery? If so, flatten
FromExpr *f = (FromExpr *) jtnode;
ListCell *l;
- Assert(!append_rel_member);
+ Assert(containing_appendrel == NULL);
foreach(l, f->fromlist)
lfirst(l) = pull_up_subqueries(root, lfirst(l),
- below_outer_join, false);
+ lowest_outer_join, NULL);
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
- Assert(!append_rel_member);
+ Assert(containing_appendrel == NULL);
/* Recurse, being careful to tell myself when inside outer join */
switch (j->jointype)
{
case JOIN_INNER:
j->larg = pull_up_subqueries(root, j->larg,
- below_outer_join, false);
+ lowest_outer_join, NULL);
j->rarg = pull_up_subqueries(root, j->rarg,
- below_outer_join, false);
+ lowest_outer_join, NULL);
break;
case JOIN_LEFT:
case JOIN_SEMI:
case JOIN_ANTI:
j->larg = pull_up_subqueries(root, j->larg,
- below_outer_join, false);
+ lowest_outer_join, NULL);
j->rarg = pull_up_subqueries(root, j->rarg,
- true, false);
+ j, NULL);
break;
case JOIN_FULL:
j->larg = pull_up_subqueries(root, j->larg,
- true, false);
+ j, NULL);
j->rarg = pull_up_subqueries(root, j->rarg,
- true, false);
+ j, NULL);
break;
case JOIN_RIGHT:
j->larg = pull_up_subqueries(root, j->larg,
- true, false);
+ j, NULL);
j->rarg = pull_up_subqueries(root, j->rarg,
- below_outer_join, false);
+ lowest_outer_join, NULL);
break;
default:
elog(ERROR, "unrecognized join type: %d",
* subquery by pull_up_subqueries. We return the replacement jointree node,
* or jtnode itself if we determine that the subquery can't be pulled up after
* all.
+ *
+ * rte is the RangeTblEntry referenced by jtnode. Remaining parameters are
+ * as for pull_up_subqueries.
*/
static Node *
pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
- bool below_outer_join, bool append_rel_member)
+ JoinExpr *lowest_outer_join,
+ AppendRelInfo *containing_appendrel)
{
Query *parse = root->parse;
int varno = ((RangeTblRef *) jtnode)->rtindex;
PlannerInfo *subroot;
int rtoffset;
List *subtlist;
- ListCell *rt;
+ List *subtlist_with_phvs;
+ ListCell *lc;
/*
* Need a modifiable copy of the subquery to hack on. Even if we didn't
* pull_up_subqueries' processing is complete for its jointree and
* rangetable.
*
- * Note: below_outer_join = false is correct here even if we are within an
- * outer join in the upper query; the lower query starts with a clean
- * slate for outer-join semantics. Likewise, we say we aren't handling an
+ * Note: we should pass NULL for containing-join info even if we are within
+ * an outer join in the upper query; the lower query starts with a clean
+ * slate for outer-join semantics. Likewise, we say we aren't handling an
* appendrel member.
*/
subquery->jointree = (FromExpr *)
- pull_up_subqueries(subroot, (Node *) subquery->jointree, false, false);
+ pull_up_subqueries(subroot, (Node *) subquery->jointree, NULL, NULL);
/*
* Now we must recheck whether the subquery is still simple enough to pull
* pull_up_subqueries.
*/
if (is_simple_subquery(subquery) &&
- (!append_rel_member || is_safe_append_member(subquery)))
+ (containing_appendrel == NULL || is_safe_append_member(subquery)))
{
/* good to go */
}
/*
* The subquery's targetlist items are now in the appropriate form to
* insert into the top query, but if we are under an outer join then
- * non-nullable items have to be turned into PlaceHolderVars. If we
+ * non-nullable items may have to be turned into PlaceHolderVars. If we
* are dealing with an appendrel member then anything that's not a
* simple Var has to be turned into a PlaceHolderVar.
*/
- if (below_outer_join || append_rel_member)
- subtlist = insert_targetlist_placeholders(root, subquery->targetList,
- varno, append_rel_member);
+ subtlist = subquery->targetList;
+ if (lowest_outer_join != NULL || containing_appendrel != NULL)
+ subtlist_with_phvs = insert_targetlist_placeholders(root,
+ subtlist,
+ varno,
+ containing_appendrel != NULL);
else
- subtlist = subquery->targetList;
+ subtlist_with_phvs = subtlist;
/*
* Replace all of the top query's references to the subquery's outputs
* with copies of the adjusted subtlist items, being careful not to
* replace any of the jointree structure. (This'd be a lot cleaner if we
- * could use query_tree_mutator.)
+ * could use query_tree_mutator.) We have to use PHVs in the targetList,
+ * returningList, and havingQual, since those are certainly above any
+ * outer join. resolvenew_in_jointree tracks its location in the jointree
+ * and uses PHVs or not appropriately.
*/
parse->targetList = (List *)
ResolveNew((Node *) parse->targetList,
varno, 0, rte,
- subtlist, CMD_SELECT, 0);
+ subtlist_with_phvs, CMD_SELECT, 0);
parse->returningList = (List *)
ResolveNew((Node *) parse->returningList,
varno, 0, rte,
- subtlist, CMD_SELECT, 0);
- resolvenew_in_jointree((Node *) parse->jointree, varno,
- rte, subtlist);
+ subtlist_with_phvs, CMD_SELECT, 0);
+ resolvenew_in_jointree((Node *) parse->jointree, varno, rte,
+ subtlist, subtlist_with_phvs,
+ lowest_outer_join);
Assert(parse->setOperations == NULL);
parse->havingQual =
ResolveNew(parse->havingQual,
varno, 0, rte,
- subtlist, CMD_SELECT, 0);
- root->append_rel_list = (List *)
- ResolveNew((Node *) root->append_rel_list,
- varno, 0, rte,
- subtlist, CMD_SELECT, 0);
+ subtlist_with_phvs, CMD_SELECT, 0);
- foreach(rt, parse->rtable)
+ /*
+ * Replace references in the translated_vars lists of appendrels.
+ * When pulling up an appendrel member, we do not need PHVs in the list
+ * of the parent appendrel --- there isn't any outer join between.
+ * Elsewhere, use PHVs for safety. (This analysis could be made tighter
+ * but it seems unlikely to be worth much trouble.)
+ */
+ foreach(lc, root->append_rel_list)
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
+
+ appinfo->translated_vars = (List *)
+ ResolveNew((Node *) appinfo->translated_vars,
+ varno, 0, rte,
+ (appinfo == containing_appendrel) ?
+ subtlist : subtlist_with_phvs,
+ CMD_SELECT, 0);
+ }
+
+ /*
+ * Replace references in the joinaliasvars lists of join RTEs.
+ *
+ * You might think that we could avoid using PHVs for alias vars of joins
+ * below lowest_outer_join, but that doesn't work because the alias vars
+ * could be referenced above that join; we need the PHVs to be present
+ * in such references after the alias vars get flattened. (It might be
+ * worth trying to be smarter here, someday.)
+ */
+ foreach(lc, parse->rtable)
{
- RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt);
+ RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(lc);
if (otherrte->rtekind == RTE_JOIN)
otherrte->joinaliasvars = (List *)
ResolveNew((Node *) otherrte->joinaliasvars,
varno, 0, rte,
- subtlist, CMD_SELECT, 0);
+ subtlist_with_phvs, CMD_SELECT, 0);
}
/*
/*
* Recursively apply pull_up_subqueries to the new child RTE. (We
* must build the AppendRelInfo first, because this will modify it.)
- * Note that we can pass below_outer_join = false even if we're
+ * Note that we can pass NULL for containing-join info even if we're
* actually under an outer join, because the child's expressions
* aren't going to propagate up above the join.
*/
rtr = makeNode(RangeTblRef);
rtr->rtindex = childRTindex;
- (void) pull_up_subqueries(root, (Node *) rtr, false, true);
+ (void) pull_up_subqueries(root, (Node *) rtr, NULL, appinfo);
}
else if (IsA(setOp, SetOperationStmt))
{
* Insert PlaceHolderVar nodes into any non-junk targetlist items that are
* not simple variables or strict functions of simple variables (and hence
* might not correctly go to NULL when examined above the point of an outer
- * join). We assume we can modify the tlist items in-place.
+ * join).
*
* varno is the upper-query relid of the subquery; this is used as the
* syntactic location of the PlaceHolderVars.
insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
int varno, bool wrap_non_vars)
{
+ List *result = NIL;
ListCell *lc;
foreach(lc, tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
+ TargetEntry *newtle;
- /* ignore resjunk columns */
+ /* resjunk columns need not be changed */
if (tle->resjunk)
+ {
+ result = lappend(result, tle);
continue;
+ }
/*
* Simple Vars always escape being wrapped. This is common enough
*/
if (tle->expr && IsA(tle->expr, Var) &&
((Var *) tle->expr)->varlevelsup == 0)
+ {
+ result = lappend(result, tle);
continue;
+ }
if (!wrap_non_vars)
{
*/
if (contain_vars_of_level((Node *) tle->expr, 0) &&
!contain_nonstrict_functions((Node *) tle->expr))
+ {
+ result = lappend(result, tle);
continue;
+ }
}
/* Else wrap it in a PlaceHolderVar */
- tle->expr = (Expr *) make_placeholder_expr(root,
- tle->expr,
- bms_make_singleton(varno));
+ newtle = makeNode(TargetEntry);
+ memcpy(newtle, tle, sizeof(TargetEntry));
+ newtle->expr = (Expr *)
+ make_placeholder_expr(root,
+ tle->expr,
+ bms_make_singleton(varno));
+ result = lappend(result, newtle);
}
- return tlist;
+ return result;
}
/*
* Helper routine for pull_up_subqueries: do ResolveNew on every expression
* in the jointree, without changing the jointree structure itself. Ugly,
* but there's no other way...
+ *
+ * If we are above lowest_outer_join then use subtlist_with_phvs; at or
+ * below it, use subtlist. (When no outer joins are in the picture,
+ * these will be the same list.)
*/
static void
-resolvenew_in_jointree(Node *jtnode, int varno,
- RangeTblEntry *rte, List *subtlist)
+resolvenew_in_jointree(Node *jtnode, int varno, RangeTblEntry *rte,
+ List *subtlist, List *subtlist_with_phvs,
+ JoinExpr *lowest_outer_join)
{
if (jtnode == NULL)
return;
ListCell *l;
foreach(l, f->fromlist)
- resolvenew_in_jointree(lfirst(l), varno, rte, subtlist);
+ resolvenew_in_jointree(lfirst(l), varno, rte,
+ subtlist, subtlist_with_phvs,
+ lowest_outer_join);
f->quals = ResolveNew(f->quals,
varno, 0, rte,
- subtlist, CMD_SELECT, 0);
+ subtlist_with_phvs, CMD_SELECT, 0);
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
- resolvenew_in_jointree(j->larg, varno, rte, subtlist);
- resolvenew_in_jointree(j->rarg, varno, rte, subtlist);
+ if (j == lowest_outer_join)
+ {
+ /* no more PHVs in or below this join */
+ subtlist_with_phvs = subtlist;
+ lowest_outer_join = NULL;
+ }
+ resolvenew_in_jointree(j->larg, varno, rte,
+ subtlist, subtlist_with_phvs,
+ lowest_outer_join);
+ resolvenew_in_jointree(j->rarg, varno, rte,
+ subtlist, subtlist_with_phvs,
+ lowest_outer_join);
j->quals = ResolveNew(j->quals,
varno, 0, rte,
- subtlist, CMD_SELECT, 0);
+ subtlist_with_phvs, CMD_SELECT, 0);
/*
* We don't bother to update the colvars list, since it won't be used