]> granicus.if.org Git - postgresql/commitdiff
Improve sublink pullup code to handle ANY/EXISTS sublinks that are at top
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 17 Aug 2008 01:20:00 +0000 (01:20 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 17 Aug 2008 01:20:00 +0000 (01:20 +0000)
level of a JOIN/ON clause, not only at top level of WHERE.  (However, we
can't do this in an outer join's ON clause, unless the ANY/EXISTS refers
only to the nullable side of the outer join, so that it can effectively
be pushed down into the nullable side.)  Per request from Kevin Grittner.

In passing, fix a bug in the initial implementation of EXISTS pullup:
it would Assert if the EXIST's WHERE clause used a join alias variable.
Since we haven't yet flattened join aliases when this transformation
happens, it's necessary to include join relids in the computed set of
RHS relids.

src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c
src/include/optimizer/prep.h
src/include/optimizer/subselect.h

index 3c08d0def91c89888a63b85fdfe60ec3848a2f5b..49c2cdf2f55ce8e15e0225b7d65b2a6d96405abd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.141 2008/08/14 18:47:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.142 2008/08/17 01:19:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -778,7 +778,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
                                root->hasPseudoConstantQuals = true;
                                /* if not below outer join, push it to top of tree */
                                if (!below_outer_join)
-                                       relids = get_relids_in_jointree((Node *) root->parse->jointree);
+                                       relids =
+                                               get_relids_in_jointree((Node *) root->parse->jointree,
+                                                                                          false);
                        }
                }
        }
index 8b432ba93fbd7d87dcb6e37fc44fd59c982fca7c..40a659391c6fddf25b58d95c5e4a48cec39b74aa 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.241 2008/08/14 18:47:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.242 2008/08/17 01:19:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -268,14 +268,13 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
        root->append_rel_list = NIL;
 
        /*
-        * Look for ANY and EXISTS SubLinks at the top level of WHERE, and try to
-        * transform them into joins.  Note that this step only handles SubLinks
-        * originally at top level of WHERE; if we pull up any subqueries below,
-        * their SubLinks are processed just before pulling them up.
+        * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try
+        * to transform them into joins.  Note that this step does not descend
+        * into subqueries; if we pull up any subqueries below, their SubLinks are
+        * processed just before pulling them up.
         */
        if (parse->hasSubLinks)
-               parse->jointree->quals = pull_up_sublinks(root,
-                                                                                                 parse->jointree->quals);
+               pull_up_sublinks(root);
 
        /*
         * Scan the rangetable for set-returning functions, and inline them
index 1e4e9fe565b74579759e1e256bd3e2a2b869775d..d4374516ac8f0bf16ae8cd94c4557eb200943d0c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.133 2008/08/14 18:47:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.134 2008/08/17 01:20:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -725,18 +725,31 @@ hash_ok_operator(OpExpr *expr)
 /*
  * convert_ANY_sublink_to_join: can we convert an ANY SubLink to a join?
  *
- * The caller has found an ANY SubLink at the top level of WHERE, but has not
- * checked the properties of the SubLink further.  Decide whether it is
- * appropriate to process this SubLink in join style.  If not, return NULL.
- * If so, build the qual clause(s) to replace the SubLink, and return them.
- * The qual clauses are wrapped in a FlattenedSubLink node to help later
- * processing place them properly.
+ * The caller has found an ANY SubLink at the top level of one of the query's
+ * qual clauses, but has not checked the properties of the SubLink further.
+ * Decide whether it is appropriate to process this SubLink in join style.
+ * Return TRUE if so, FALSE if the SubLink cannot be converted.
+ *
+ * The only non-obvious input parameter is available_rels: this is the set
+ * of query rels that can safely be referenced in the sublink expression.
+ * (We must restrict this to avoid changing the semantics when a sublink
+ * is present in an outer join's ON qual.)  The conversion must fail if
+ * the converted qual would reference any but these parent-query relids.
+ *
+ * On success, two output parameters are returned:
+ *     *new_qual is set to the qual tree that should replace the SubLink in
+ *             the parent query's qual tree.  The qual clauses are wrapped in a
+ *             FlattenedSubLink node to help later processing place them properly.
+ *     *fromlist is set to a list of pulled-up jointree item(s) that must be
+ *             added at the proper spot in the parent query's jointree.
  *
  * Side effects of a successful conversion include adding the SubLink's
  * subselect to the query's rangetable.
  */
