diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2011-05-02 15:56:43 -0400 | 
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2011-05-02 15:56:43 -0400 | 
| commit | 65a7cd0e9ced21ec7ad67c11cd9a263640fe22e9 (patch) | |
| tree | 8fc4dde16cca1d10aaf9a38f2260625467cf4dd7 | |
| parent | e79518e93744a547ff874832dd40638b9531105e (diff) | |
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.
| -rw-r--r-- | src/backend/optimizer/plan/subselect.c | 5 | ||||
| -rw-r--r-- | 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 cf503d51135..30386a38091 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -995,6 +995,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 5a49768c6ce..93e9a947787 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -317,6 +317,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) @@ -325,7 +326,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 */ @@ -338,7 +350,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 */ @@ -353,6 +376,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))  		{ @@ -362,7 +386,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 */ | 
