diff options
Diffstat (limited to 'src/backend/optimizer/plan/subselect.c')
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 126 |
1 files changed, 74 insertions, 52 deletions
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 1e4e9fe565b..d4374516ac8 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.133 2008/08/14 18:47:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.134 2008/08/17 01:20:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -725,18 +725,31 @@ hash_ok_operator(OpExpr *expr) /* * 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 WHERE, but has not - * checked the properties of the SubLink further. Decide whether it is - * appropriate to process this SubLink in join style. If not, return NULL. - * If so, build the qual clause(s) to replace the SubLink, and return them. - * The qual clauses are wrapped in a FlattenedSubLink node to help later - * processing place them properly. + * 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. + * (We must restrict this to avoid changing the semantics when a sublink + * 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. */ -Node * -convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) +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; @@ -755,7 +768,7 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) * higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) - return NULL; + return false; /* * The test expression must contain some Vars of the current query, @@ -764,16 +777,22 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) */ left_varnos = pull_varnos(sublink->testexpr); if (bms_is_empty(left_varnos)) - return NULL; + 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 NULL; + return false; /* - * Okay, pull up the sub-select into top range table and jointree. + * Okay, pull up the sub-select into upper range table. * * We rely here on the assumption that the outer query has no references * to the inner (necessarily true, other than the Vars that we build @@ -786,16 +805,15 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) false); parse->rtable = lappend(parse->rtable, rte); rtindex = list_length(parse->rtable); - rtr = makeNode(RangeTblRef); - rtr->rtindex = rtindex; /* - * We assume it's okay to add the pulled-up subquery to the topmost FROM - * list. This should be all right for ANY clauses appearing in WHERE - * or in upper-level plain JOIN/ON clauses. ANYs appearing below any - * outer joins couldn't be placed there, however. + * 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. */ - parse->jointree->fromlist = lappend(parse->jointree->fromlist, rtr); + rtr = makeNode(RangeTblRef); + rtr->rtindex = rtindex; + *fromlist = list_make1(rtr); /* * Build a list of Vars representing the subselect outputs. @@ -805,14 +823,14 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) rtindex); /* - * Build the result qual expression, replacing Params with these Vars. + * Build the replacement qual expression, replacing Params with these Vars. */ quals = (Expr *) convert_testexpr(root, sublink->testexpr, subquery_vars); /* - * Now build the FlattenedSubLink node. + * And finally, build the FlattenedSubLink node. */ fslink = makeNode(FlattenedSubLink); fslink->jointype = JOIN_SEMI; @@ -820,7 +838,9 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink) fslink->righthand = bms_make_singleton(rtindex); fslink->quals = quals; - return (Node *) fslink; + *new_qual = (Node *) fslink; + + return true; } /* @@ -883,20 +903,15 @@ simplify_EXISTS_query(Query *query) /* * convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join? * - * The caller has found an EXISTS SubLink at the top level of WHERE, or just - * underneath a NOT, but has not checked the properties of the SubLink - * further. Decide whether it is appropriate to process this SubLink in join - * style. If not, return NULL. If so, build the qual clause(s) to replace - * the SubLink, and return them. (In the NOT case, the returned clauses are - * intended to replace the NOT as well.) The qual clauses are wrapped in a - * FlattenedSubLink node to help later processing place them properly. - * - * Side effects of a successful conversion include adding the SubLink's - * subselect to the query's rangetable. + * 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". */ -Node * +bool convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, - bool under_not) + bool under_not, + Relids available_rels, + Node **new_qual, List **fromlist) { Query *parse = root->parse; Query *subselect = (Query *) sublink->subselect; @@ -924,7 +939,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, * us with noplace to evaluate the targetlist. */ if (!simplify_EXISTS_query(subselect)) - return NULL; + return false; /* * Separate out the WHERE clause. (We could theoretically also remove @@ -939,20 +954,20 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, * query. (Vars of higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) - return NULL; + 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 NULL; + return false; /* * We don't risk optimizing if the WHERE clause is volatile, either. */ if (contain_volatile_functions(whereClause)) - return NULL; + return false; /* * Also disallow SubLinks within the WHERE clause. (XXX this could @@ -960,10 +975,10 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, * below, and it doesn't seem worth worrying about in a first pass.) */ if (contain_subplans(whereClause)) - return NULL; + return false; /* - * Okay, pull up the sub-select into top range table and jointree. + * Prepare to pull up the sub-select into top range table. * * We rely here on the assumption that the outer query has no references * to the inner (necessarily true). Therefore this is a lot easier than @@ -973,7 +988,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, * 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 append the fromlist to our own. + * 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. */ @@ -1007,24 +1022,29 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, bms_free(clause_varnos); Assert(!bms_is_empty(left_varnos)); - /* Also identify all the rels syntactically within the subselect */ - subselect_varnos = get_relids_in_jointree((Node *) subselect->jointree); + /* + * 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_subset(right_varnos, subselect_varnos)); /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); /* - * We assume it's okay to add the pulled-up subquery to the topmost FROM - * list. This should be all right for EXISTS clauses appearing in WHERE - * or in upper-level plain JOIN/ON clauses. EXISTS appearing below any - * outer joins couldn't be placed there, however. + * Pass back the subquery fromlist to be attached to upper jointree + * in a suitable place. */ - parse->jointree->fromlist = list_concat(parse->jointree->fromlist, - subselect->jointree->fromlist); + *fromlist = subselect->jointree->fromlist; /* - * Now build the FlattenedSubLink node. + * And finally, build the FlattenedSubLink node. */ fslink = makeNode(FlattenedSubLink); fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI; @@ -1032,7 +1052,9 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, fslink->righthand = subselect_varnos; fslink->quals = (Expr *) whereClause; - return (Node *) fslink; + *new_qual = (Node *) fslink; + + return true; } /* |