-Node *
-convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
+bool
+convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
+                                                       Relids available_rels,
+                                                       Node **new_qual, List **fromlist)
 {
        Query      *parse = root->parse;
        Query      *subselect = (Query *) sublink->subselect;
@@ -755,7 +768,7 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
         * higher levels should be okay, though.)
         */
        if (contain_vars_of_level((Node *) subselect, 1))
-               return NULL;
+               return false;
 
        /*
         * The test expression must contain some Vars of the current query,
@@ -764,16 +777,22 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
         */
        left_varnos = pull_varnos(sublink->testexpr);
        if (bms_is_empty(left_varnos))
-               return NULL;
+               return false;
+
+       /*
+        * However, it can't refer to anything outside available_rels.
+        */
+       if (!bms_is_subset(left_varnos, available_rels))
+               return false;
 
        /*
         * The combining operators and left-hand expressions mustn't be volatile.
         */
        if (contain_volatile_functions(sublink->testexpr))
-               return NULL;
+               return false;
 
        /*
-        * Okay, pull up the sub-select into top range table and jointree.
+        * Okay, pull up the sub-select into upper range table.
         *
         * We rely here on the assumption that the outer query has no references
         * to the inner (necessarily true, other than the Vars that we build
@@ -786,16 +805,15 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
                                                                                false);
        parse->rtable = lappend(parse->rtable, rte);
        rtindex = list_length(parse->rtable);
-       rtr = makeNode(RangeTblRef);
-       rtr->rtindex = rtindex;
 
        /*
-        * We assume it's okay to add the pulled-up subquery to the topmost FROM
-        * list.  This should be all right for ANY clauses appearing in WHERE
-        * or in upper-level plain JOIN/ON clauses.  ANYs appearing below any
-        * outer joins couldn't be placed there, however.
+        * Form a RangeTblRef for the pulled-up sub-select.  This must be added
+        * to the upper jointree, but it is caller's responsibility to figure
+        * out where.
         */
-       parse->jointree->fromlist = lappend(parse->jointree->fromlist, rtr);
+       rtr = makeNode(RangeTblRef);
+       rtr->rtindex = rtindex;
+       *fromlist = list_make1(rtr);
 
        /*
         * Build a list of Vars representing the subselect outputs.
@@ -805,14 +823,14 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
                                                                                   rtindex);
 
        /*
-        * Build the result qual expression, replacing Params with these Vars.
+        * Build the replacement qual expression, replacing Params with these Vars.
         */
        quals = (Expr *) convert_testexpr(root,
                                                                          sublink->testexpr,
                                                                          subquery_vars);
 
        /*
-        * Now build the FlattenedSubLink node.
+        * And finally, build the FlattenedSubLink node.
         */
        fslink = makeNode(FlattenedSubLink);
        fslink->jointype = JOIN_SEMI;
@@ -820,7 +838,9 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink)
        fslink->righthand = bms_make_singleton(rtindex);
        fslink->quals = quals;
 
-       return (Node *) fslink;
+       *new_qual = (Node *) fslink;
+
+       return true;
 }
 
 /*
@@ -883,20 +903,15 @@ simplify_EXISTS_query(Query *query)
 /*
  * convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join?
  *
- * The caller has found an EXISTS SubLink at the top level of WHERE, or just
- * underneath a NOT, but has not checked the properties of the SubLink
- * further.  Decide whether it is appropriate to process this SubLink in join
- * style.  If not, return NULL.  If so, build the qual clause(s) to replace
- * the SubLink, and return them.  (In the NOT case, the returned clauses are
- * intended to replace the NOT as well.)  The qual clauses are wrapped in a
- * FlattenedSubLink node to help later processing place them properly.
- *
- * Side effects of a successful conversion include adding the SubLink's
- * subselect to the query's rangetable.
+ * The API of this function is identical to convert_ANY_sublink_to_join's,
+ * except that we also support the case where the caller has found NOT EXISTS,
+ * so we need an additional input parameter "under_not".
  */
-Node *
+bool
 convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
