mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Fix an old corner-case error in match_unsorted_outer(): don't consider
the cheapest-total inner path as a new candidate while truncating the sort key list, if it already matched the full sort key list. This is too much of a corner case to be worth back-patching, since it's unusual for the cheapest total path to be sorted, and anyway no real harm is done (except in JOIN_SEMI/ANTI cases where cost_mergejoin is a bit broken at the moment). But it wasn't behaving as intended, so fix it. Noted while examining a test case from Kevin Grittner. This error doesn't explain his issue, but it does explain why "set enable_seqscan = off" seemed to reproduce it for me.
This commit is contained in:
		@@ -8,7 +8,7 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * IDENTIFICATION
 | 
					 * IDENTIFICATION
 | 
				
			||||||
 *	  $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.120 2009/01/01 17:23:43 momjian Exp $
 | 
					 *	  $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.121 2009/02/05 01:24:55 tgl Exp $
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *-------------------------------------------------------------------------
 | 
					 *-------------------------------------------------------------------------
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -593,19 +593,44 @@ match_unsorted_outer(PlannerInfo *root,
 | 
				
			|||||||
		 * Look for presorted inner paths that satisfy the innersortkey list
 | 
							 * Look for presorted inner paths that satisfy the innersortkey list
 | 
				
			||||||
		 * --- or any truncation thereof, if we are allowed to build a
 | 
							 * --- or any truncation thereof, if we are allowed to build a
 | 
				
			||||||
		 * mergejoin using a subset of the merge clauses.  Here, we consider
 | 
							 * mergejoin using a subset of the merge clauses.  Here, we consider
 | 
				
			||||||
		 * both cheap startup cost and cheap total cost.  We can ignore
 | 
							 * both cheap startup cost and cheap total cost.
 | 
				
			||||||
		 * inner_cheapest_total on the first iteration, since we already made
 | 
							 *
 | 
				
			||||||
		 * a path with it --- but not on later iterations with shorter sort
 | 
							 * As we shorten the sortkey list, we should consider only paths that
 | 
				
			||||||
		 * keys, because then we are considering a different situation, viz
 | 
							 * are strictly cheaper than (in particular, not the same as) any path
 | 
				
			||||||
		 * using a simpler mergejoin to avoid a sort of the inner rel.
 | 
							 * found in an earlier iteration.  Otherwise we'd be intentionally
 | 
				
			||||||
 | 
							 * using fewer merge keys than a given path allows (treating the rest
 | 
				
			||||||
 | 
							 * as plain joinquals), which is unlikely to be a good idea.  Also,
 | 
				
			||||||
 | 
							 * eliminating paths here on the basis of compare_path_costs is a lot
 | 
				
			||||||
 | 
							 * cheaper than building the mergejoin path only to throw it away.
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * If inner_cheapest_total is well enough sorted to have not required
 | 
				
			||||||
 | 
							 * a sort in the path made above, we shouldn't make a duplicate path
 | 
				
			||||||
 | 
							 * with it, either.  We handle that case with the same logic that
 | 
				
			||||||
 | 
							 * handles the previous consideration, by initializing the variables
 | 
				
			||||||
 | 
							 * that track cheapest-so-far properly.  Note that we do NOT reject
 | 
				
			||||||
 | 
							 * inner_cheapest_total if we find it matches some shorter set of
 | 
				
			||||||
 | 
							 * pathkeys.  That case corresponds to using fewer mergekeys to avoid
 | 
				
			||||||
 | 
							 * sorting inner_cheapest_total, whereas we did sort it above, so the
 | 
				
			||||||
 | 
							 * plans being considered are different.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
 | 
							if (pathkeys_contained_in(innersortkeys,
 | 
				
			||||||
 | 
													  inner_cheapest_total->pathkeys))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								/* inner_cheapest_total didn't require a sort */
 | 
				
			||||||
 | 
								cheapest_startup_inner = inner_cheapest_total;
 | 
				
			||||||
 | 
								cheapest_total_inner = inner_cheapest_total;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								/* it did require a sort, at least for the full set of keys */
 | 
				
			||||||
 | 
								cheapest_startup_inner = NULL;
 | 
				
			||||||
 | 
								cheapest_total_inner = NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		num_sortkeys = list_length(innersortkeys);
 | 
							num_sortkeys = list_length(innersortkeys);
 | 
				
			||||||
		if (num_sortkeys > 1 && !useallclauses)
 | 
							if (num_sortkeys > 1 && !useallclauses)
 | 
				
			||||||
			trialsortkeys = list_copy(innersortkeys);	/* need modifiable copy */
 | 
								trialsortkeys = list_copy(innersortkeys);	/* need modifiable copy */
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			trialsortkeys = innersortkeys;		/* won't really truncate */
 | 
								trialsortkeys = innersortkeys;		/* won't really truncate */
 | 
				
			||||||
		cheapest_startup_inner = NULL;
 | 
					 | 
				
			||||||
		cheapest_total_inner = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (sortkeycnt = num_sortkeys; sortkeycnt > 0; sortkeycnt--)
 | 
							for (sortkeycnt = num_sortkeys; sortkeycnt > 0; sortkeycnt--)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -622,8 +647,6 @@ match_unsorted_outer(PlannerInfo *root,
 | 
				
			|||||||
													   trialsortkeys,
 | 
																		   trialsortkeys,
 | 
				
			||||||
													   TOTAL_COST);
 | 
																		   TOTAL_COST);
 | 
				
			||||||
			if (innerpath != NULL &&
 | 
								if (innerpath != NULL &&
 | 
				
			||||||
				(innerpath != inner_cheapest_total ||
 | 
					 | 
				
			||||||
				 sortkeycnt < num_sortkeys) &&
 | 
					 | 
				
			||||||
				(cheapest_total_inner == NULL ||
 | 
									(cheapest_total_inner == NULL ||
 | 
				
			||||||
				 compare_path_costs(innerpath, cheapest_total_inner,
 | 
									 compare_path_costs(innerpath, cheapest_total_inner,
 | 
				
			||||||
									TOTAL_COST) < 0))
 | 
														TOTAL_COST) < 0))
 | 
				
			||||||
@@ -660,8 +683,6 @@ match_unsorted_outer(PlannerInfo *root,
 | 
				
			|||||||
													   trialsortkeys,
 | 
																		   trialsortkeys,
 | 
				
			||||||
													   STARTUP_COST);
 | 
																		   STARTUP_COST);
 | 
				
			||||||
			if (innerpath != NULL &&
 | 
								if (innerpath != NULL &&
 | 
				
			||||||
				(innerpath != inner_cheapest_total ||
 | 
					 | 
				
			||||||
				 sortkeycnt < num_sortkeys) &&
 | 
					 | 
				
			||||||
				(cheapest_startup_inner == NULL ||
 | 
									(cheapest_startup_inner == NULL ||
 | 
				
			||||||
				 compare_path_costs(innerpath, cheapest_startup_inner,
 | 
									 compare_path_costs(innerpath, cheapest_startup_inner,
 | 
				
			||||||
									STARTUP_COST) < 0))
 | 
														STARTUP_COST) < 0))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user