1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-29 23:43:17 +03:00

Teach planner about some cases where a restriction clause can be

propagated inside an outer join.  In particular, given
LEFT JOIN ON (A = B) WHERE A = constant, we cannot conclude that
B = constant at the top level (B might be null instead), but we
can nonetheless put a restriction B = constant into the quals for
B's relation, since no inner-side rows not meeting that condition
can contribute to the final result.  Similarly, given
FULL JOIN USING (J) WHERE J = constant, we can't directly conclude
that either input J variable = constant, but it's OK to push such
quals into each input rel.  Per recent gripe from Kim Bisgaard.
Along the way, remove 'valid_everywhere' flag from RestrictInfo,
as on closer analysis it was not being used for anything, and was
defined backwards anyway.
This commit is contained in:
Tom Lane
2005-07-02 23:00:42 +00:00
parent ea1e2b948d
commit cc5e80b8d1
13 changed files with 424 additions and 114 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.107 2005/06/09 04:18:59 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.108 2005/07/02 23:00:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -374,8 +374,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
Relids qualscope)
{
Relids relids;
bool valid_everywhere;
bool can_be_equijoin;
bool maybe_equijoin;
bool maybe_outer_join;
RestrictInfo *restrictinfo;
RelOptInfo *rel;
List *vars;
@@ -409,14 +409,15 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
if (isdeduced)
{
/*
* If the qual came from implied-equality deduction, we can
* evaluate the qual at its natural semantic level. It is not
* affected by any outer-join rules (else we'd not have decided
* the vars were equal).
* If the qual came from implied-equality deduction, we always
* evaluate the qual at its natural semantic level. It is the
* responsibility of the deducer not to create any quals that
* should be delayed by outer-join rules.
*/
Assert(bms_equal(relids, qualscope));
valid_everywhere = true;
can_be_equijoin = true;
/* Needn't feed it back for more deductions */
maybe_equijoin = false;
maybe_outer_join = false;
}
else if (bms_overlap(relids, outerjoin_nonnullable))
{
@@ -434,8 +435,14 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
* result, so we treat it the same as an ordinary inner-join qual.
*/
relids = qualscope;
valid_everywhere = false;
can_be_equijoin = false;
/*
* We can't use such a clause to deduce equijoin (the left and
* right sides might be unequal above the join because one of
* them has gone to NULL) ... but we might be able to use it
* for more limited purposes.
*/
maybe_equijoin = false;
maybe_outer_join = true;
}
else
{
@@ -449,34 +456,25 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
* time we are called, the outerjoinset of each baserel will show
* exactly those outer joins that are below the qual in the join
* tree.
*
* We also need to determine whether the qual is "valid everywhere",
* which is true if the qual mentions no variables that are
* involved in lower-level outer joins (this may be an overly
* strong test).
*/
Relids addrelids = NULL;
Relids tmprelids;
int relno;
valid_everywhere = true;
tmprelids = bms_copy(relids);
while ((relno = bms_first_member(tmprelids)) >= 0)
{
RelOptInfo *rel = find_base_rel(root, relno);
if (rel->outerjoinset != NULL)
{
addrelids = bms_add_members(addrelids, rel->outerjoinset);
valid_everywhere = false;
}
}
bms_free(tmprelids);
if (bms_is_subset(addrelids, relids))
{
/* Qual is not affected by any outer-join restriction */
can_be_equijoin = true;
maybe_equijoin = true;
}
else
{
@@ -488,9 +486,10 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
* Because application of the qual will be delayed by outer
* join, we mustn't assume its vars are equal everywhere.
*/
can_be_equijoin = false;
maybe_equijoin = false;
}
bms_free(addrelids);
maybe_outer_join = false;
}
/*
@@ -508,7 +507,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
*/
restrictinfo = make_restrictinfo((Expr *) clause,
is_pushed_down,
valid_everywhere,
relids);
/*
@@ -533,8 +531,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
* allows us to consider z and q equal after their rels are
* joined.
*/
if (can_be_equijoin)
check_mergejoinable(restrictinfo);
check_mergejoinable(restrictinfo);
/*
* If the clause was deduced from implied equality, check to
@@ -601,18 +598,60 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
}
/*
* If the clause has a mergejoinable operator, and is not an
* outer-join qualification nor bubbled up due to an outer join, then
* the two sides represent equivalent PathKeyItems for path keys: any
* path that is sorted by one side will also be sorted by the other
* (as soon as the two rels are joined, that is). Record the key
* equivalence for future use. (We can skip this for a deduced
* clause, since the keys are already known equivalent in that case.)
* If the clause has a mergejoinable operator, we may be able to
* deduce more things from it under the principle of transitivity.
*
* If it is not an outer-join qualification nor bubbled up due to an outer
* join, then the two sides represent equivalent PathKeyItems for path
* keys: any path that is sorted by one side will also be sorted by the
* other (as soon as the two rels are joined, that is). Pass such clauses
* to add_equijoined_keys.
*
* If it is a left or right outer-join qualification that relates the two
* sides of the outer join (no funny business like leftvar1 = leftvar2 +
* rightvar), we add it to root->left_join_clauses or
* root->right_join_clauses according to which side the nonnullable
* variable appears on.
*
* If it is a full outer-join qualification, we add it to
* root->full_join_clauses. (Ideally we'd discard cases that aren't
* leftvar = rightvar, as we do for left/right joins, but this routine
* doesn't have the info needed to do that; and the current usage of the
* full_join_clauses list doesn't require that, so it's not currently
* worth complicating this routine's API to make it possible.)
*/
if (can_be_equijoin &&
restrictinfo->mergejoinoperator != InvalidOid &&
!isdeduced)
add_equijoined_keys(root, restrictinfo);
if (restrictinfo->mergejoinoperator != InvalidOid)
{
if (maybe_equijoin)
add_equijoined_keys(root, restrictinfo);
else if (maybe_outer_join && restrictinfo->can_join)
{
if (bms_is_subset(restrictinfo->left_relids,
outerjoin_nonnullable) &&
!bms_overlap(restrictinfo->right_relids,
outerjoin_nonnullable))
{
/* we have outervar = innervar */
root->left_join_clauses = lappend(root->left_join_clauses,
restrictinfo);
}
else if (bms_is_subset(restrictinfo->right_relids,
outerjoin_nonnullable) &&
!bms_overlap(restrictinfo->left_relids,
outerjoin_nonnullable))
{
/* we have innervar = outervar */
root->right_join_clauses = lappend(root->right_join_clauses,
restrictinfo);
}
else if (bms_equal(outerjoin_nonnullable, qualscope))
{
/* FULL JOIN (above tests cannot match in this case) */
root->full_join_clauses = lappend(root->full_join_clauses,
restrictinfo);
}
}
}
}
/*