-                                                          bool under_not)
+                                                          bool under_not,
+                                                          Relids available_rels,
+                                                          Node **new_qual, List **fromlist)
 {
        Query      *parse = root->parse;
        Query      *subselect = (Query *) sublink->subselect;
@@ -924,7 +939,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
         * us with noplace to evaluate the targetlist.
         */
        if (!simplify_EXISTS_query(subselect))
-               return NULL;
+               return false;
 
        /*
         * Separate out the WHERE clause.  (We could theoretically also remove
@@ -939,20 +954,20 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
         * query.  (Vars of higher levels should be okay, though.)
         */
        if (contain_vars_of_level((Node *) subselect, 1))
-               return NULL;
+               return false;
 
        /*
         * On the other hand, the WHERE clause must contain some Vars of the
         * parent query, else it's not gonna be a join.
         */
        if (!contain_vars_of_level(whereClause, 1))
-               return NULL;
+               return false;
 
        /*
         * We don't risk optimizing if the WHERE clause is volatile, either.
         */
        if (contain_volatile_functions(whereClause))
-               return NULL;
+               return false;
 
        /*
         * Also disallow SubLinks within the WHERE clause.  (XXX this could
@@ -960,10 +975,10 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
         * below, and it doesn't seem worth worrying about in a first pass.)
         */
        if (contain_subplans(whereClause))
-               return NULL;
+               return false;
 
        /*
-        * Okay, pull up the sub-select into top range table and jointree.
+        * Prepare to pull up the sub-select into top range table.
         *
         * We rely here on the assumption that the outer query has no references
         * to the inner (necessarily true). Therefore this is a lot easier than
@@ -973,7 +988,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
         * to do.  The machinations of simplify_EXISTS_query ensured that there
         * is nothing interesting in the subquery except an rtable and jointree,
         * and even the jointree FromExpr no longer has quals.  So we can just
-        * append the rtable to our own and append the fromlist to our own.
+        * append the rtable to our own and attach the fromlist to our own.
         * But first, adjust all level-zero varnos in the subquery to account
         * for the rtable merger.
         */
@@ -1007,24 +1022,29 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
        bms_free(clause_varnos);
        Assert(!bms_is_empty(left_varnos));
 
-       /* Also identify all the rels syntactically within the subselect */
-       subselect_varnos = get_relids_in_jointree((Node *) subselect->jointree);
+       /*
+        * Now that we've got the set of upper-level varnos, we can make the
+        * last check: only available_rels can be referenced.
+        */
+       if (!bms_is_subset(left_varnos, available_rels))
+               return false;
+
+       /* Identify all the rels syntactically within the subselect */
+       subselect_varnos = get_relids_in_jointree((Node *) subselect->jointree,
+                                                                                         true);
        Assert(bms_is_subset(right_varnos, subselect_varnos));
 
        /* Now we can attach the modified subquery rtable to the parent */
        parse->rtable = list_concat(parse->rtable, subselect->rtable);
 
        /*
-        * We assume it's okay to add the pulled-up subquery to the topmost FROM
-        * list.  This should be all right for EXISTS clauses appearing in WHERE
-        * or in upper-level plain JOIN/ON clauses.  EXISTS appearing below any
-        * outer joins couldn't be placed there, however.
+        * Pass back the subquery fromlist to be attached to upper jointree
+        * in a suitable place.
         */
-       parse->jointree->fromlist = list_concat(parse->jointree->fromlist,
-                                                                                       subselect->jointree->fromlist);
+       *fromlist = subselect->jointree->fromlist;
 
        /*
-        * Now build the FlattenedSubLink node.
+        * And finally, build the FlattenedSubLink node.
         */
        fslink = makeNode(FlattenedSubLink);
        fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
@@ -1032,7 +1052,9 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
        fslink->righthand = subselect_varnos;
        fslink->quals = (Expr *) whereClause;
 
-       return (Node *) fslink;
+       *new_qual = (Node *) fslink;
+
+       return true;
 }
 
 /*
index 241ad668072524c64a3f8424114903bd5de7b42d..24e9fdb0b0aab7e74803c9b21eceb7e8d80581a0 100644 (file)
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.52 2008/08/14 20:31:29 heikki Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.53 2008/08/17 01:20:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,10 @@ typedef struct reduce_outer_joins_state
        List       *sub_states;         /* List of states for subtree components */
 } 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);
 static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
                                                RangeTblEntry *rte,
                                                bool below_outer_join,
