Improve outer-join-deduction logic to be able to propagate equalities

through multiple join clauses.
This commit is contained in:
Tom Lane 2005-07-03 18:26:32 +00:00
parent 76eca0ec98
commit cc9bcbc8a4
1 changed files with 209 additions and 164 deletions

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.69 2005/07/02 23:00:40 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.70 2005/07/03 18:26:32 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -35,6 +35,10 @@ static PathKeyItem *makePathKeyItem(Node *key, Oid sortop, bool checkType);
static void generate_outer_join_implications(PlannerInfo *root, static void generate_outer_join_implications(PlannerInfo *root,
List *equi_key_set, List *equi_key_set,
Relids *relids); Relids *relids);
static void sub_generate_join_implications(PlannerInfo *root,
List *equi_key_set, Relids *relids,
Node *item1, Oid sortop1,
Relids item1_relids);
static void process_implied_const_eq(PlannerInfo *root, static void process_implied_const_eq(PlannerInfo *root,
List *equi_key_set, Relids *relids, List *equi_key_set, Relids *relids,
Node *item1, Oid sortop1, Node *item1, Oid sortop1,
@ -250,65 +254,65 @@ generate_implied_equalities(PlannerInfo *root)
i1++; i1++;
} }
/*
* Match each item in the set with all that appear after it (it's
* sufficient to generate A=B, need not process B=A too).
*
* A set containing only two items cannot imply any equalities
* beyond the one that created the set, so we can skip this
* processing in that case.
*/
if (nitems >= 3)
{
i1 = 0;
foreach(ptr1, curset)
{
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
bool i1_is_variable = !bms_is_empty(relids[i1]);
ListCell *ptr2;
int i2 = i1 + 1;
for_each_cell(ptr2, lnext(ptr1))
{
PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2);
bool i2_is_variable = !bms_is_empty(relids[i2]);
/*
* If it's "const = const" then just ignore it altogether.
* There is no place in the restrictinfo structure to
* store it. (If the two consts are in fact unequal, then
* propagating the comparison to Vars will cause us to
* produce zero rows out, as expected.)
*/
if (i1_is_variable || i2_is_variable)
{
/*
* Tell process_implied_equality to delete the clause,
* not add it, if it's "var = var" and we have
* constants present in the list.
*/
bool delete_it = (have_consts &&
i1_is_variable &&
i2_is_variable);
process_implied_equality(root,
item1->key, item2->key,
item1->sortop, item2->sortop,
relids[i1], relids[i2],
delete_it);
}
i2++;
}
i1++;
}
}
/* /*
* If we have constant(s) and outer joins, try to propagate the * If we have constant(s) and outer joins, try to propagate the
* constants through outer-join quals. * constants through outer-join quals.
*/ */
if (have_consts && root->hasOuterJoins) if (have_consts && root->hasOuterJoins)
generate_outer_join_implications(root, curset, relids); generate_outer_join_implications(root, curset, relids);
/*
* A set containing only two items cannot imply any equalities
* beyond the one that created the set, so we can skip it.
*/
if (nitems < 3)
continue;
/*
* Match each item in the set with all that appear after it (it's
* sufficient to generate A=B, need not process B=A too).
*/
i1 = 0;
foreach(ptr1, curset)
{
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
bool i1_is_variable = !bms_is_empty(relids[i1]);
ListCell *ptr2;
int i2 = i1 + 1;
for_each_cell(ptr2, lnext(ptr1))
{
PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2);
bool i2_is_variable = !bms_is_empty(relids[i2]);
/*
* If it's "const = const" then just ignore it altogether.
* There is no place in the restrictinfo structure to
* store it. (If the two consts are in fact unequal, then
* propagating the comparison to Vars will cause us to
* produce zero rows out, as expected.)
*/
if (i1_is_variable || i2_is_variable)
{
/*
* Tell process_implied_equality to delete the clause,
* not add it, if it's "var = var" and we have
* constants present in the list.
*/
bool delete_it = (have_consts &&
i1_is_variable &&
i2_is_variable);
process_implied_equality(root,
item1->key, item2->key,
item1->sortop, item2->sortop,
relids[i1], relids[i2],
delete_it);
}
i2++;
}
i1++;
}
} }
} }
@ -362,118 +366,154 @@ generate_outer_join_implications(PlannerInfo *root,
List *equi_key_set, List *equi_key_set,
Relids *relids) Relids *relids)
{ {
ListCell *l1; ListCell *l;
int i = 0;
/* Examine each mergejoinable outer-join clause with OUTERVAR on left */ /* Process each non-constant element of equi_key_set */
foreach(l1, root->left_join_clauses) foreach(l, equi_key_set)
{ {
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l1); PathKeyItem *item1 = (PathKeyItem *) lfirst(l);
Node *leftop = get_leftop(rinfo->clause);
Node *rightop = get_rightop(rinfo->clause);
ListCell *l2;
/* Scan to see if it matches any element of equi_key_set */ if (!bms_is_empty(relids[i]))
foreach(l2, equi_key_set)
{ {
PathKeyItem *item1 = (PathKeyItem *) lfirst(l2); sub_generate_join_implications(root, equi_key_set, relids,
item1->key,
item1->sortop,
relids[i]);
}
i++;
}
}
if (equal(leftop, item1->key) && /*
rinfo->left_sortop == item1->sortop) * sub_generate_join_implications
{ * Propagate a constant equality through outer join clauses.
/* *
* Yes, so find constant member(s) of set and generate * The item described by item1/sortop1/item1_relids has been determined
* implied INNERVAR = CONSTANT * to be equal to the constant(s) listed in equi_key_set. Recursively
*/ * trace out the implications of this.
process_implied_const_eq(root, equi_key_set, relids, *
rightop, * equi_key_set and relids are as for generate_outer_join_implications.
rinfo->right_sortop, */
rinfo->right_relids, static void
false); sub_generate_join_implications(PlannerInfo *root,
/* List *equi_key_set, Relids *relids,
* We can remove the explicit outer join qual, too, Node *item1, Oid sortop1, Relids item1_relids)
* since we now have tests forcing each of its sides
* to the same value.
*/
process_implied_equality(root,
leftop,
rightop,
rinfo->left_sortop,
rinfo->right_sortop,
rinfo->left_relids,
rinfo->right_relids,
true);
/* No need to match against remaining set members */ {
break; ListCell *l;
}
/*
* Examine each mergejoinable outer-join clause with OUTERVAR on left,
* looking for an OUTERVAR identical to item1
*/
foreach(l, root->left_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Node *leftop = get_leftop(rinfo->clause);
if (equal(leftop, item1) && rinfo->left_sortop == sortop1)
{
/*
* Match, so find constant member(s) of set and generate
* implied INNERVAR = CONSTANT
*/
Node *rightop = get_rightop(rinfo->clause);
process_implied_const_eq(root, equi_key_set, relids,
rightop,
rinfo->right_sortop,
rinfo->right_relids,
false);
/*
* We can remove explicit tests of this outer-join qual, too,
* since we now have tests forcing each of its sides
* to the same value.
*/
process_implied_equality(root,
leftop, rightop,
rinfo->left_sortop, rinfo->right_sortop,
rinfo->left_relids, rinfo->right_relids,
true);
/*
* And recurse to see if we can deduce anything from
* INNERVAR = CONSTANT
*/
sub_generate_join_implications(root, equi_key_set, relids,
rightop,
rinfo->right_sortop,
rinfo->right_relids);
} }
} }
/* Examine each mergejoinable outer-join clause with OUTERVAR on right */ /* The same, looking at clauses with OUTERVAR on right */
foreach(l1, root->right_join_clauses) foreach(l, root->right_join_clauses)
{ {
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l1); RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Node *leftop = get_leftop(rinfo->clause);
Node *rightop = get_rightop(rinfo->clause); Node *rightop = get_rightop(rinfo->clause);
ListCell *l2;
/* Scan to see if it matches any element of equi_key_set */ if (equal(rightop, item1) && rinfo->right_sortop == sortop1)
foreach(l2, equi_key_set)
{ {
PathKeyItem *item1 = (PathKeyItem *) lfirst(l2); /*
* Match, so find constant member(s) of set and generate
* implied INNERVAR = CONSTANT
*/
Node *leftop = get_leftop(rinfo->clause);
if (equal(rightop, item1->key) && process_implied_const_eq(root, equi_key_set, relids,
rinfo->right_sortop == item1->sortop) leftop,
{ rinfo->left_sortop,
/* rinfo->left_relids,
* Yes, so find constant member(s) of set and generate false);
* implied INNERVAR = CONSTANT /*
*/ * We can remove explicit tests of this outer-join qual, too,
process_implied_const_eq(root, equi_key_set, relids, * since we now have tests forcing each of its sides
leftop, * to the same value.
rinfo->left_sortop, */
rinfo->left_relids, process_implied_equality(root,
false); leftop, rightop,
/* rinfo->left_sortop, rinfo->right_sortop,
* We can remove the explicit outer join qual, too, rinfo->left_relids, rinfo->right_relids,
* since we now have tests forcing each of its sides true);
* to the same value. /*
*/ * And recurse to see if we can deduce anything from
process_implied_equality(root, * INNERVAR = CONSTANT
leftop, */
rightop, sub_generate_join_implications(root, equi_key_set, relids,
rinfo->left_sortop, leftop,
rinfo->right_sortop, rinfo->left_sortop,
rinfo->left_relids, rinfo->left_relids);
rinfo->right_relids,
true);
/* No need to match against remaining set members */
break;
}
} }
} }
/* Examine each mergejoinable full-join clause */ /*
foreach(l1, root->full_join_clauses) * Only COALESCE(x,y) items can possibly match full joins
*/
if (IsA(item1, CoalesceExpr))
{ {
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l1); CoalesceExpr *cexpr = (CoalesceExpr *) item1;
Node *leftop = get_leftop(rinfo->clause); Node *cfirst;
Node *rightop = get_rightop(rinfo->clause); Node *csecond;
int i1 = 0;
ListCell *l2;
/* Scan to see if it matches any element of equi_key_set */ if (list_length(cexpr->args) != 2)
foreach(l2, equi_key_set) return;
cfirst = (Node *) linitial(cexpr->args);
csecond = (Node *) lsecond(cexpr->args);
/*
* Examine each mergejoinable full-join clause, looking for a
* clause of the form "x = y" matching the COALESCE(x,y) expression
*/
foreach(l, root->full_join_clauses)
{ {
PathKeyItem *item1 = (PathKeyItem *) lfirst(l2); RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
CoalesceExpr *cexpr = (CoalesceExpr *) item1->key; Node *leftop = get_leftop(rinfo->clause);
Node *rightop = get_rightop(rinfo->clause);
/* /*
* Try to match a pathkey containing a COALESCE() expression * We can assume the COALESCE() inputs are in the same order
* to the join clause. We can assume the COALESCE() inputs * as the join clause, since both were automatically generated
* are in the same order as the join clause, since both were * in the cases we care about.
* automatically generated in the cases we care about.
* *
* XXX currently this may fail to match in cross-type cases * XXX currently this may fail to match in cross-type cases
* because the COALESCE will contain typecast operations while * because the COALESCE will contain typecast operations while
@ -482,15 +522,13 @@ generate_outer_join_implications(PlannerInfo *root,
* Is it OK to strip implicit coercions from the COALESCE * Is it OK to strip implicit coercions from the COALESCE
* arguments? What of the sortops in such cases? * arguments? What of the sortops in such cases?
*/ */
if (IsA(cexpr, CoalesceExpr) && if (equal(leftop, cfirst) &&
list_length(cexpr->args) == 2 && equal(rightop, csecond) &&
equal(leftop, (Node *) linitial(cexpr->args)) && rinfo->left_sortop == sortop1 &&
equal(rightop, (Node *) lsecond(cexpr->args)) && rinfo->right_sortop == sortop1)
rinfo->left_sortop == item1->sortop &&
rinfo->right_sortop == item1->sortop)
{ {
/* /*
* Yes, so find constant member(s) of set and generate * Match, so find constant member(s) of set and generate
* implied LEFTVAR = CONSTANT * implied LEFTVAR = CONSTANT
*/ */
process_implied_const_eq(root, equi_key_set, relids, process_implied_const_eq(root, equi_key_set, relids,
@ -506,28 +544,37 @@ generate_outer_join_implications(PlannerInfo *root,
false); false);
/* ... and remove COALESCE() = CONSTANT */ /* ... and remove COALESCE() = CONSTANT */
process_implied_const_eq(root, equi_key_set, relids, process_implied_const_eq(root, equi_key_set, relids,
item1->key, item1,
item1->sortop, sortop1,
relids[i1], item1_relids,
true); true);
/* /*
* We can remove the explicit outer join qual, too, * We can remove explicit tests of this outer-join qual, too,
* since we now have tests forcing each of its sides * since we now have tests forcing each of its sides
* to the same value. * to the same value.
*/ */
process_implied_equality(root, process_implied_equality(root,
leftop, leftop, rightop,
rightop,
rinfo->left_sortop, rinfo->left_sortop,
rinfo->right_sortop, rinfo->right_sortop,
rinfo->left_relids, rinfo->left_relids,
rinfo->right_relids, rinfo->right_relids,
true); true);
/*
* And recurse to see if we can deduce anything from
* LEFTVAR = CONSTANT
*/
sub_generate_join_implications(root, equi_key_set, relids,
leftop,
rinfo->left_sortop,
rinfo->left_relids);
/* ... and RIGHTVAR = CONSTANT */
sub_generate_join_implications(root, equi_key_set, relids,
rightop,
rinfo->right_sortop,
rinfo->right_relids);
/* No need to match against remaining set members */
break;
} }
i1++;
} }
} }
} }
@ -537,10 +584,8 @@ generate_outer_join_implications(PlannerInfo *root,
* Apply process_implied_equality with the given item and each * Apply process_implied_equality with the given item and each
* pseudoconstant member of equi_key_set. * pseudoconstant member of equi_key_set.
* *
* This is just a subroutine to save some cruft in * equi_key_set and relids are as for generate_outer_join_implications,
* generate_outer_join_implications. equi_key_set and relids are as in * the other parameters as for process_implied_equality.
* generate_outer_join_implications, the other parameters as for
* process_implied_equality.
*/ */
static void static void
process_implied_const_eq(PlannerInfo *root, List *equi_key_set, Relids *relids, process_implied_const_eq(PlannerInfo *root, List *equi_key_set, Relids *relids,