summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/prep/prepjointree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/prep/prepjointree.c')
-rw-r--r--src/backend/optimizer/prep/prepjointree.c226
1 files changed, 119 insertions, 107 deletions
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 9896b10280..600facb578 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -16,7 +16,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.62 2009/01/01 17:23:44 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.63 2009/02/25 03:30:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -44,7 +44,7 @@ typedef struct reduce_outer_joins_state
static Node *pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
Relids *relids);
static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
- Relids available_rels, List **fromlist);
+ Relids available_rels, Node **jtlink);
static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
RangeTblEntry *rte,
bool below_outer_join,
@@ -91,7 +91,7 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
* distinguish whether the ANY ought to return FALSE or NULL in cases
* involving NULL inputs. Also, in an outer join's ON clause we can only
* do this if the sublink is degenerate (ie, references only the nullable
- * side of the join). In that case we can effectively push the semijoin
+ * side of the join). In that case it is legal to push the semijoin
* down into the nullable side of the join. If the sublink references any
* nonnullable-side variables then it would have to be evaluated as part
* of the outer join, which makes things way too complicated.
@@ -110,13 +110,22 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
void
pull_up_sublinks(PlannerInfo *root)
{
+ Node *jtnode;
Relids relids;
/* Begin recursion through the jointree */
- root->parse->jointree = (FromExpr *)
- pull_up_sublinks_jointree_recurse(root,
- (Node *) root->parse->jointree,
- &relids);
+ jtnode = pull_up_sublinks_jointree_recurse(root,
+ (Node *) root->parse->jointree,
+ &relids);
+
+ /*
+ * root->parse->jointree must always be a FromExpr, so insert a dummy one
+ * if we got a bare RangeTblRef or JoinExpr out of the recursion.
+ */
+ if (IsA(jtnode, FromExpr))
+ root->parse->jointree = (FromExpr *) jtnode;
+ else
+ root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL);
}
/*
@@ -144,9 +153,9 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
{
FromExpr *f = (FromExpr *) jtnode;
List *newfromlist = NIL;
- Node *newquals;
- List *subfromlist = NIL;
Relids frelids = NULL;
+ FromExpr *newf;
+ Node *jtlink;
ListCell *l;
/* First, recurse to process children and collect their relids */
@@ -161,26 +170,32 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
newfromlist = lappend(newfromlist, newchild);
frelids = bms_join(frelids, childrelids);
}
+ /* Build the replacement FromExpr; no quals yet */
+ newf = makeFromExpr(newfromlist, NULL);
+ /* Set up a link representing the rebuilt jointree */
+ jtlink = (Node *) newf;
/* Now process qual --- all children are available for use */
- newquals = pull_up_sublinks_qual_recurse(root, f->quals, frelids,
- &subfromlist);
- /* Any pulled-up subqueries can just be attached to the fromlist */
- newfromlist = list_concat(newfromlist, subfromlist);
+ newf->quals = pull_up_sublinks_qual_recurse(root, f->quals, frelids,
+ &jtlink);
/*
+ * Note that the result will be either newf, or a stack of JoinExprs
+ * with newf at the base. We rely on subsequent optimization steps
+ * to flatten this and rearrange the joins as needed.
+ *
* Although we could include the pulled-up subqueries in the returned
* relids, there's no need since upper quals couldn't refer to their
* outputs anyway.
*/
*relids = frelids;
- jtnode = (Node *) makeFromExpr(newfromlist, newquals);
+ jtnode = jtlink;
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j;
Relids leftrelids;
Relids rightrelids;
- List *subfromlist = NIL;
+ Node *jtlink;
/*
* Make a modifiable copy of join node, but don't bother copying
@@ -188,6 +203,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
*/
j = (JoinExpr *) palloc(sizeof(JoinExpr));
memcpy(j, jtnode, sizeof(JoinExpr));
+ jtlink = (Node *) j;
/* Recurse to process children and collect their relids */
j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,
@@ -197,13 +213,15 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
/*
* Now process qual, showing appropriate child relids as available,
- * and then attach any pulled-up jointree items at the right place.
- * The pulled-up items must go below where the quals that refer to
- * them will be placed. Since the JoinExpr itself can only handle
- * two child nodes, we hack up a valid jointree by inserting dummy
- * FromExprs that have no quals. These should get flattened out
- * during deconstruct_recurse(), so they won't impose any extra
- * overhead.
+ * and attach any pulled-up jointree items at the right place.
+ * In the inner-join case we put new JoinExprs above the existing one
+ * (much as for a FromExpr-style join). In outer-join cases the
+ * new JoinExprs must go into the nullable side of the outer join.
+ * The point of the available_rels machinations is to ensure that we
+ * only pull up quals for which that's okay.
+ *
+ * We don't expect to see any pre-existing JOIN_SEMI or JOIN_ANTI
+ * nodes here.
*/
switch (j->jointype)
{
@@ -211,22 +229,12 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
bms_union(leftrelids,
rightrelids),
- &subfromlist);
- /* We arbitrarily put pulled-up subqueries into right child */
- if (subfromlist)
- j->rarg = (Node *) makeFromExpr(lcons(j->rarg,
- subfromlist),
- NULL);
+ &jtlink);
break;
case JOIN_LEFT:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
rightrelids,
- &subfromlist);
- /* Any pulled-up subqueries must go into right child */
- if (subfromlist)
- j->rarg = (Node *) makeFromExpr(lcons(j->rarg,
- subfromlist),
- NULL);
+ &j->rarg);
break;
case JOIN_FULL:
/* can't do anything with full-join quals */
@@ -234,12 +242,7 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
case JOIN_RIGHT:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
leftrelids,
- &subfromlist);
- /* Any pulled-up subqueries must go into left child */
- if (subfromlist)
- j->larg = (Node *) makeFromExpr(lcons(j->larg,
- subfromlist),
- NULL);
+ &j->larg);
break;
default:
elog(ERROR, "unrecognized join type: %d",
@@ -255,9 +258,10 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
* levels would mistakenly think they couldn't use references to this
* join.
*/
- *relids = bms_add_member(bms_join(leftrelids, rightrelids),
- j->rtindex);
- jtnode = (Node *) j;
+ *relids = bms_join(leftrelids, rightrelids);
+ if (j->rtindex)
+ *relids = bms_add_member(*relids, j->rtindex);
+ jtnode = jtlink;
}
else
elog(ERROR, "unrecognized node type: %d",
@@ -268,40 +272,47 @@ pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
/*
* Recurse through top-level qual nodes for pull_up_sublinks()
*
- * Caller must have initialized *fromlist to NIL. We append any new
- * jointree items to that list.
+ * jtlink points to the link in the jointree where any new JoinExprs should be
+ * inserted. If we find multiple pull-up-able SubLinks, they'll get stacked
+ * there in the order we encounter them. We rely on subsequent optimization
+ * to rearrange the stack if appropriate.
*/
static Node *
pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
- Relids available_rels, List **fromlist)
+ Relids available_rels, Node **jtlink)
{
if (node == NULL)
return NULL;
if (IsA(node, SubLink))
{
SubLink *sublink = (SubLink *) node;
- Node *new_qual;
- List *new_fromlist;
+ JoinExpr *j;
/* Is it a convertible ANY or EXISTS clause? */
if (sublink->subLinkType == ANY_SUBLINK)
{
- if (convert_ANY_sublink_to_join(root, sublink,
- available_rels,
- &new_qual, &new_fromlist))
+ j = convert_ANY_sublink_to_join(root, sublink,
+ available_rels);
+ if (j)
{
- *fromlist = list_concat(*fromlist, new_fromlist);
- return new_qual;
+ /* Yes, insert the new join node into the join tree */
+ j->larg = *jtlink;
+ *jtlink = (Node *) j;
+ /* and return NULL representing constant TRUE */
+ return NULL;
}
}
else if (sublink->subLinkType == EXISTS_SUBLINK)
{
- if (convert_EXISTS_sublink_to_join(root, sublink, false,
- available_rels,
- &new_qual, &new_fromlist))
+ j = convert_EXISTS_sublink_to_join(root, sublink, false,
+ available_rels);
+ if (j)
{
- *fromlist = list_concat(*fromlist, new_fromlist);
- return new_qual;
+ /* Yes, insert the new join node into the join tree */
+ j->larg = *jtlink;
+ *jtlink = (Node *) j;
+ /* and return NULL representing constant TRUE */
+ return NULL;
}
}
/* Else return it unmodified */
@@ -311,19 +322,21 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
{
/* If the immediate argument of NOT is EXISTS, try to convert */
SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node);
- Node *new_qual;
- List *new_fromlist;
+ JoinExpr *j;
if (sublink && IsA(sublink, SubLink))
{
if (sublink->subLinkType == EXISTS_SUBLINK)
{
- if (convert_EXISTS_sublink_to_join(root, sublink, true,
- available_rels,
- &new_qual, &new_fromlist))
+ j = convert_EXISTS_sublink_to_join(root, sublink, true,
+ available_rels);
+ if (j)
{
- *fromlist = list_concat(*fromlist, new_fromlist);
- return new_qual;
+ /* Yes, insert the new join node into the join tree */
+ j->larg = *jtlink;
+ *jtlink = (Node *) j;
+ /* and return NULL representing constant TRUE */
+ return NULL;
}
}
}
@@ -339,14 +352,22 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
foreach(l, ((BoolExpr *) node)->args)
{
Node *oldclause = (Node *) lfirst(l);
-
- newclauses = lappend(newclauses,
- pull_up_sublinks_qual_recurse(root,
- oldclause,
- available_rels,
- fromlist));
+ Node *newclause;
+
+ newclause = pull_up_sublinks_qual_recurse(root,
+ oldclause,
+ available_rels,
+ jtlink);
+ if (newclause)
+ newclauses = lappend(newclauses, newclause);
}
- return (Node *) make_andclause(newclauses);
+ /* We might have got back fewer clauses than we started with */
+ if (newclauses == NIL)
+ return NULL;
+ else if (list_length(newclauses) == 1)
+ return (Node *) linitial(newclauses);
+ else
+ return (Node *) make_andclause(newclauses);
}
/* Stop if not an AND */
return node;
@@ -489,6 +510,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
below_outer_join, false);
break;
case JOIN_LEFT:
+ case JOIN_SEMI:
+ case JOIN_ANTI:
j->larg = pull_up_subqueries(root, j->larg,
below_outer_join, false);
j->rarg = pull_up_subqueries(root, j->rarg,
@@ -702,12 +725,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
/*
- * We also have to fix the relid sets of any FlattenedSubLink and
- * PlaceHolderVar nodes in the parent query. (This could perhaps be done
- * by ResolveNew, but it would clutter that routine's API unreasonably.)
- * Note in particular that any PlaceHolderVar nodes just created by
- * insert_targetlist_placeholders() will be adjusted, so having created
- * them with the subquery's varno is correct.
+ * We also have to fix the relid sets of any PlaceHolderVar nodes in the
+ * parent query. (This could perhaps be done by ResolveNew, but it would
+ * clutter that routine's API unreasonably.) Note in particular that any
+ * PlaceHolderVar nodes just created by insert_targetlist_placeholders()
+ * will be adjusted, so having created them with the subquery's varno is
+ * correct.
*
* Likewise, relids appearing in AppendRelInfo nodes have to be fixed.
* We already checked that this won't require introducing multiple
@@ -1419,6 +1442,14 @@ reduce_outer_joins_pass2(Node *jtnode,
jointype = JOIN_RIGHT;
}
break;
+ case JOIN_SEMI:
+ case JOIN_ANTI:
+ /*
+ * These could only have been introduced by pull_up_sublinks,
+ * so there's no way that upper quals could refer to their
+ * righthand sides, and no point in checking.
+ */
+ break;
default:
elog(ERROR, "unrecognized join type: %d",
(int) jointype);
@@ -1475,14 +1506,15 @@ reduce_outer_joins_pass2(Node *jtnode,
}
/* Apply the jointype change, if any, to both jointree node and RTE */
- if (jointype != j->jointype)
+ if (rtindex && jointype != j->jointype)
{
RangeTblEntry *rte = rt_fetch(rtindex, root->parse->rtable);
Assert(rte->rtekind == RTE_JOIN);
Assert(rte->jointype == j->jointype);
- rte->jointype = j->jointype = jointype;
+ rte->jointype = jointype;
}
+ j->jointype = jointype;
/* Only recurse if there's more to do below here */
if (left_state->contains_outer || right_state->contains_outer)
@@ -1542,7 +1574,7 @@ reduce_outer_joins_pass2(Node *jtnode,
pass_nonnullable_vars = local_nonnullable_vars;
pass_forced_null_vars = local_forced_null_vars;
}
- else if (jointype != JOIN_FULL) /* ie, LEFT or ANTI */
+ else if (jointype != JOIN_FULL) /* ie, LEFT/SEMI/ANTI */
{
/* can't pass local constraints to non-nullable side */
pass_nonnullable_rels = nonnullable_rels;
@@ -1564,7 +1596,7 @@ reduce_outer_joins_pass2(Node *jtnode,
if (right_state->contains_outer)
{
- if (jointype != JOIN_FULL) /* ie, INNER, LEFT or ANTI */
+ if (jointype != JOIN_FULL) /* ie, INNER/LEFT/SEMI/ANTI */
{
/* pass appropriate constraints, per comment above */
pass_nonnullable_rels = local_nonnullable_rels;
@@ -1595,10 +1627,10 @@ reduce_outer_joins_pass2(Node *jtnode,
* substitute_multiple_relids - adjust node relid sets after pulling up
* a subquery
*
- * Find any FlattenedSubLink or PlaceHolderVar nodes in the given tree that
- * reference the pulled-up relid, and change them to reference the replacement
- * relid(s). We do not need to recurse into subqueries, since no subquery of
- * the current top query could (yet) contain such a reference.
+ * Find any PlaceHolderVar nodes in the given tree that reference the
+ * pulled-up relid, and change them to reference the replacement relid(s).
+ * We do not need to recurse into subqueries, since no subquery of the current
+ * top query could (yet) contain such a reference.
*
* NOTE: although this has the form of a walker, we cheat and modify the
* nodes in-place. This should be OK since the tree was copied by ResolveNew
@@ -1618,26 +1650,6 @@ substitute_multiple_relids_walker(Node *node,
{
if (node == NULL)
return false;
- if (IsA(node, FlattenedSubLink))
- {
- FlattenedSubLink *fslink = (FlattenedSubLink *) node;
-
- if (bms_is_member(context->varno, fslink->lefthand))
- {
- fslink->lefthand = bms_union(fslink->lefthand,
- context->subrelids);
- fslink->lefthand = bms_del_member(fslink->lefthand,
- context->varno);
- }
- if (bms_is_member(context->varno, fslink->righthand))
- {
- fslink->righthand = bms_union(fslink->righthand,
- context->subrelids);
- fslink->righthand = bms_del_member(fslink->righthand,
- context->varno);
- }
- /* fall through to examine children */
- }
if (IsA(node, PlaceHolderVar))
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
@@ -1757,7 +1769,7 @@ get_relids_in_jointree(Node *jtnode, bool include_joins)
result = get_relids_in_jointree(j->larg, include_joins);
result = bms_join(result,
get_relids_in_jointree(j->rarg, include_joins));
- if (include_joins)
+ if (include_joins && j->rtindex)
result = bms_add_member(result, j->rtindex);
}
else