@@ -76,51 +80,228 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
 
 /*
  * pull_up_sublinks
- *             Attempt to pull up top-level ANY and EXISTS SubLinks to be treated
- *             as semijoins or anti-semijoins.
+ *             Attempt to pull up ANY and EXISTS SubLinks to be treated as
+ *             semijoins or anti-semijoins.
  *
- * A clause "foo op ANY (sub-SELECT)" appearing at the top level of WHERE
- * can be processed by pulling the sub-SELECT up to become a rangetable entry
- * and handling the implied comparisons as quals of a semijoin.
- * This optimization *only* works at the top level of WHERE, because
- * it cannot distinguish whether the ANY ought to return FALSE or NULL in
- * cases involving NULL inputs.  Similarly, EXISTS and NOT EXISTS clauses
- * can be handled by pulling up the sub-SELECT and creating a semijoin
- * or anti-semijoin respectively.
+ * A clause "foo op ANY (sub-SELECT)" can be processed by pulling the
+ * sub-SELECT up to become a rangetable entry and treating the implied
+ * comparisons as quals of a semijoin.  However, this optimization *only*
+ * works at the top level of WHERE or a JOIN/ON clause, because we cannot
+ * 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
+ * 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.
+ *
+ * Under similar conditions, EXISTS and NOT EXISTS clauses can be handled
+ * by pulling up the sub-SELECT and creating a semijoin or anti-semijoin.
  *
  * This routine searches for such clauses and does the necessary parsetree
  * transformations if any are found.
  *
- * This routine has to run before preprocess_expression(), so the WHERE
- * clause is not yet reduced to implicit-AND format.  That means we need
+ * This routine has to run before preprocess_expression(), so the quals
+ * clauses are not yet reduced to implicit-AND format.  That means we need
  * to recursively search through explicit AND clauses, which are
  * probably only binary ANDs.  We stop as soon as we hit a non-AND item.
+ */
+void
+pull_up_sublinks(PlannerInfo *root)
+{
+       Relids          relids;
+
+       /* Begin recursion through the jointree */
+       root->parse->jointree = (FromExpr *)
+               pull_up_sublinks_jointree_recurse(root,
+                                                                                 (Node *) root->parse->jointree,
+                                                                                 &relids);
+}
+
+/*
+ * Recurse through jointree nodes for pull_up_sublinks()
  *
- * Returns the possibly-modified version of the given qual-tree node.
- * There may be side-effects on the query's rtable and jointree, too.
+ * In addition to returning the possibly-modified jointree node, we return
+ * a relids set of the contained rels into *relids.
  */
