*** pgsql/src/backend/optimizer/prep/prepjointree.c 2009/01/01 17:23:44 1.62 --- pgsql/src/backend/optimizer/prep/prepjointree.c 2009/02/25 03:30:37 1.63 *************** *** 16,22 **** * * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.61 2008/12/28 18:53:57 tgl Exp $ * *------------------------------------------------------------------------- */ --- 16,22 ---- * * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.62 2009/01/01 17:23:44 momjian Exp $ * *------------------------------------------------------------------------- */ *************** typedef struct reduce_outer_joins_state *** 44,50 **** 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, --- 44,50 ---- 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); static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, bool below_outer_join, *************** static Node *find_jointree_node_for_rel( *** 91,97 **** * 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. --- 91,97 ---- * 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 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. *************** static Node *find_jointree_node_for_rel( *** 110,122 **** 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); } /* --- 110,131 ---- void pull_up_sublinks(PlannerInfo *root) { + Node *jtnode; Relids relids; /* Begin recursion through the jointree */ ! 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); } /* *************** pull_up_sublinks_jointree_recurse(Planne *** 144,152 **** { 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 */ --- 153,161 ---- { FromExpr *f = (FromExpr *) jtnode; List *newfromlist = NIL; Relids frelids = NULL; + FromExpr *newf; + Node *jtlink; ListCell *l; /* First, recurse to process children and collect their relids */ *************** pull_up_sublinks_jointree_recurse(Planne *** 161,186 **** 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 --- 170,201 ---- 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 */ ! 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 = jtlink; } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j; Relids leftrelids; Relids rightrelids; ! Node *jtlink; /* * Make a modifiable copy of join node, but don't bother copying *************** pull_up_sublinks_jointree_recurse(Planne *** 188,193 **** --- 203,209 ---- */ 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, *************** pull_up_sublinks_jointree_recurse(Planne *** 197,209 **** /* * 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) { --- 213,227 ---- /* * Now process qual, showing appropriate child relids as available, ! * 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) { *************** pull_up_sublinks_jointree_recurse(Planne *** 211,232 **** 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 */ --- 229,240 ---- j->quals = pull_up_sublinks_qual_recurse(root, j->quals, bms_union(leftrelids, rightrelids), ! &jtlink); break; case JOIN_LEFT: j->quals = pull_up_sublinks_qual_recurse(root, j->quals, rightrelids, ! &j->rarg); break; case JOIN_FULL: /* can't do anything with full-join quals */ *************** pull_up_sublinks_jointree_recurse(Planne *** 234,245 **** 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", --- 242,248 ---- case JOIN_RIGHT: j->quals = pull_up_sublinks_qual_recurse(root, j->quals, leftrelids, ! &j->larg); break; default: elog(ERROR, "unrecognized join type: %d", *************** pull_up_sublinks_jointree_recurse(Planne *** 255,263 **** * 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", --- 258,267 ---- * levels would mistakenly think they couldn't use references to this * join. */ ! *relids = bms_join(leftrelids, rightrelids); ! if (j->rtindex) ! *relids = bms_add_member(*relids, j->rtindex); ! jtnode = jtlink; } else elog(ERROR, "unrecognized node type: %d", *************** pull_up_sublinks_jointree_recurse(Planne *** 268,307 **** /* * 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 *new_qual; ! List *new_fromlist; /* 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)) { ! *fromlist = list_concat(*fromlist, new_fromlist); ! return new_qual; } } else if (sublink->subLinkType == EXISTS_SUBLINK) { ! 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 */ --- 272,318 ---- /* * 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. */ static Node * pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, ! Relids available_rels, Node **jtlink) { if (node == NULL) return NULL; if (IsA(node, SubLink)) { SubLink *sublink = (SubLink *) node; ! JoinExpr *j; /* 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) { ! /* 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) { ! j = convert_EXISTS_sublink_to_join(root, sublink, false, ! available_rels); ! if (j) { ! /* 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 */ *************** pull_up_sublinks_qual_recurse(PlannerInf *** 311,329 **** { /* If the immediate argument of NOT is EXISTS, try to convert */ SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node); ! Node *new_qual; ! List *new_fromlist; 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)) { ! *fromlist = list_concat(*fromlist, new_fromlist); ! return new_qual; } } } --- 322,342 ---- { /* If the immediate argument of NOT is EXISTS, try to convert */ SubLink *sublink = (SubLink *) get_notclausearg((Expr *) node); ! JoinExpr *j; if (sublink && IsA(sublink, SubLink)) { if (sublink->subLinkType == EXISTS_SUBLINK) { ! j = convert_EXISTS_sublink_to_join(root, sublink, true, ! available_rels); ! if (j) { ! /* Yes, insert the new join node into the join tree */ ! j->larg = *jtlink; ! *jtlink = (Node *) j; ! /* and return NULL representing constant TRUE */ ! return NULL; } } } *************** pull_up_sublinks_qual_recurse(PlannerInf *** 339,352 **** foreach(l, ((BoolExpr *) node)->args) { Node *oldclause = (Node *) lfirst(l); ! newclauses = lappend(newclauses, ! pull_up_sublinks_qual_recurse(root, ! oldclause, ! available_rels, ! fromlist)); ! } ! return (Node *) make_andclause(newclauses); } /* Stop if not an AND */ return node; --- 352,373 ---- foreach(l, ((BoolExpr *) node)->args) { Node *oldclause = (Node *) lfirst(l); + Node *newclause; ! newclause = pull_up_sublinks_qual_recurse(root, ! oldclause, ! available_rels, ! jtlink); ! if (newclause) ! newclauses = lappend(newclauses, newclause); ! } ! /* 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; *************** pull_up_subqueries(PlannerInfo *root, No *** 489,494 **** --- 510,517 ---- 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, *************** pull_up_simple_subquery(PlannerInfo *roo *** 702,713 **** 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. * * Likewise, relids appearing in AppendRelInfo nodes have to be fixed. * We already checked that this won't require introducing multiple --- 725,736 ---- parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks); /* ! * 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 *************** reduce_outer_joins_pass2(Node *jtnode, *** 1419,1424 **** --- 1442,1455 ---- 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); *************** reduce_outer_joins_pass2(Node *jtnode, *** 1475,1488 **** } /* Apply the jointype change, if any, to both jointree node and RTE */ ! if (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; } /* Only recurse if there's more to do below here */ if (left_state->contains_outer || right_state->contains_outer) --- 1506,1520 ---- } /* Apply the jointype change, if any, to both jointree node and RTE */ ! 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 = jointype; } + j->jointype = jointype; /* Only recurse if there's more to do below here */ if (left_state->contains_outer || right_state->contains_outer) *************** reduce_outer_joins_pass2(Node *jtnode, *** 1542,1548 **** pass_nonnullable_vars = local_nonnullable_vars; pass_forced_null_vars = local_forced_null_vars; } ! else if (jointype != JOIN_FULL) /* ie, LEFT or ANTI */ { /* can't pass local constraints to non-nullable side */ pass_nonnullable_rels = nonnullable_rels; --- 1574,1580 ---- pass_nonnullable_vars = local_nonnullable_vars; pass_forced_null_vars = local_forced_null_vars; } ! else if (jointype != JOIN_FULL) /* ie, LEFT/SEMI/ANTI */ { /* can't pass local constraints to non-nullable side */ pass_nonnullable_rels = nonnullable_rels; *************** reduce_outer_joins_pass2(Node *jtnode, *** 1564,1570 **** if (right_state->contains_outer) { ! if (jointype != JOIN_FULL) /* ie, INNER, LEFT or ANTI */ { /* pass appropriate constraints, per comment above */ pass_nonnullable_rels = local_nonnullable_rels; --- 1596,1602 ---- if (right_state->contains_outer) { ! if (jointype != JOIN_FULL) /* ie, INNER/LEFT/SEMI/ANTI */ { /* pass appropriate constraints, per comment above */ pass_nonnullable_rels = local_nonnullable_rels; *************** reduce_outer_joins_pass2(Node *jtnode, *** 1595,1604 **** * 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. * * 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 --- 1627,1636 ---- * substitute_multiple_relids - adjust node relid sets after pulling up * a subquery * ! * 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 *************** substitute_multiple_relids_walker(Node * *** 1618,1643 **** { 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; --- 1650,1655 ---- *************** get_relids_in_jointree(Node *jtnode, boo *** 1757,1763 **** 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 --- 1769,1775 ---- result = get_relids_in_jointree(j->larg, include_joins); result = bms_join(result, get_relids_in_jointree(j->rarg, include_joins)); ! if (include_joins && j->rtindex) result = bms_add_member(result, j->rtindex); } else