mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-29 22:49:41 +03:00 
			
		
		
		
	Fix miscomputation of direct_lateral_relids for join relations.
If a PlaceHolderVar is to be evaluated at a join relation, but its value is only needed there and not at higher levels, we neglected to update the joinrel's direct_lateral_relids to include the PHV's source rel. This causes problems because join_is_legal() then won't allow joining the joinrel to the PHV's source rel at all, leading to "failed to build any N-way joins" planner failures. Per report from Andreas Seltenreich. Back-patch to 9.5 where the problem originated. Discussion: https://postgr.es/m/87blfgqa4t.fsf@aurora.ydns.eu
This commit is contained in:
		| @@ -404,8 +404,10 @@ add_placeholders_to_base_rels(PlannerInfo *root) | |||||||
|  *		and if they contain lateral references, add those references to the |  *		and if they contain lateral references, add those references to the | ||||||
|  *		joinrel's direct_lateral_relids. |  *		joinrel's direct_lateral_relids. | ||||||
|  * |  * | ||||||
|  * A join rel should emit a PlaceHolderVar if (a) the PHV is needed above |  * A join rel should emit a PlaceHolderVar if (a) the PHV can be computed | ||||||
|  * this join level and (b) the PHV can be computed at or below this level. |  * at or below this join level and (b) the PHV is needed above this level. | ||||||
|  |  * However, condition (a) is sufficient to add to direct_lateral_relids, | ||||||
|  |  * as explained below. | ||||||
|  */ |  */ | ||||||
| void | void | ||||||
| add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel, | add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel, | ||||||
| @@ -418,11 +420,11 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel, | |||||||
| 	{ | 	{ | ||||||
| 		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); | 		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); | ||||||
|  |  | ||||||
| 		/* Is it still needed above this joinrel? */ |  | ||||||
| 		if (bms_nonempty_difference(phinfo->ph_needed, relids)) |  | ||||||
| 		{ |  | ||||||
| 		/* Is it computable here? */ | 		/* Is it computable here? */ | ||||||
| 		if (bms_is_subset(phinfo->ph_eval_at, relids)) | 		if (bms_is_subset(phinfo->ph_eval_at, relids)) | ||||||
|  | 		{ | ||||||
|  | 			/* Is it still needed above this joinrel? */ | ||||||
|  | 			if (bms_nonempty_difference(phinfo->ph_needed, relids)) | ||||||
| 			{ | 			{ | ||||||
| 				/* Yup, add it to the output */ | 				/* Yup, add it to the output */ | ||||||
| 				joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, | 				joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, | ||||||
| @@ -450,12 +452,26 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel, | |||||||
| 					joinrel->reltarget->cost.startup += cost.startup; | 					joinrel->reltarget->cost.startup += cost.startup; | ||||||
| 					joinrel->reltarget->cost.per_tuple += cost.per_tuple; | 					joinrel->reltarget->cost.per_tuple += cost.per_tuple; | ||||||
| 				} | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 				/* Adjust joinrel's direct_lateral_relids as needed */ | 			/* | ||||||
|  | 			 * Also adjust joinrel's direct_lateral_relids to include the | ||||||
|  | 			 * PHV's source rel(s).  We must do this even if we're not | ||||||
|  | 			 * actually going to emit the PHV, otherwise join_is_legal() will | ||||||
|  | 			 * reject valid join orderings.  (In principle maybe we could | ||||||
|  | 			 * instead remove the joinrel's lateral_relids dependency; but | ||||||
|  | 			 * that's complicated to get right, and cases where we're not | ||||||
|  | 			 * going to emit the PHV are too rare to justify the work.) | ||||||
|  | 			 * | ||||||
|  | 			 * In principle we should only do this if the join doesn't yet | ||||||
|  | 			 * include the PHV's source rel(s).  But our caller | ||||||
|  | 			 * build_join_rel() will clean things up by removing the join's | ||||||
|  | 			 * own relids from its direct_lateral_relids, so we needn't | ||||||
|  | 			 * account for that here. | ||||||
|  | 			 */ | ||||||
| 			joinrel->direct_lateral_relids = | 			joinrel->direct_lateral_relids = | ||||||
| 				bms_add_members(joinrel->direct_lateral_relids, | 				bms_add_members(joinrel->direct_lateral_relids, | ||||||
| 								phinfo->ph_lateral); | 								phinfo->ph_lateral); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	} |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3010,6 +3010,70 @@ order by 1,2; | |||||||
|  4567890123456789 | 4567890123456789 | 4567890123456789 | 42 |  4567890123456789 | 4567890123456789 | 4567890123456789 | 42 | ||||||
| (8 rows) | (8 rows) | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- variant where a PlaceHolderVar is needed at a join, but not above the join | ||||||
|  | -- | ||||||
|  | explain (costs off) | ||||||
|  | select * from | ||||||
|  |   int4_tbl as i41, | ||||||
|  |   lateral | ||||||
|  |     (select 1 as x from | ||||||
|  |       (select i41.f1 as lat, | ||||||
|  |               i42.f1 as loc from | ||||||
|  |          int8_tbl as i81, int4_tbl as i42) as ss1 | ||||||
|  |       right join int4_tbl as i43 on (i43.f1 > 1) | ||||||
|  |       where ss1.loc = ss1.lat) as ss2 | ||||||
|  | where i41.f1 > 0; | ||||||
|  |                     QUERY PLAN                     | ||||||
|  | -------------------------------------------------- | ||||||
|  |  Nested Loop | ||||||
|  |    ->  Nested Loop | ||||||
|  |          ->  Seq Scan on int4_tbl i41 | ||||||
|  |                Filter: (f1 > 0) | ||||||
|  |          ->  Nested Loop | ||||||
|  |                Join Filter: (i41.f1 = i42.f1) | ||||||
|  |                ->  Seq Scan on int8_tbl i81 | ||||||
|  |                ->  Materialize | ||||||
|  |                      ->  Seq Scan on int4_tbl i42 | ||||||
|  |    ->  Materialize | ||||||
|  |          ->  Seq Scan on int4_tbl i43 | ||||||
|  |                Filter: (f1 > 1) | ||||||
|  | (12 rows) | ||||||
|  |  | ||||||
|  | select * from | ||||||
|  |   int4_tbl as i41, | ||||||
|  |   lateral | ||||||
|  |     (select 1 as x from | ||||||
|  |       (select i41.f1 as lat, | ||||||
|  |               i42.f1 as loc from | ||||||
|  |          int8_tbl as i81, int4_tbl as i42) as ss1 | ||||||
|  |       right join int4_tbl as i43 on (i43.f1 > 1) | ||||||
|  |       where ss1.loc = ss1.lat) as ss2 | ||||||
|  | where i41.f1 > 0; | ||||||
|  |      f1     | x  | ||||||
|  | ------------+--- | ||||||
|  |      123456 | 1 | ||||||
|  |      123456 | 1 | ||||||
|  |      123456 | 1 | ||||||
|  |      123456 | 1 | ||||||
|  |      123456 | 1 | ||||||
|  |      123456 | 1 | ||||||
|  |      123456 | 1 | ||||||
|  |      123456 | 1 | ||||||
|  |      123456 | 1 | ||||||
|  |      123456 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  |  2147483647 | 1 | ||||||
|  | (20 rows) | ||||||
|  |  | ||||||
| -- | -- | ||||||
| -- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE | -- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE | ||||||
| -- | -- | ||||||
|   | |||||||
| @@ -905,6 +905,33 @@ where | |||||||
|   1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1) |   1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1) | ||||||
| order by 1,2; | order by 1,2; | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- variant where a PlaceHolderVar is needed at a join, but not above the join | ||||||
|  | -- | ||||||
|  |  | ||||||
|  | explain (costs off) | ||||||
|  | select * from | ||||||
|  |   int4_tbl as i41, | ||||||
|  |   lateral | ||||||
|  |     (select 1 as x from | ||||||
|  |       (select i41.f1 as lat, | ||||||
|  |               i42.f1 as loc from | ||||||
|  |          int8_tbl as i81, int4_tbl as i42) as ss1 | ||||||
|  |       right join int4_tbl as i43 on (i43.f1 > 1) | ||||||
|  |       where ss1.loc = ss1.lat) as ss2 | ||||||
|  | where i41.f1 > 0; | ||||||
|  |  | ||||||
|  | select * from | ||||||
|  |   int4_tbl as i41, | ||||||
|  |   lateral | ||||||
|  |     (select 1 as x from | ||||||
|  |       (select i41.f1 as lat, | ||||||
|  |               i42.f1 as loc from | ||||||
|  |          int8_tbl as i81, int4_tbl as i42) as ss1 | ||||||
|  |       right join int4_tbl as i43 on (i43.f1 > 1) | ||||||
|  |       where ss1.loc = ss1.lat) as ss2 | ||||||
|  | where i41.f1 > 0; | ||||||
|  |  | ||||||
| -- | -- | ||||||
| -- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE | -- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE | ||||||
| -- | -- | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user