-Node *
-pull_up_sublinks(PlannerInfo *root, Node *node)
+static Node *
+pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
+                                                                 Relids *relids)
+{
+       if (jtnode == NULL)
+       {
+               *relids = NULL;
+       }
+       else if (IsA(jtnode, RangeTblRef))
+       {
+               int                     varno = ((RangeTblRef *) jtnode)->rtindex;
+
+               *relids = bms_make_singleton(varno);
+               /* jtnode is returned unmodified */
+       }
+       else if (IsA(jtnode, FromExpr))
+       {
+               FromExpr   *f = (FromExpr *) jtnode;
+               List       *newfromlist = NIL;
+               Node       *newquals;
+               List       *subfromlist = NIL;
+               Relids          frelids = NULL;
+               ListCell   *l;
+
+               /* First, recurse to process children and collect their relids */
+               foreach(l, f->fromlist)
+               {
+                       Node   *newchild;
+                       Relids  childrelids;
+
+                       newchild = pull_up_sublinks_jointree_recurse(root,
+                                                                                                                lfirst(l),
+                                                                                                                &childrelids);
+                       newfromlist = lappend(newfromlist, newchild);
+                       frelids = bms_join(frelids, childrelids);
+               }
+               /* 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);
+
+               /*
+                * 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);
+       }
+       else if (IsA(jtnode, JoinExpr))
+       {
+               JoinExpr   *j;
+               Relids          leftrelids;
+               Relids          rightrelids;
+               List       *subfromlist = NIL;
+
+               /*
+                * Make a modifiable copy of join node, but don't bother copying
+                * its subnodes (yet).
+                */
+               j = (JoinExpr *) palloc(sizeof(JoinExpr));
+               memcpy(j, jtnode, sizeof(JoinExpr));
+
+               /* Recurse to process children and collect their relids */
+               j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,
+                                                                                                       &leftrelids);
+               j->rarg = pull_up_sublinks_jointree_recurse(root, j->rarg,
+                                                                                                       &rightrelids);
+
+               /*
+                * 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.
+                */
+               switch (j->jointype)
+               {
+                       case JOIN_INNER:
+                               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);
+                               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);
+                               break;
+                       case JOIN_FULL:
+                               /* can't do anything with full-join quals */
+                               break;
+                       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);
+                               break;
+                       default:
+                               elog(ERROR, "unrecognized join type: %d",
+                                        (int) j->jointype);
+                               break;
+               }
+
+               /*
+                * 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.  But we *do* need to include the join's own rtindex
+                * because we haven't yet collapsed join alias variables, so upper
+                * 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;
+       }
+       else
+               elog(ERROR, "unrecognized node type: %d",
+                        (int) nodeTag(jtnode));
+       return 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.
+ */
+static Node *
+pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
+                                                         Relids available_rels, List **fromlist)
 {
        if (node == NULL)
                return NULL;
        if (IsA(node, SubLink))
        {
                SubLink    *sublink = (SubLink *) node;
-               Node       *subst;
+               Node       *new_qual;
+               List       *new_fromlist;
 
                /* Is it a convertible ANY or EXISTS clause? */
                if (sublink->subLinkType == ANY_SUBLINK)
                {
-                       subst = convert_ANY_sublink_to_join(root, sublink);
-                       if (subst)
-                               return subst;
+                       if (convert_ANY_sublink_to_join(root, sublink,
+                                                                                       available_rels,
+                                                                                       &new_qual, &new_fromlist))
+                       {
+                               *fromlist = list_concat(*fromlist, new_fromlist);
+                               return new_qual;
+                       }
                }
                else if (sublink->subLinkType == EXISTS_SUBLINK)
                {
-                       subst = convert_EXISTS_sublink_to_join(root, sublink, false);
-                       if (subst)
-                               return subst;
+                       if (convert_EXISTS_sublink_to_join(root, sublink, false,
+                                                                                          available_rels,
+                                                                                          &new_qual, &new_fromlist))
+                       {
+                               *fromlist = list_concat(*fromlist, new_fromlist);
+                               return new_qual;
+                       }
                }
                /* Else return it unmodified */
                return node;
@@ -129,15 +310,20 @@ pull_up_sublinks(PlannerInfo *root, Node *node)
        {
                /* If the immediate argument of NOT is EXISTS, try to convert */
                SubLink    *sublink = (SubLink *) get_notclausearg((Expr *) node);
-               Node       *subst;
+               Node       *new_qual;
+               List       *new_fromlist;
 
                if (sublink && IsA(sublink, SubLink))
                {
                        if (sublink->subLinkType == EXISTS_SUBLINK)
                        {
-                               subst = convert_EXISTS_sublink_to_join(root, sublink, true);
-                               if (subst)
-                                       return subst;
+                               if (convert_EXISTS_sublink_to_join(root, sublink, true,
+                                                                                                  available_rels,
+                                                                                                  &new_qual, &new_fromlist))
+                               {
+                                       *fromlist = list_concat(*fromlist, new_fromlist);
+                                       return new_qual;
+                               }
                        }
                }
                /* Else return it unmodified */
@@ -145,6 +331,7 @@ pull_up_sublinks(PlannerInfo *root, Node *node)
        }
        if (and_clause(node))
        {
+               /* Recurse into AND clause */
                List       *newclauses = NIL;
                ListCell   *l;
 
@@ -153,7 +340,10 @@ pull_up_sublinks(PlannerInfo *root, Node *node)
                        Node       *oldclause = (Node *) lfirst(l);
 
                        newclauses = lappend(newclauses,
-                                                                pull_up_sublinks(root, oldclause));
+                                                                pull_up_sublinks_qual_recurse(root,
+                                                                                                                          oldclause,
+                                                                                                                          available_rels,
+                                                                                                                          fromlist));
                }
                return (Node *) make_andclause(newclauses);
        }
@@ -383,12 +573,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
        subroot->append_rel_list = NIL;
 
        /*
-        * Pull up any SubLinks within the subquery's WHERE, so that we don't
+        * Pull up any SubLinks within the subquery's quals, so that we don't
         * leave unoptimized SubLinks behind.
         */
        if (subquery->hasSubLinks)
