1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-17 06:41:09 +03:00

Modify planner's implied-equality-deduction code so that when a set

of known-equal expressions includes any constant expressions (including
Params from outer queries), we actively suppress any 'var = var'
clauses that are or could be deduced from the set, generating only the
deducible 'var = const' clauses instead.  The idea here is to push down
the restrictions implied by the equality set to base relations whenever
possible.  Once we have applied the 'var = const' clauses, the 'var = var'
clauses are redundant, and should be suppressed both to save work at
execution and to avoid double-counting restrictivity.
This commit is contained in:
Tom Lane
2003-01-24 03:58:44 +00:00
parent ef7422510e
commit f5e83662d0
12 changed files with 395 additions and 168 deletions

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.44 2003/01/15 19:35:40 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.45 2003/01/24 03:58:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,6 +23,7 @@
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
#include "parser/parse_func.h"
#include "utils/lsyscache.h"
@ -147,14 +148,30 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
* generate_implied_equalities
* Scan the completed equi_key_list for the query, and generate explicit
* qualifications (WHERE clauses) for all the pairwise equalities not
* already mentioned in the quals. This is useful because the additional
* clauses help the selectivity-estimation code, and in fact it's
* *necessary* to ensure that sort keys we think are equivalent really
* are (see src/backend/optimizer/README for more info).
* already mentioned in the quals; or remove qualifications found to be
* redundant.
*
* Adding deduced equalities is useful because the additional clauses help
* the selectivity-estimation code and may allow better joins to be chosen;
* and in fact it's *necessary* to ensure that sort keys we think are
* equivalent really are (see src/backend/optimizer/README for more info).
*
* If an equi_key_list set includes any constants then we adopt a different
* strategy: we record all the "var = const" deductions we can make, and
* actively remove all the "var = var" clauses that are implied by the set
* (including the clauses that originally gave rise to the set!). The reason
* is that given input like "a = b AND b = 42", once we have deduced "a = 42"
* there is no longer any need to apply the clause "a = b"; not only is
* it a waste of time to check it, but we will misestimate selectivity if the
* clause is left in. So we must remove it. For this purpose, any pathkey
* item that mentions no Vars of the current level can be taken as a constant.
* (The only case where this would be risky is if the item contains volatile
* functions; but we will never consider such an expression to be a pathkey
* at all, because check_mergejoinable() will reject it.)
*
* This routine just walks the equi_key_list to find all pairwise equalities.
* We call process_implied_equality (in plan/initsplan.c) to determine whether
* each is already known and add it to the proper restrictinfo list if not.
* We call process_implied_equality (in plan/initsplan.c) to adjust the
* restrictinfo datastructures for each pair.
*/
void
generate_implied_equalities(Query *root)
@ -164,35 +181,119 @@ generate_implied_equalities(Query *root)
foreach(cursetlink, root->equi_key_list)
{
List *curset = lfirst(cursetlink);
int nitems = length(curset);
Relids *relids;
bool have_consts;
List *ptr1;
int i1;
/*
* A set containing only two items cannot imply any equalities
* beyond the one that created the set, so we can skip it.
*/
if (length(curset) < 3)
if (nitems < 3)
continue;
/*
* Collect info about relids mentioned in each item. For this
* routine we only really care whether there are any at all in
* each item, but process_implied_equality() needs the exact
* lists, so we may as well pull them here.
*/
relids = (Relids *) palloc(nitems * sizeof(Relids));
have_consts = false;
i1 = 0;
foreach(ptr1, curset)
{
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
relids[i1] = pull_varnos(item1->key);
if (relids[i1] == NIL)
have_consts = true;
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).
*/
i1 = 0;
foreach(ptr1, curset)
{
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
List *ptr2;
int i2 = i1 + 1;
foreach(ptr2, lnext(ptr1))
{
PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2);
process_implied_equality(root, item1->key, item2->key,
item1->sortop, item2->sortop);
/*
* 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 (relids[i1] != NIL || relids[i2] != NIL)
{
/*
* 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 &&
relids[i1] != NIL &&
relids[i2] != NIL);
process_implied_equality(root,
item1->key, item2->key,
item1->sortop, item2->sortop,
relids[i1], relids[i2],
delete_it);
}
i2++;
}
i1++;
}
}
}
/*
* exprs_known_equal
* Detect whether two expressions are known equal due to equijoin clauses.
*
* Note: does not bother to check for "equal(item1, item2)"; caller must
* check that case if it's possible to pass identical items.
*/
bool
exprs_known_equal(Query *root, Node *item1, Node *item2)
{
List *cursetlink;
foreach(cursetlink, root->equi_key_list)
{
List *curset = lfirst(cursetlink);
bool item1member = false;
bool item2member = false;
List *ptr;
foreach(ptr, curset)
{
PathKeyItem *pitem = (PathKeyItem *) lfirst(ptr);
if (equal(item1, pitem->key))
item1member = true;
else if (equal(item2, pitem->key))
item2member = true;
/* Exit as soon as equality is proven */
if (item1member && item2member)
return true;
}
}
return false;
}
/*
* make_canonical_pathkey
* Given a PathKeyItem, find the equi_key_list subset it is a member of,