Fix pull_up_sublinks' failure to handle nested pull-up opportunities.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 May 2011 19:56:43 +0000 (15:56 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 May 2011 19:56:43 +0000 (15:56 -0400)
After finding an EXISTS or ANY sub-select that can be converted to a
semi-join or anti-join, we should recurse into the body of the sub-select.
This allows cases such as EXISTS-within-EXISTS to be optimized properly.
The original coding would leave the lower sub-select as a SubLink, which
is no better and often worse than what we can do with a join.  Per example
from Wayne Conrad.

Back-patch to 8.4.  There is a related issue in older versions' handling
of pull_up_IN_clauses, but they're lame enough anyway about the whole area
that it seems not worth the extra work to try to fix.

src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c

index cf503d51135d2b34947cf3eca30b8cb75be96859..30386a38091b92d04fe121cf7b650ae756492f3a 100644 (file)
@@ -995,6 +995,11 @@ SS_process_ctes(PlannerInfo *root)
  * (Notionally, we replace the SubLink with a constant TRUE, then elide the
  * redundant constant from the qual.)
  *
+ * On success, the caller is also responsible for recursively applying
+ * pull_up_sublinks processing to the rarg and quals of the returned JoinExpr.
+ * (On failure, there is no need to do anything, since pull_up_sublinks will
+ * be applied when we recursively plan the sub-select.)
+ *
  * Side effects of a successful conversion include adding the SubLink's
  * subselect to the query's rangetable, so that it can be referenced in
  * the JoinExpr's rarg.
index 5a49768c6ce02d56d637de1c3f9c95d0db824bf7..93e9a94778760038b94a2e8eb455016e5d857933 100644 (file)
@@ -317,6 +317,7 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
        {
                SubLink    *sublink = (SubLink *) node;
                JoinExpr   *j;
+               Relids          child_rels;
 
                /* Is it a convertible ANY or EXISTS clause? */
                if (sublink->subLinkType == ANY_SUBLINK)
@@ -325,7 +326,18 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
                                                                                        available_rels);
                        if (j)
                        {
-                               /* Yes, insert the new join node into the join tree */
+                               /* Yes; recursively process what we pulled up */
+                               j->rarg = pull_up_sublinks_jointree_recurse(root,
+                                                                                                                       j->rarg,
+                                                                                                                       &child_rels);
+                               /* Pulled-up ANY/EXISTS quals can use those rels too */
+                               child_rels = bms_add_members(child_rels, available_rels);
+                               /* ... and any inserted joins get stacked onto j->rarg */
+                               j->quals = pull_up_sublinks_qual_recurse(root,
+                                                                                                                j->quals,
+                                                                                                                child_rels,
+                                                                                                                &j->rarg);
+                               /* Now insert the new join node into the join tree */
                                j->larg = *jtlink;
                                *jtlink = (Node *) j;
                                /* and return NULL representing constant TRUE */
@@ -338,7 +350,18 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
                                                                                           available_rels);
                        if (j)
                        {
-                               /* Yes, insert the new join node into the join tree */
+                               /* Yes; recursively process what we pulled up */
+                               j->rarg = pull_up_sublinks_jointree_recurse(root,
+                                                                                                                       j->rarg,
+                                                                                                                       &child_rels);
+                               /* Pulled-up ANY/EXISTS quals can use those rels too */
+                               child_rels = bms_add_members(child_rels, available_rels);
+                               /* ... and any inserted joins get stacked onto j->rarg */
+                               j->quals = pull_up_sublinks_qual_recurse(root,
+                                                                                                                j->quals,
+                                                                                                                child_rels,
+                                                                                                                &j->rarg);
+                               /* Now insert the new join node into the join tree */
                                j->larg = *jtlink;
                                *jtlink = (Node *) j;
                                /* and return NULL representing constant TRUE */
@@ -353,6 +376,7 @@ 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);
                JoinExpr   *j;
+               Relids          child_rels;
 
                if (sublink && IsA(sublink, SubLink))
                {
@@ -362,7 +386,18 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
                                                                                                   available_rels);
                                if (j)
                                {
-                                       /* Yes, insert the new join node into the join tree */
+                                       /* Yes; recursively process what we pulled up */
+                                       j->rarg = pull_up_sublinks_jointree_recurse(root,
+                                                                                                                               j->rarg,
+                                                                                                                               &child_rels);
+                                       /* Pulled-up ANY/EXISTS quals can use those rels too */
+                                       child_rels = bms_add_members(child_rels, available_rels);
+                                       /* ... and any inserted joins get stacked onto j->rarg */
+                                       j->quals = pull_up_sublinks_qual_recurse(root,
+                                                                                                                        j->quals,
+                                                                                                                        child_rels,
+                                                                                                                        &j->rarg);
+                                       /* Now insert the new join node into the join tree */
                                        j->larg = *jtlink;
                                        *jtlink = (Node *) j;
                                        /* and return NULL representing constant TRUE */