-               subquery->jointree->quals = pull_up_sublinks(subroot,
-                                                                                                 subquery->jointree->quals);
+               pull_up_sublinks(subroot);
 
        /*
         * Similarly, inline any set-returning functions in its rangetable.
@@ -516,7 +705,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
        {
                Relids          subrelids;
 
-               subrelids = get_relids_in_jointree((Node *) subquery->jointree);
+               subrelids = get_relids_in_jointree((Node *) subquery->jointree, false);
                fix_flattened_sublink_relids((Node *) parse, varno, subrelids);
                fix_append_rel_relids(root->append_rel_list, varno, subrelids);
        }
@@ -1484,10 +1673,13 @@ fix_append_rel_relids(List *append_rel_list, int varno, Relids subrelids)
 }
 
 /*
- * get_relids_in_jointree: get set of base RT indexes present in a jointree
+ * get_relids_in_jointree: get set of RT indexes present in a jointree
+ *
+ * If include_joins is true, join RT indexes are included; if false,
+ * only base rels are included.
  */
 Relids
-get_relids_in_jointree(Node *jtnode)
+get_relids_in_jointree(Node *jtnode, bool include_joins)
 {
        Relids          result = NULL;
 
@@ -1507,16 +1699,19 @@ get_relids_in_jointree(Node *jtnode)
                foreach(l, f->fromlist)
                {
                        result = bms_join(result,
-                                                         get_relids_in_jointree(lfirst(l)));
+                                                         get_relids_in_jointree(lfirst(l),
+                                                                                                        include_joins));
                }
        }
        else if (IsA(jtnode, JoinExpr))
        {
                JoinExpr   *j = (JoinExpr *) jtnode;
 
-               /* join's own RT index is not wanted in result */
-               result = get_relids_in_jointree(j->larg);
-               result = bms_join(result, get_relids_in_jointree(j->rarg));
+               result = get_relids_in_jointree(j->larg, include_joins);
+               result = bms_join(result,
+                                                 get_relids_in_jointree(j->rarg, include_joins));
+               if (include_joins)
+                       result = bms_add_member(result, j->rtindex);
        }
        else
                elog(ERROR, "unrecognized node type: %d",
@@ -1536,7 +1731,7 @@ get_relids_for_join(PlannerInfo *root, int joinrelid)
                                                                                joinrelid);
        if (!jtnode)
                elog(ERROR, "could not find join node %d", joinrelid);
-       return get_relids_in_jointree(jtnode);
+       return get_relids_in_jointree(jtnode, false);
 }
 
 /*
index 1a90a13208c5ef130b10ed88f997cb1fb1b98a08..9326623b91a4106062bebedfa0d0ee0966f6fc15 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.61 2008/08/14 18:48:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.62 2008/08/17 01:20:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 /*
  * prototypes for prepjointree.c
  */
-extern Node *pull_up_sublinks(PlannerInfo *root, Node *node);
+extern void pull_up_sublinks(PlannerInfo *root);
 extern void inline_set_returning_functions(PlannerInfo *root);
 extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
                                   bool below_outer_join, bool append_rel_member);
 extern void reduce_outer_joins(PlannerInfo *root);
-extern Relids get_relids_in_jointree(Node *jtnode);
+extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);
 extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);
 
 /*
index b9bd76b07ff4b856276cb8305f6360ec4f37eef5..97e230a2dbf2dd44a4cb73b4af694c5e64c14a51 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.32 2008/08/14 18:48:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.33 2008/08/17 01:20:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "nodes/plannodes.h"
 #include "nodes/relation.h"
 
-extern Node *convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink);
-extern Node *convert_EXISTS_sublink_to_join(PlannerInfo *root,
-                                                                                       SubLink *sublink, bool under_not);
+extern bool convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
+                                                                               Relids available_rels,
+                                                                               Node **new_qual, List **fromlist);
+extern bool convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
+                                                                                  bool under_not,
+                                                                                  Relids available_rels,
+                                                                                  Node **new_qual, List **fromlist);
 extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr);
 extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual);
 extern void SS_finalize_plan(PlannerInfo *root, Plan *plan,