Improve outer-join-deduction logic to be able to propagate equalities
through multiple join clauses.
This commit is contained in:
parent
76eca0ec98
commit
cc9bcbc8a4
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user