From dcc685debb02c507b88aa8052f9e6334a7c3f830 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 2 May 2011 15:56:36 -0400 Subject: [PATCH] Fix pull_up_sublinks' failure to handle nested pull-up opportunities. After finding an EXISTS or ANY sub-select that can be converted to a semi-join or anti-join, we should recurse into the body of the sub-select. This allows cases such as EXISTS-within-EXISTS to be optimized properly. The original coding would leave the lower sub-select as a SubLink, which is no better and often worse than what we can do with a join. Per example from Wayne Conrad. Back-patch to 8.4. There is a related issue in older versions' handling of pull_up_IN_clauses, but they're lame enough anyway about the whole area that it seems not worth the extra work to try to fix. --- src/backend/optimizer/plan/subselect.c | 5 +++ src/backend/optimizer/prep/prepjointree.c | 41 +++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 493bc86299..45eaa03fda 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1065,6 +1065,11 @@ SS_process_ctes(PlannerInfo *root) * (Notionally, we replace the SubLink with a constant TRUE, then elide the * redundant constant from the qual.) * + * On success, the caller is also responsible for recursively applying + * pull_up_sublinks processing to the rarg and quals of the returned JoinExpr. + * (On failure, there is no need to do anything, since pull_up_sublinks will + * be applied when we recursively plan the sub-select.) + * * 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. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index a70439cc67..5d163292c5 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -318,6 +318,7 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, { SubLink *sublink = (SubLink *) node; JoinExpr *j; + Relids child_rels; /* Is it a convertible ANY or EXISTS clause? */ if (sublink->subLinkType == ANY_SUBLINK) @@ -326,7 +327,18 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, available_rels); if (j) { - /* Yes, insert the new join node into the join tree */ + /* Yes; recursively process what we pulled up */ + j->rarg = pull_up_sublinks_jointree_recurse(root, + j->rarg, + &child_rels); + /* Pulled-up ANY/EXISTS quals can use those rels too */ + child_rels = bms_add_members(child_rels, available_rels); + /* ... and any inserted joins get stacked onto j->rarg */ + 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 */ @@ -339,7 +351,18 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, available_rels); if (j) { - /* Yes, insert the new join node into the join tree */ + /* Yes; recursively process what we pulled up */ + j->rarg = pull_up_sublinks_jointree_recurse(root, + j->rarg, + &child_rels); + /* Pulled-up ANY/EXISTS quals can use those rels too */ + child_rels = bms_add_members(child_rels, available_rels); + /* ... and any inserted joins get stacked onto j->rarg */ + 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 */ @@ -354,6 +377,7 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, /* 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)) { @@ -363,7 +387,18 @@ pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node, available_rels); if (j) { - /* Yes, insert the new join node into the join tree */ + /* Yes; recursively process what we pulled up */ + j->rarg = pull_up_sublinks_jointree_recurse(root, + j->rarg, + &child_rels); + /* Pulled-up ANY/EXISTS quals can use those rels too */ + child_rels = bms_add_members(child_rels, available_rels); + /* ... and any inserted joins get stacked onto j->rarg */ + 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 */