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, Node **jtlink);
+ Node **jtlink1, Relids available_rels1,
+ Node **jtlink2, Relids available_rels2);
static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
RangeTblEntry *rte,
JoinExpr *lowest_outer_join,
/* Set up a link representing the rebuilt jointree */
jtlink = (Node *) newf;
/* Now process qual --- all children are available for use */
- newf->quals = pull_up_sublinks_qual_recurse(root, f->quals, frelids,
- &jtlink);
+ newf->quals = pull_up_sublinks_qual_recurse(root, f->quals,
+ &jtlink, frelids,
+ NULL, NULL);
/*
* Note that the result will be either newf, or a stack of JoinExprs
* point of the available_rels machinations is to ensure that we only
* pull up quals for which that's okay.
*
- * XXX for the moment, we refrain from pulling up IN/EXISTS clauses
- * appearing in LEFT or RIGHT join conditions. Although it is
- * semantically valid to do so under the above conditions, we end up
- * with a query in which the semijoin or antijoin must be evaluated
- * below the outer join, which could perform far worse than leaving it
- * as a sublink that is executed only for row pairs that meet the
- * other join conditions. Fixing this seems to require considerable
- * restructuring of the executor, but maybe someday it can happen.
- * (See also the comparable case in pull_up_sublinks_qual_recurse.)
- *
* We don't expect to see any pre-existing JOIN_SEMI or JOIN_ANTI
* nodes here.
*/
{
case JOIN_INNER:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
+ &jtlink,
bms_union(leftrelids,
rightrelids),
- &jtlink);
+ NULL, NULL);
break;
case JOIN_LEFT:
-#ifdef NOT_USED /* see XXX comment above */
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
+ &j->rarg,
rightrelids,
- &j->rarg);
-#endif
+ NULL, NULL);
break;
case JOIN_FULL:
/* can't do anything with full-join quals */
break;
case JOIN_RIGHT:
-#ifdef NOT_USED /* see XXX comment above */
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
+ &j->larg,
leftrelids,
- &j->larg);
-#endif
+ NULL, NULL);
break;
default:
elog(ERROR, "unrecognized join type: %d",
/*
* Recurse through top-level qual nodes for pull_up_sublinks()
*
- * 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.
+ * jtlink1 points to the link in the jointree where any new JoinExprs should
+ * be inserted if they reference available_rels1 (i.e., available_rels1
+ * denotes the relations present underneath jtlink1). Optionally, jtlink2 can
+ * point to a second link where new JoinExprs should be inserted if they
+ * reference available_rels2 (pass NULL for both those arguments if not used).
+ * Note that SubLinks referencing both sets of variables cannot be optimized.
+ * If we find multiple pull-up-able SubLinks, they'll get stacked onto jtlink1
+ * and/or jtlink2 in the order we encounter them. We rely on subsequent
+ * optimization to rearrange the stack if appropriate.
+ *
+ * Returns the replacement qual node, or NULL if the qual should be removed.
*/
static Node *
pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
- Relids available_rels, Node **jtlink)
+ Node **jtlink1, Relids available_rels1,
+ Node **jtlink2, Relids available_rels2)
{
if (node == NULL)
return NULL;
/* Is it a convertible ANY or EXISTS clause? */
if (sublink->subLinkType == ANY_SUBLINK)
{
- j = convert_ANY_sublink_to_join(root, sublink,
- available_rels);
- if (j)
+ if ((j = convert_ANY_sublink_to_join(root, sublink,
+ available_rels1)) != NULL)
+ {
+ /* Yes; insert the new join node into the join tree */
+ j->larg = *jtlink1;
+ *jtlink1 = (Node *) j;
+ /* Recursively process pulled-up jointree nodes */
+ j->rarg = pull_up_sublinks_jointree_recurse(root,
+ j->rarg,
+ &child_rels);
+ /*
+ * Now recursively process the pulled-up quals. Any inserted
+ * joins can get stacked onto either j->larg or j->rarg,
+ * depending on which rels they reference.
+ */
+ j->quals = pull_up_sublinks_qual_recurse(root,
+ j->quals,
+ &j->larg,
+ available_rels1,
+ &j->rarg,
+ child_rels);
+ /* Return NULL representing constant TRUE */
+ return NULL;
+ }
+ if (available_rels2 != NULL &&
+ (j = convert_ANY_sublink_to_join(root, sublink,
+ available_rels2)) != NULL)
{
- /* Yes; recursively process what we pulled up */
+ /* Yes; insert the new join node into the join tree */
+ j->larg = *jtlink2;
+ *jtlink2 = (Node *) j;
+ /* Recursively process pulled-up jointree nodes */
j->rarg = pull_up_sublinks_jointree_recurse(root,
j->rarg,
&child_rels);
- /* Any inserted joins get stacked onto j->rarg */
+ /*
+ * Now recursively process the pulled-up quals. Any inserted
+ * joins can get stacked onto either j->larg or j->rarg,
+ * depending on which rels they reference.
+ */
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 */
+ &j->larg,
+ available_rels2,
+ &j->rarg,
+ child_rels);
+ /* Return NULL representing constant TRUE */
return NULL;
}
}
else if (sublink->subLinkType == EXISTS_SUBLINK)
{
- j = convert_EXISTS_sublink_to_join(root, sublink, false,
- available_rels);
- if (j)
+ if ((j = convert_EXISTS_sublink_to_join(root, sublink, false,
+ available_rels1)) != NULL)
{
- /* Yes; recursively process what we pulled up */
+ /* Yes; insert the new join node into the join tree */
+ j->larg = *jtlink1;
+ *jtlink1 = (Node *) j;
+ /* Recursively process pulled-up jointree nodes */
j->rarg = pull_up_sublinks_jointree_recurse(root,
j->rarg,
&child_rels);
- /* Any inserted joins get stacked onto j->rarg */
+ /*
+ * Now recursively process the pulled-up quals. Any inserted
+ * joins can get stacked onto either j->larg or j->rarg,
+ * depending on which rels they reference.
+ */
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 */
+ &j->larg,
+ available_rels1,
+ &j->rarg,
+ child_rels);
+ /* Return NULL representing constant TRUE */
+ return NULL;
+ }
+ if (available_rels2 != NULL &&
+ (j = convert_EXISTS_sublink_to_join(root, sublink, false,
+ available_rels2)) != NULL)
+ {
+ /* Yes; insert the new join node into the join tree */
+ j->larg = *jtlink2;
+ *jtlink2 = (Node *) j;
+ /* Recursively process pulled-up jointree nodes */
+ j->rarg = pull_up_sublinks_jointree_recurse(root,
+ j->rarg,
+ &child_rels);
+ /*
+ * Now recursively process the pulled-up quals. Any inserted
+ * joins can get stacked onto either j->larg or j->rarg,
+ * depending on which rels they reference.
+ */
+ j->quals = pull_up_sublinks_qual_recurse(root,
+ j->quals,
+ &j->larg,
+ available_rels2,
+ &j->rarg,
+ child_rels);
+ /* Return NULL representing constant TRUE */
return NULL;
}
}
/* 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))
{
if (sublink->subLinkType == EXISTS_SUBLINK)
{
- j = convert_EXISTS_sublink_to_join(root, sublink, true,
- available_rels);
- if (j)
+ if ((j = convert_EXISTS_sublink_to_join(root, sublink, true,
+ available_rels1)) != NULL)
{
+ /* Yes; insert the new join node into the join tree */
+ j->larg = *jtlink1;
+ *jtlink1 = (Node *) j;
+ /* Recursively process pulled-up jointree nodes */
+ j->rarg = pull_up_sublinks_jointree_recurse(root,
+ j->rarg,
+ &child_rels);
/*
- * For the moment, refrain from recursing underneath NOT.
- * As in pull_up_sublinks_jointree_recurse, recursing here
- * would result in inserting a join underneath an ANTI
- * join with which it could not commute, and that could
- * easily lead to a worse plan than what we've
- * historically generated.
+ * Now recursively process the pulled-up quals. Because
+ * we are underneath a NOT, we can't pull up sublinks
+ * that reference the left-hand stuff, but it's still
+ * okay to pull up sublinks referencing j->rarg.
*/
-#ifdef NOT_USED
- /* Yes; recursively process what we pulled up */
- Relids child_rels;
-
+ j->quals = pull_up_sublinks_qual_recurse(root,
+ j->quals,
+ &j->rarg,
+ child_rels,
+ NULL, NULL);
+ /* Return NULL representing constant TRUE */
+ return NULL;
+ }
+ if (available_rels2 != NULL &&
+ (j = convert_EXISTS_sublink_to_join(root, sublink, true,
+ available_rels2)) != NULL)
+ {
+ /* Yes; insert the new join node into the join tree */
+ j->larg = *jtlink2;
+ *jtlink2 = (Node *) j;
+ /* Recursively process pulled-up jointree nodes */
j->rarg = pull_up_sublinks_jointree_recurse(root,
j->rarg,
&child_rels);
- /* Any inserted joins get stacked onto j->rarg */
+ /*
+ * Now recursively process the pulled-up quals. Because
+ * we are underneath a NOT, we can't pull up sublinks
+ * that reference the left-hand stuff, but it's still
+ * okay to pull up sublinks referencing j->rarg.
+ */
j->quals = pull_up_sublinks_qual_recurse(root,
j->quals,
+ &j->rarg,
child_rels,
- &j->rarg);
-#endif
- /* Now insert the new join node into the join tree */
- j->larg = *jtlink;
- *jtlink = (Node *) j;
- /* and return NULL representing constant TRUE */
+ NULL, NULL);
+ /* Return NULL representing constant TRUE */
return NULL;
}
}
newclause = pull_up_sublinks_qual_recurse(root,
oldclause,
- available_rels,
- jtlink);
+ jtlink1,
+ available_rels1,
+ jtlink2,
+ available_rels2);
if (newclause)
newclauses = lappend(newclauses, newclause);
}