aboutsummaryrefslogtreecommitdiff
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.c275
1 files changed, 235 insertions, 40 deletions
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 241ad668072..24e9fdb0b0a 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.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);
}
/*