aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r--src/backend/optimizer/plan/initsplan.c151
-rw-r--r--src/backend/optimizer/plan/planner.c5
2 files changed, 138 insertions, 18 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index cf2f798954b..ff2e13ec977 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.63 2001/06/05 05:26:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.64 2001/10/18 16:11:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -40,10 +40,13 @@ static void mark_baserels_for_outer_join(Query *root, Relids rels,
static void distribute_qual_to_rels(Query *root, Node *clause,
bool ispusheddown,
bool isouterjoin,
+ bool isdeduced,
Relids qualscope);
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
Relids join_relids);
static void add_vars_to_targetlist(Query *root, List *vars);
+static bool qual_is_redundant(Query *root, RestrictInfo *restrictinfo,
+ List *restrictlist);
static void check_mergejoinable(RestrictInfo *restrictinfo);
static void check_hashjoinable(RestrictInfo *restrictinfo);
@@ -219,7 +222,7 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
*/
foreach(qual, (List *) f->quals)
distribute_qual_to_rels(root, (Node *) lfirst(qual),
- true, false, result);
+ true, false, false, result);
}
else if (IsA(jtnode, JoinExpr))
{
@@ -279,7 +282,7 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
foreach(qual, (List *) j->quals)
distribute_qual_to_rels(root, (Node *) lfirst(qual),
- false, isouterjoin, result);
+ false, isouterjoin, false, result);
}
else
elog(ERROR, "distribute_quals_to_rels: unexpected node type %d",
@@ -339,6 +342,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
* 'ispusheddown': if TRUE, force the clause to be marked 'ispusheddown'
* (this indicates the clause came from a FromExpr, not a JoinExpr)
* 'isouterjoin': TRUE if the qual came from an OUTER JOIN's ON-clause
+ * 'isdeduced': TRUE if the qual came from implied-equality deduction
* 'qualscope': list of baserels the qual's syntactic scope covers
*
* 'qualscope' identifies what level of JOIN the qual came from. For a top
@@ -349,6 +353,7 @@ static void
distribute_qual_to_rels(Query *root, Node *clause,
bool ispusheddown,
bool isouterjoin,
+ bool isdeduced,
Relids qualscope)
{
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
@@ -405,8 +410,17 @@ distribute_qual_to_rels(Query *root, Node *clause,
* clause's own reference list. At the time we are called, the
* outerjoinset list of each baserel will show exactly those outer
* joins that are below the qual in the join tree.
+ *
+ * If the qual came from implied-equality deduction, we can evaluate
+ * the qual at its natural semantic level.
+ *
*/
- if (isouterjoin)
+ if (isdeduced)
+ {
+ Assert(sameseti(relids, qualscope));
+ can_be_equijoin = true;
+ }
+ else if (isouterjoin)
{
relids = qualscope;
can_be_equijoin = false;
@@ -461,9 +475,6 @@ distribute_qual_to_rels(Query *root, Node *clause,
*/
RelOptInfo *rel = build_base_rel(root, lfirsti(relids));
- rel->baserestrictinfo = lappend(rel->baserestrictinfo,
- restrictinfo);
-
/*
* Check for a "mergejoinable" clause even though it's not a join
* clause. This is so that we can recognize that "a.x = a.y"
@@ -474,10 +485,26 @@ distribute_qual_to_rels(Query *root, Node *clause,
*/
if (can_be_equijoin)
check_mergejoinable(restrictinfo);
+
+ /*
+ * If the clause was deduced from implied equality, check to see
+ * whether it is redundant with restriction clauses we already have
+ * for this rel. Note we cannot apply this check to user-written
+ * clauses, since we haven't found the canonical pathkey sets yet
+ * while processing user clauses. (NB: no comparable check is done
+ * in the join-clause case; redundancy will be detected when the
+ * join clause is moved into a join rel's restriction list.)
+ */
+ if (!isdeduced ||
+ !qual_is_redundant(root, restrictinfo, rel->baserestrictinfo))
+ {
+ /* Add clause to rel's restriction list */
+ rel->baserestrictinfo = lappend(rel->baserestrictinfo,
+ restrictinfo);
+ }
}
else if (relids != NIL)
{
-
/*
* 'clause' is a join clause, since there is more than one rel in
* the relid list. Set additional RestrictInfo fields for
@@ -524,9 +551,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
* 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.
+ * equivalence for future use. (We can skip this for a deduced clause,
+ * since the keys are already known equivalent in that case.)
*/
- if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid)
+ if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid &&
+ !isdeduced)
add_equijoined_keys(root, restrictinfo);
}
@@ -684,18 +713,106 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
/*
* Note: we mark the qual "pushed down" to ensure that it can never be
- * taken for an original JOIN/ON clause. We also claim it is an
- * outer- join clause, which it isn't, but that keeps
- * distribute_qual_to_rels from examining the outerjoinsets of the
- * relevant rels (which are no longer of interest, but could keep the
- * qual from being pushed down to where it should be). It'll also
- * save a useless call to add_equijoined keys...
+ * taken for an original JOIN/ON clause.
*/
distribute_qual_to_rels(root, (Node *) clause,
- true, true,
+ true, false, true,
pull_varnos((Node *) clause));
}
+/*
+ * qual_is_redundant
+ * Detect whether an implied-equality qual that turns out to be a
+ * restriction clause for a single base relation is redundant with
+ * already-known restriction clauses for that rel. This occurs with,
+ * for example,
+ * SELECT * FROM tab WHERE f1 = f2 AND f2 = f3;
+ * We need to suppress the redundant condition to avoid computing
+ * too-small selectivity, not to mention wasting time at execution.
+ */
+static bool
+qual_is_redundant(Query *root,
+ RestrictInfo *restrictinfo,
+ List *restrictlist)
+{
+ List *oldquals;
+ List *olditem;
+ Node *newleft;
+ Node *newright;
+ List *equalvars;
+ bool someadded;
+
+ /*
+ * Set cached pathkeys. NB: it is okay to do this now because this
+ * routine is only invoked while we are generating implied equalities.
+ * Therefore, the equi_key_list is already complete and so we can
+ * correctly determine canonical pathkeys.
+ */
+ cache_mergeclause_pathkeys(root, restrictinfo);
+ /* If different, say "not redundant" (should never happen) */
+ if (restrictinfo->left_pathkey != restrictinfo->right_pathkey)
+ return false;
+ /*
+ * Scan existing quals to find those referencing same pathkeys.
+ * Usually there will be few, if any, so build a list of just the
+ * interesting ones.
+ */
+ oldquals = NIL;
+ foreach(olditem, restrictlist)
+ {
+ RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
+
+ if (oldrinfo->mergejoinoperator != InvalidOid)
+ {
+ cache_mergeclause_pathkeys(root, oldrinfo);
+ if (restrictinfo->left_pathkey == oldrinfo->left_pathkey &&
+ restrictinfo->right_pathkey == oldrinfo->right_pathkey)
+ oldquals = lcons(oldrinfo, oldquals);
+ }
+ }
+ if (oldquals == NIL)
+ return false;
+ /*
+ * Now, we want to develop a list of Vars that are known equal to the
+ * left side of the new qual. We traverse the old-quals list repeatedly
+ * to transitively expand the Vars list. If at any point we find we
+ * can reach the right-side Var of the new qual, we are done. We give
+ * up when we can't expand the equalvars list any more.
+ */
+ newleft = (Node *) get_leftop(restrictinfo->clause);
+ newright = (Node *) get_rightop(restrictinfo->clause);
+ equalvars = makeList1(newleft);
+ do {
+ someadded = false;
+ foreach(olditem, oldquals)
+ {
+ RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
+ Node *oldleft = (Node *) get_leftop(oldrinfo->clause);
+ Node *oldright = (Node *) get_rightop(oldrinfo->clause);
+ Node *newguy = NULL;
+
+ if (member(oldleft, equalvars))
+ newguy = oldright;
+ else if (member(oldright, equalvars))
+ newguy = oldleft;
+ else
+ continue;
+ if (equal(newguy, newright))
+ return true; /* we proved new clause is redundant */
+ equalvars = lcons(newguy, equalvars);
+ someadded = true;
+ /*
+ * Remove this qual from list, since we don't need it anymore.
+ * Note this doesn't break the foreach() loop, since lremove
+ * doesn't touch the next-link of the removed cons cell.
+ */
+ oldquals = lremove(oldrinfo, oldquals);
+ }
+ } while (someadded);
+
+ return false; /* it's not redundant */
+}
+
/*****************************************************************************
*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a2fa8832058..48e41686e9b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.108 2001/06/05 05:26:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.109 2001/10/18 16:11:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,6 +17,9 @@
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
+#ifdef OPTIMIZER_DEBUG
+#include "nodes/print.h"
+#endif
#include "optimizer/clauses.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"