mirror of
https://github.com/postgres/postgres.git
synced 2025-06-27 23:21:58 +03:00
Fix some planner issues found while investigating Kevin Grittner's report
of poorer planning in 8.3 than 8.2: 1. After pushing a constant across an outer join --- ie, given "a LEFT JOIN b ON (a.x = b.y) WHERE a.x = 42", we can deduce that b.y is sort of equal to 42, in the sense that we needn't fetch any b rows where it isn't 42 --- loop to see if any additional deductions can be made. Previous releases did that by recursing, but I had mistakenly thought that this was no longer necessary given the EquivalenceClass machinery. 2. Allow pushing constants across outer join conditions even if the condition is outerjoin_delayed due to a lower outer join. This is safe as long as the condition is strict and we re-test it at the upper join. 3. Keep the outer-join clause even if we successfully push a constant across it. This is *necessary* in the outerjoin_delayed case, but even in the simple case, it seems better to do this to ensure that the join search order heuristics will consider the join as reasonable to make. Mark such a clause as having selectivity 1.0, though, since it's not going to eliminate very many rows after application of the constant condition. 4. Tweak have_relevant_eclass_joinclause to report that two relations are joinable when they have vars that are equated to the same constant. We won't actually generate any joinclause from such an EquivalenceClass, but again it seems that in such a case it's a good idea to consider the join as worth costing out. 5. Fix a bug in select_mergejoin_clauses that was exposed by these changes: we have to reject candidate mergejoin clauses if either side was equated to a constant, because we can't construct a canonical pathkey list for such a clause. This is an implementation restriction that might be worth fixing someday, but it doesn't seem critical to get it done for 8.3.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.114 2008/01/01 19:45:50 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.115 2008/01/09 20:42:27 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -34,7 +34,8 @@ static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
List *restrictlist, JoinType jointype);
|
||||
static Path *best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,
|
||||
RelOptInfo *outer_rel, JoinType jointype);
|
||||
static List *select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
static List *select_mergejoin_clauses(PlannerInfo *root,
|
||||
RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist,
|
||||
@ -69,7 +70,8 @@ add_paths_to_joinrel(PlannerInfo *root,
|
||||
* disable if it's a full join.
|
||||
*/
|
||||
if (enable_mergejoin || jointype == JOIN_FULL)
|
||||
mergeclause_list = select_mergejoin_clauses(joinrel,
|
||||
mergeclause_list = select_mergejoin_clauses(root,
|
||||
joinrel,
|
||||
outerrel,
|
||||
innerrel,
|
||||
restrictlist,
|
||||
@ -883,7 +885,8 @@ best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,
|
||||
* currently of interest.
|
||||
*/
|
||||
static List *
|
||||
select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
select_mergejoin_clauses(PlannerInfo *root,
|
||||
RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist,
|
||||
@ -937,6 +940,35 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
continue; /* no good for these input relations */
|
||||
}
|
||||
|
||||
/*
|
||||
* Insist that each side have a non-redundant eclass. This
|
||||
* restriction is needed because various bits of the planner expect
|
||||
* that each clause in a merge be associatable with some pathkey in a
|
||||
* canonical pathkey list, but redundant eclasses can't appear in
|
||||
* canonical sort orderings. (XXX it might be worth relaxing this,
|
||||
* but not enough time to address it for 8.3.)
|
||||
*
|
||||
* Note: it would be bad if this condition failed for an otherwise
|
||||
* mergejoinable FULL JOIN clause, since that would result in
|
||||
* undesirable planner failure. I believe that is not possible
|
||||
* however; a variable involved in a full join could only appear
|
||||
* in below_outer_join eclasses, which aren't considered redundant.
|
||||
*
|
||||
* This case *can* happen for left/right join clauses: the
|
||||
* outer-side variable could be equated to a constant. Because we
|
||||
* will propagate that constant across the join clause, the loss of
|
||||
* ability to do a mergejoin is not really all that big a deal, and
|
||||
* so it's not clear that improving this is important.
|
||||
*/
|
||||
cache_mergeclause_eclasses(root, restrictinfo);
|
||||
|
||||
if (EC_MUST_BE_REDUNDANT(restrictinfo->left_ec) ||
|
||||
EC_MUST_BE_REDUNDANT(restrictinfo->right_ec))
|
||||
{
|
||||
have_nonmergeable_joinclause = true;
|
||||
continue; /* can't handle redundant eclasses */
|
||||
}
|
||||
|
||||
result_list = lappend(result_list, restrictinfo);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user