*** pgsql/src/backend/optimizer/plan/subselect.c 2009/01/01 17:23:44 1.145 --- pgsql/src/backend/optimizer/plan/subselect.c 2009/02/25 03:30:37 1.146 *************** *** 7,13 **** * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.144 2008/12/28 18:53:57 tgl Exp $ * *------------------------------------------------------------------------- */ --- 7,13 ---- * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.145 2009/01/01 17:23:44 momjian Exp $ * *------------------------------------------------------------------------- */ *************** SS_process_ctes(PlannerInfo *root) *** 932,943 **** } /* ! * 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 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. --- 932,944 ---- } /* ! * convert_ANY_sublink_to_join: try to convert an ANY SubLink to a join * * 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. ! * If so, form a JoinExpr and return it. Return NULL if the SubLink cannot ! * be converted to a join. * * 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. *************** SS_process_ctes(PlannerInfo *root) *** 945,974 **** * 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. */ ! 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; ! Relids left_varnos; int rtindex; RangeTblEntry *rte; RangeTblRef *rtr; List *subquery_vars; ! Expr *quals; ! FlattenedSubLink *fslink; Assert(sublink->subLinkType == ANY_SUBLINK); --- 946,978 ---- * 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, the returned JoinExpr has larg = NULL and rarg = the jointree ! * item representing the pulled-up subquery. The caller must set larg to ! * represent the relation(s) on the lefthand side of the new join, and insert ! * the JoinExpr into the upper query's jointree at an appropriate place ! * (typically, where the lefthand relation(s) had been). Note that the ! * passed-in SubLink must also be removed from its original position in the ! * query quals, since the quals of the returned JoinExpr replace it. ! * (Notionally, we replace the SubLink with a constant TRUE, then elide the ! * redundant constant from the qual.) * * 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. */ ! JoinExpr * convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, ! Relids available_rels) { + JoinExpr *result; Query *parse = root->parse; Query *subselect = (Query *) sublink->subselect; ! Relids upper_varnos; int rtindex; RangeTblEntry *rte; RangeTblRef *rtr; List *subquery_vars; ! Node *quals; Assert(sublink->subLinkType == ANY_SUBLINK); *************** convert_ANY_sublink_to_join(PlannerInfo *** 977,1004 **** * higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) ! return false; /* ! * The test expression must contain some Vars of the current query, * else it's not gonna be a join. (Note that it won't have Vars * referring to the subquery, rather Params.) */ ! left_varnos = pull_varnos(sublink->testexpr); ! if (bms_is_empty(left_varnos)) ! 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 false; /* * Okay, pull up the sub-select into upper range table. --- 981,1008 ---- * higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) ! return NULL; /* ! * The test expression must contain some Vars of the parent query, * else it's not gonna be a join. (Note that it won't have Vars * referring to the subquery, rather Params.) */ ! upper_varnos = pull_varnos(sublink->testexpr); ! if (bms_is_empty(upper_varnos)) ! return NULL; /* * However, it can't refer to anything outside available_rels. */ ! if (!bms_is_subset(upper_varnos, available_rels)) ! return NULL; /* * The combining operators and left-hand expressions mustn't be volatile. */ if (contain_volatile_functions(sublink->testexpr)) ! return NULL; /* * Okay, pull up the sub-select into upper range table. *************** convert_ANY_sublink_to_join(PlannerInfo *** 1016,1028 **** rtindex = list_length(parse->rtable); /* ! * 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. */ rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; - *fromlist = list_make1(rtr); /* * Build a list of Vars representing the subselect outputs. --- 1020,1029 ---- rtindex = list_length(parse->rtable); /* ! * Form a RangeTblRef for the pulled-up sub-select. */ rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; /* * Build a list of Vars representing the subselect outputs. *************** convert_ANY_sublink_to_join(PlannerInfo *** 1032,1084 **** rtindex); /* ! * Build the replacement qual expression, replacing Params with these Vars. */ ! quals = (Expr *) convert_testexpr(root, ! sublink->testexpr, ! subquery_vars); /* ! * And finally, build the FlattenedSubLink node. ! * ! * Note: at this point left_varnos may well contain join relids, since ! * the testexpr hasn't been run through flatten_join_alias_vars. This ! * will get fixed when flatten_join_alias_vars is run. ! */ ! fslink = makeNode(FlattenedSubLink); ! fslink->jointype = JOIN_SEMI; ! fslink->lefthand = left_varnos; ! fslink->righthand = bms_make_singleton(rtindex); ! fslink->quals = quals; ! ! *new_qual = (Node *) fslink; ! return true; } /* ! * convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join? * * 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". */ ! bool convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ! bool under_not, ! Relids available_rels, ! Node **new_qual, List **fromlist) { Query *parse = root->parse; Query *subselect = (Query *) sublink->subselect; Node *whereClause; int rtoffset; int varno; Relids clause_varnos; ! Relids left_varnos; ! Relids right_varnos; ! Relids subselect_varnos; ! FlattenedSubLink *fslink; Assert(sublink->subLinkType == EXISTS_SUBLINK); --- 1033,1077 ---- rtindex); /* ! * Build the new join's qual expression, replacing Params with these Vars. */ ! quals = convert_testexpr(root, sublink->testexpr, subquery_vars); /* ! * And finally, build the JoinExpr node. ! */ ! result = makeNode(JoinExpr); ! result->jointype = JOIN_SEMI; ! result->isNatural = false; ! result->larg = NULL; /* caller must fill this in */ ! result->rarg = (Node *) rtr; ! result->using = NIL; ! result->quals = quals; ! result->alias = NULL; ! result->rtindex = 0; /* we don't need an RTE for it */ ! return result; } /* ! * convert_EXISTS_sublink_to_join: try to convert an EXISTS SubLink to a join * * 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". */ ! JoinExpr * convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, ! bool under_not, Relids available_rels) { + JoinExpr *result; Query *parse = root->parse; Query *subselect = (Query *) sublink->subselect; Node *whereClause; int rtoffset; int varno; Relids clause_varnos; ! Relids upper_varnos; Assert(sublink->subLinkType == EXISTS_SUBLINK); *************** convert_EXISTS_sublink_to_join(PlannerIn *** 1095,1107 **** * us with noplace to evaluate the targetlist. */ if (!simplify_EXISTS_query(subselect)) ! return false; /* * The subquery must have a nonempty jointree, else we won't have a join. */ if (subselect->jointree->fromlist == NIL) ! return false; /* * Separate out the WHERE clause. (We could theoretically also remove --- 1088,1100 ---- * us with noplace to evaluate the targetlist. */ if (!simplify_EXISTS_query(subselect)) ! return NULL; /* * The subquery must have a nonempty jointree, else we won't have a join. */ if (subselect->jointree->fromlist == NIL) ! return NULL; /* * Separate out the WHERE clause. (We could theoretically also remove *************** convert_EXISTS_sublink_to_join(PlannerIn *** 1116,1135 **** * query. (Vars of higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) ! 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 false; /* * We don't risk optimizing if the WHERE clause is volatile, either. */ if (contain_volatile_functions(whereClause)) ! return false; /* * Prepare to pull up the sub-select into top range table. --- 1109,1128 ---- * query. (Vars of higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) ! return NULL; /* * 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; /* * We don't risk optimizing if the WHERE clause is volatile, either. */ if (contain_volatile_functions(whereClause)) ! return NULL; /* * Prepare to pull up the sub-select into top range table. *************** convert_EXISTS_sublink_to_join(PlannerIn *** 1142,1148 **** * 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 attach the fromlist to our own. * But first, adjust all level-zero varnos in the subquery to account * for the rtable merger. */ --- 1135,1141 ---- * 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 use the FromExpr in our jointree. * But first, adjust all level-zero varnos in the subquery to account * for the rtable merger. */ *************** convert_EXISTS_sublink_to_join(PlannerIn *** 1161,1218 **** /* * Now that the WHERE clause is adjusted to match the parent query * environment, we can easily identify all the level-zero rels it uses. ! * The ones <= rtoffset are "left rels" of the join we're forming, ! * and the ones > rtoffset are "right rels". */ clause_varnos = pull_varnos(whereClause); ! left_varnos = right_varnos = NULL; while ((varno = bms_first_member(clause_varnos)) >= 0) { if (varno <= rtoffset) ! left_varnos = bms_add_member(left_varnos, varno); ! else ! right_varnos = bms_add_member(right_varnos, varno); } bms_free(clause_varnos); ! Assert(!bms_is_empty(left_varnos)); /* * 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_empty(subselect_varnos)); ! 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); /* ! * Pass back the subquery fromlist to be attached to upper jointree ! * in a suitable place. */ ! *fromlist = subselect->jointree->fromlist; ! ! /* ! * And finally, build the FlattenedSubLink node. ! * ! * Note: at this point left_varnos and subselect_varnos may well contain ! * join relids. This will get fixed when flatten_join_alias_vars is run. ! */ ! fslink = makeNode(FlattenedSubLink); ! fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI; ! fslink->lefthand = left_varnos; ! fslink->righthand = subselect_varnos; ! fslink->quals = (Expr *) whereClause; ! ! *new_qual = (Node *) fslink; ! return true; } /* --- 1154,1200 ---- /* * Now that the WHERE clause is adjusted to match the parent query * environment, we can easily identify all the level-zero rels it uses. ! * The ones <= rtoffset belong to the upper query; the ones > rtoffset ! * do not. */ clause_varnos = pull_varnos(whereClause); ! upper_varnos = NULL; while ((varno = bms_first_member(clause_varnos)) >= 0) { if (varno <= rtoffset) ! upper_varnos = bms_add_member(upper_varnos, varno); } bms_free(clause_varnos); ! Assert(!bms_is_empty(upper_varnos)); /* * 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(upper_varnos, available_rels)) ! return NULL; /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); /* ! * And finally, build the JoinExpr node. */ ! result = makeNode(JoinExpr); ! result->jointype = under_not ? JOIN_ANTI : JOIN_SEMI; ! result->isNatural = false; ! result->larg = NULL; /* caller must fill this in */ ! /* flatten out the FromExpr node if it's useless */ ! if (list_length(subselect->jointree->fromlist) == 1) ! result->rarg = (Node *) linitial(subselect->jointree->fromlist); ! else ! result->rarg = (Node *) subselect->jointree; ! result->using = NIL; ! result->quals = whereClause; ! result->alias = NULL; ! result->rtindex = 0; /* we don't need an RTE for it */ ! return result; } /*