mirror of
https://github.com/postgres/postgres.git
synced 2025-08-31 17:02:12 +03:00
Restructure planning of nestloop inner indexscans so that the set of usable
joinclauses is determined accurately for each join. Formerly, the code only considered joinclauses that used all of the rels from the outer side of the join; thus for example FROM (a CROSS JOIN b) JOIN c ON (c.f1 = a.x AND c.f2 = b.y) could not exploit a two-column index on c(f1,f2), since neither of the qual clauses would be in the joininfo list it looked in. The new code does this correctly, and also is able to eliminate redundant clauses, thus fixing the problem noted 24-Oct-02 by Hans-Jürgen Schönig.
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.220 2002/11/23 03:59:07 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.221 2002/11/24 21:52:13 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1148,7 +1148,9 @@ _copyRelOptInfo(RelOptInfo *from)
|
||||
newnode->baserestrictcost = from->baserestrictcost;
|
||||
newnode->outerjoinset = listCopy(from->outerjoinset);
|
||||
Node_Copy(from, newnode, joininfo);
|
||||
Node_Copy(from, newnode, innerjoin);
|
||||
|
||||
newnode->index_outer_relids = listCopy(from->index_outer_relids);
|
||||
Node_Copy(from, newnode, index_inner_paths);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
@@ -1200,6 +1202,9 @@ _copyIndexOptInfo(IndexOptInfo *from)
|
||||
Node_Copy(from, newnode, indpred);
|
||||
newnode->unique = from->unique;
|
||||
|
||||
newnode->outer_relids = listCopy(from->outer_relids);
|
||||
Node_Copy(from, newnode, inner_paths);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
@@ -1262,8 +1267,6 @@ _copyIndexPath(IndexPath *from)
|
||||
Node_Copy(from, newnode, indexinfo);
|
||||
Node_Copy(from, newnode, indexqual);
|
||||
newnode->indexscandir = from->indexscandir;
|
||||
newnode->joinrelids = listCopy(from->joinrelids);
|
||||
newnode->alljoinquals = from->alljoinquals;
|
||||
newnode->rows = from->rows;
|
||||
|
||||
return newnode;
|
||||
@@ -1491,6 +1494,25 @@ _copyJoinInfo(JoinInfo *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _copyInnerIndexscanInfo
|
||||
* ----------------
|
||||
*/
|
||||
static InnerIndexscanInfo *
|
||||
_copyInnerIndexscanInfo(InnerIndexscanInfo *from)
|
||||
{
|
||||
InnerIndexscanInfo *newnode = makeNode(InnerIndexscanInfo);
|
||||
|
||||
/*
|
||||
* copy remainder of node
|
||||
*/
|
||||
newnode->other_relids = listCopy(from->other_relids);
|
||||
newnode->isouterjoin = from->isouterjoin;
|
||||
Node_Copy(from, newnode, best_innerpath);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/* ****************************************************************
|
||||
* parsenodes.h copy functions
|
||||
* ****************************************************************
|
||||
@@ -2952,6 +2974,9 @@ copyObject(void *from)
|
||||
case T_IndexOptInfo:
|
||||
retval = _copyIndexOptInfo(from);
|
||||
break;
|
||||
case T_InnerIndexscanInfo:
|
||||
retval = _copyInnerIndexscanInfo(from);
|
||||
break;
|
||||
|
||||
/*
|
||||
* VALUE NODES
|
||||
|
@@ -20,7 +20,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.166 2002/11/23 03:59:07 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.167 2002/11/24 21:52:13 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -429,10 +429,6 @@ _equalIndexPath(IndexPath *a, IndexPath *b)
|
||||
return false;
|
||||
if (a->indexscandir != b->indexscandir)
|
||||
return false;
|
||||
if (!equali(a->joinrelids, b->joinrelids))
|
||||
return false;
|
||||
if (a->alljoinquals != b->alljoinquals)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Skip 'rows' because of possibility of floating-point roundoff
|
||||
@@ -548,13 +544,11 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* We ignore eval_cost, this_selec, left/right_pathkey, and
|
||||
* left/right_bucketsize, since they may not be set yet, and should be
|
||||
* derivable from the clause anyway. Probably it's not really
|
||||
* necessary to compare any of these remaining fields ...
|
||||
* We ignore subclauseindices, eval_cost, this_selec, left/right_pathkey,
|
||||
* and left/right_bucketsize, since they may not be set yet, and should be
|
||||
* derivable from the clause anyway. Probably it's not really necessary
|
||||
* to compare any of these remaining fields ...
|
||||
*/
|
||||
if (!equal(a->subclauseindices, b->subclauseindices))
|
||||
return false;
|
||||
if (a->mergejoinoperator != b->mergejoinoperator)
|
||||
return false;
|
||||
if (a->left_sortop != b->left_sortop)
|
||||
@@ -576,6 +570,18 @@ _equalJoinInfo(JoinInfo *a, JoinInfo *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalInnerIndexscanInfo(InnerIndexscanInfo *a, InnerIndexscanInfo *b)
|
||||
{
|
||||
if (!equali(a->other_relids, b->other_relids))
|
||||
return false;
|
||||
if (a->isouterjoin != b->isouterjoin)
|
||||
return false;
|
||||
if (!equal(a->best_innerpath, b->best_innerpath))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stuff from parsenodes.h
|
||||
*/
|
||||
@@ -2120,6 +2126,9 @@ equal(void *a, void *b)
|
||||
case T_JoinInfo:
|
||||
retval = _equalJoinInfo(a, b);
|
||||
break;
|
||||
case T_InnerIndexscanInfo:
|
||||
retval = _equalInnerIndexscanInfo(a, b);
|
||||
break;
|
||||
case T_TidPath:
|
||||
retval = _equalTidPath(a, b);
|
||||
break;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.41 2002/06/20 20:29:29 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.42 2002/11/24 21:52:13 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* XXX a few of the following functions are duplicated to handle
|
||||
@@ -372,6 +372,46 @@ set_unioni(List *l1, List *l2)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the intersection of two lists,
|
||||
* ie, all members of both l1 and l2.
|
||||
*
|
||||
* NOTE: if there are duplicates in l1 they will still be duplicate in the
|
||||
* result; but duplicates in l2 are discarded.
|
||||
*
|
||||
* The result is a fresh List, but it points to the same member nodes
|
||||
* as were in the inputs.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
List *
|
||||
set_intersect(List *l1, List *l2)
|
||||
{
|
||||
List *retval = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, l1)
|
||||
{
|
||||
if (member(lfirst(i), l2))
|
||||
retval = lappend(retval, lfirst(i));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
List *
|
||||
set_intersecti(List *l1, List *l2)
|
||||
{
|
||||
List *retval = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, l1)
|
||||
{
|
||||
if (intMember(lfirsti(i), l2))
|
||||
retval = lappendi(retval, lfirsti(i));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* member()
|
||||
* nondestructive, returns t iff l1 is a member of the list l2
|
||||
|
@@ -5,7 +5,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.180 2002/11/15 02:50:07 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.181 2002/11/24 21:52:13 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||
@@ -1067,12 +1067,8 @@ _outIndexPath(StringInfo str, IndexPath *node)
|
||||
appendStringInfo(str, " :indexqual ");
|
||||
_outNode(str, node->indexqual);
|
||||
|
||||
appendStringInfo(str, " :indexscandir %d :joinrelids ",
|
||||
(int) node->indexscandir);
|
||||
_outIntList(str, node->joinrelids);
|
||||
|
||||
appendStringInfo(str, " :alljoinquals %s :rows %.2f ",
|
||||
booltostr(node->alljoinquals),
|
||||
appendStringInfo(str, " :indexscandir %d :rows %.2f ",
|
||||
(int) node->indexscandir,
|
||||
node->rows);
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.137 2002/11/15 02:50:07 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.138 2002/11/24 21:52:13 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Most of the read functions for plan nodes are tested. (In fact, they
|
||||
@@ -1824,13 +1824,6 @@ _readIndexPath(void)
|
||||
token = pg_strtok(&length); /* now read it */
|
||||
local_node->indexscandir = (ScanDirection) atoi(token);
|
||||
|
||||
token = pg_strtok(&length); /* get :joinrelids */
|
||||
local_node->joinrelids = toIntList(nodeRead(true));
|
||||
|
||||
token = pg_strtok(&length); /* get :alljoinquals */
|
||||
token = pg_strtok(&length); /* now read it */
|
||||
local_node->alljoinquals = strtobool(token);
|
||||
|
||||
token = pg_strtok(&length); /* get :rows */
|
||||
token = pg_strtok(&length); /* now read it */
|
||||
local_node->rows = atof(token);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.71 2002/09/04 20:31:20 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.72 2002/11/24 21:52:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -42,8 +42,6 @@ static void match_unsorted_inner(Query *root, RelOptInfo *joinrel,
|
||||
static void hash_inner_and_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, JoinType jointype);
|
||||
static Path *best_innerjoin(List *join_paths, List *outer_relid,
|
||||
JoinType jointype);
|
||||
static List *select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
@@ -351,8 +349,8 @@ match_unsorted_outer(Query *root,
|
||||
* Get the best innerjoin indexpath (if any) for this outer rel. It's
|
||||
* the same for all outer paths.
|
||||
*/
|
||||
bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids,
|
||||
jointype);
|
||||
bestinnerjoin = best_inner_indexscan(root, innerrel,
|
||||
outerrel->relids, jointype);
|
||||
|
||||
foreach(i, outerrel->pathlist)
|
||||
{
|
||||
@@ -812,69 +810,6 @@ hash_inner_and_outer(Query *root,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* best_innerjoin
|
||||
* Find the cheapest index path that has already been identified by
|
||||
* indexable_joinclauses() as being a possible inner path for the given
|
||||
* outer relation(s) in a nestloop join.
|
||||
*
|
||||
* We compare indexpaths on total_cost only, assuming that they will all have
|
||||
* zero or negligible startup_cost. We might have to think harder someday...
|
||||
*
|
||||
* 'join_paths' is a list of potential inner indexscan join paths
|
||||
* 'outer_relids' is the relid list of the outer join relation
|
||||
*
|
||||
* Returns the pathnode of the best path, or NULL if there's no
|
||||
* usable path.
|
||||
*/
|
||||
static Path *
|
||||
best_innerjoin(List *join_paths, Relids outer_relids, JoinType jointype)
|
||||
{
|
||||
Path *cheapest = (Path *) NULL;
|
||||
bool isouterjoin;
|
||||
List *join_path;
|
||||
|
||||
/*
|
||||
* Nestloop only supports inner and left joins.
|
||||
*/
|
||||
switch (jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
isouterjoin = false;
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
isouterjoin = true;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
foreach(join_path, join_paths)
|
||||
{
|
||||
IndexPath *path = (IndexPath *) lfirst(join_path);
|
||||
|
||||
Assert(IsA(path, IndexPath));
|
||||
|
||||
/*
|
||||
* If processing an outer join, only use explicit join clauses in
|
||||
* the inner indexscan. For inner joins we need not be so picky.
|
||||
*/
|
||||
if (isouterjoin && !path->alljoinquals)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* path->joinrelids is the set of base rels that must be part of
|
||||
* outer_relids in order to use this inner path, because those
|
||||
* rels are used in the index join quals of this inner path.
|
||||
*/
|
||||
if (is_subseti(path->joinrelids, outer_relids) &&
|
||||
(cheapest == NULL ||
|
||||
compare_path_costs((Path *) path, cheapest, TOTAL_COST) < 0))
|
||||
cheapest = (Path *) path;
|
||||
}
|
||||
return cheapest;
|
||||
}
|
||||
|
||||
/*
|
||||
* select_mergejoin_clauses
|
||||
* Select mergejoin clauses that are usable for a particular join.
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.47 2002/06/20 20:29:30 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.48 2002/11/24 21:52:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -92,9 +92,6 @@ create_or_index_paths(Query *root, RelOptInfo *rel)
|
||||
/* We don't actually care what order the index scans in. */
|
||||
pathnode->indexscandir = NoMovementScanDirection;
|
||||
|
||||
/* This isn't a nestloop innerjoin, so: */
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->alljoinquals = false;
|
||||
pathnode->rows = rel->rows;
|
||||
|
||||
best_or_subclause_indices(root,
|
||||
|
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.11 2002/09/05 00:43:06 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.12 2002/11/24 21:52:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
static void create_tidscan_joinpaths(Query *root, RelOptInfo *rel);
|
||||
static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo);
|
||||
static bool isEvaluable(int varno, Node *node);
|
||||
static Node *TidequalClause(int varno, Expr *node);
|
||||
@@ -236,44 +235,6 @@ TidqualFromRestrictinfo(List *relids, List *restrictinfo)
|
||||
return rlst;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_tidscan_joinpaths
|
||||
* Create innerjoin paths if there are suitable joinclauses.
|
||||
*
|
||||
* XXX does this actually work?
|
||||
*/
|
||||
static void
|
||||
create_tidscan_joinpaths(Query *root, RelOptInfo *rel)
|
||||
{
|
||||
List *rlst = NIL,
|
||||
*lst;
|
||||
|
||||
foreach(lst, rel->joininfo)
|
||||
{
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(lst);
|
||||
List *restinfo,
|
||||
*tideval;
|
||||
|
||||
restinfo = joininfo->jinfo_restrictinfo;
|
||||
tideval = TidqualFromRestrictinfo(rel->relids, restinfo);
|
||||
if (length(tideval) == 1)
|
||||
{
|
||||
TidPath *pathnode = makeNode(TidPath);
|
||||
|
||||
pathnode->path.pathtype = T_TidScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.pathkeys = NIL;
|
||||
pathnode->tideval = tideval;
|
||||
pathnode->unjoined_relids = joininfo->unjoined_relids;
|
||||
|
||||
cost_tidscan(&pathnode->path, root, rel, tideval);
|
||||
|
||||
rlst = lappend(rlst, pathnode);
|
||||
}
|
||||
}
|
||||
rel->innerjoin = nconc(rel->innerjoin, rlst);
|
||||
}
|
||||
|
||||
/*
|
||||
* create_tidscan_paths
|
||||
* Creates paths corresponding to tid direct scans of the given rel.
|
||||
@@ -287,5 +248,4 @@ create_tidscan_paths(Query *root, RelOptInfo *rel)
|
||||
|
||||
if (tideval)
|
||||
add_path(rel, (Path *) create_tidscan_path(root, rel, tideval));
|
||||
create_tidscan_joinpaths(root, rel);
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.76 2002/11/19 23:21:58 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.77 2002/11/24 21:52:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -577,15 +577,12 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
||||
* the relid list. Set additional RestrictInfo fields for
|
||||
* joining.
|
||||
*
|
||||
* We don't bother setting the merge/hashjoin info if we're not going
|
||||
* to need it. We do want to know about mergejoinable ops in any
|
||||
* potential equijoin clause (see later in this routine), and we
|
||||
* ignore enable_mergejoin if isouterjoin is true, because
|
||||
* mergejoin is the only implementation we have for full and right
|
||||
* outer joins.
|
||||
* We don't bother setting the hashjoin info if we're not going
|
||||
* to need it. We do want to know about mergejoinable ops in all
|
||||
* cases, however, because we use mergejoinable ops for other
|
||||
* purposes such as detecting redundant clauses.
|
||||
*/
|
||||
if (enable_mergejoin || isouterjoin || can_be_equijoin)
|
||||
check_mergejoinable(restrictinfo);
|
||||
check_mergejoinable(restrictinfo);
|
||||
if (enable_hashjoin)
|
||||
check_hashjoinable(restrictinfo);
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.79 2002/11/06 00:00:44 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.80 2002/11/24 21:52:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -354,12 +354,9 @@ create_index_path(Query *root,
|
||||
pathnode->indexscandir = indexscandir;
|
||||
|
||||
/*
|
||||
* This routine is only used to generate "standalone" indexpaths, not
|
||||
* nestloop inner indexpaths. So joinrelids is always NIL and the
|
||||
* number of rows is the same as the parent rel's estimate.
|
||||
* The number of rows is the same as the parent rel's estimate, since
|
||||
* this isn't a join inner indexscan.
|
||||
*/
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->alljoinquals = false;
|
||||
pathnode->rows = rel->rows;
|
||||
|
||||
/*
|
||||
|
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.74 2002/09/04 20:31:22 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.75 2002/11/24 21:52:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -172,6 +172,10 @@ find_secondary_indexes(Oid relationObjectId)
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize cached join info to empty */
|
||||
info->outer_relids = NIL;
|
||||
info->inner_paths = NIL;
|
||||
|
||||
index_close(indexRelation);
|
||||
|
||||
indexinfos = lcons(info, indexinfos);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.40 2002/10/12 22:24:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.41 2002/11/24 21:52:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -17,8 +17,8 @@
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/joininfo.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/restrictinfo.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "parser/parsetree.h"
|
||||
|
||||
@@ -152,7 +152,8 @@ make_base_rel(Query *root, int relid)
|
||||
rel->baserestrictcost = 0;
|
||||
rel->outerjoinset = NIL;
|
||||
rel->joininfo = NIL;
|
||||
rel->innerjoin = NIL;
|
||||
rel->index_outer_relids = NIL;
|
||||
rel->index_inner_paths = NIL;
|
||||
|
||||
/* Check type of rtable entry */
|
||||
switch (rte->rtekind)
|
||||
@@ -365,7 +366,8 @@ build_join_rel(Query *root,
|
||||
joinrel->baserestrictcost = 0;
|
||||
joinrel->outerjoinset = NIL;
|
||||
joinrel->joininfo = NIL;
|
||||
joinrel->innerjoin = NIL;
|
||||
joinrel->index_outer_relids = NIL;
|
||||
joinrel->index_inner_paths = NIL;
|
||||
|
||||
/* Is there a join RTE matching this join? */
|
||||
joinrterel = find_other_rel_for_join(root, joinrelids);
|
||||
@@ -529,9 +531,8 @@ build_joinrel_restrictlist(Query *root,
|
||||
RelOptInfo *inner_rel,
|
||||
JoinType jointype)
|
||||
{
|
||||
List *result = NIL;
|
||||
List *result;
|
||||
List *rlist;
|
||||
List *item;
|
||||
|
||||
/*
|
||||
* Collect all the clauses that syntactically belong at this level.
|
||||
@@ -549,59 +550,8 @@ build_joinrel_restrictlist(Query *root,
|
||||
* mergejoinable clause, it's possible that it is redundant with
|
||||
* previous clauses (see optimizer/README for discussion). We detect
|
||||
* that case and omit the redundant clause from the result list.
|
||||
*
|
||||
* We can detect redundant mergejoinable clauses very cheaply by using
|
||||
* their left and right pathkeys, which uniquely identify the sets of
|
||||
* equijoined variables in question. All the members of a pathkey set
|
||||
* that are in the left relation have already been forced to be equal;
|
||||
* likewise for those in the right relation. So, we need to have only
|
||||
* one clause that checks equality between any set member on the left
|
||||
* and any member on the right; by transitivity, all the rest are then
|
||||
* equal.
|
||||
*
|
||||
* Weird special case: if we have two clauses that seem redundant
|
||||
* except one is pushed down into an outer join and the other isn't,
|
||||
* then they're not really redundant, because one constrains the
|
||||
* joined rows after addition of null fill rows, and the other doesn't.
|
||||
*/
|
||||
foreach(item, rlist)
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
|
||||
|
||||
/* eliminate duplicates */
|
||||
if (member(rinfo, result))
|
||||
continue;
|
||||
|
||||
/* check for redundant merge clauses */
|
||||
if (rinfo->mergejoinoperator != InvalidOid)
|
||||
{
|
||||
bool redundant = false;
|
||||
List *olditem;
|
||||
|
||||
cache_mergeclause_pathkeys(root, rinfo);
|
||||
|
||||
foreach(olditem, result)
|
||||
{
|
||||
RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
|
||||
|
||||
if (oldrinfo->mergejoinoperator != InvalidOid &&
|
||||
rinfo->left_pathkey == oldrinfo->left_pathkey &&
|
||||
rinfo->right_pathkey == oldrinfo->right_pathkey &&
|
||||
(rinfo->ispusheddown == oldrinfo->ispusheddown ||
|
||||
!IS_OUTER_JOIN(jointype)))
|
||||
{
|
||||
redundant = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (redundant)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* otherwise, add it to result list */
|
||||
result = lappend(result, rinfo);
|
||||
}
|
||||
result = remove_redundant_join_clauses(root, rlist, jointype);
|
||||
|
||||
freeList(rlist);
|
||||
|
||||
|
@@ -8,21 +8,21 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.14 2002/06/20 20:29:31 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.15 2002/11/24 21:52:14 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/restrictinfo.h"
|
||||
|
||||
|
||||
/*
|
||||
* restriction_is_or_clause
|
||||
*
|
||||
* Returns t iff the restrictinfo node contains an 'or' clause.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
restriction_is_or_clause(RestrictInfo *restrictinfo)
|
||||
@@ -37,8 +37,7 @@ restriction_is_or_clause(RestrictInfo *restrictinfo)
|
||||
/*
|
||||
* get_actual_clauses
|
||||
*
|
||||
* Returns a list containing the clauses from 'restrictinfo_list'.
|
||||
*
|
||||
* Returns a list containing the bare clauses from 'restrictinfo_list'.
|
||||
*/
|
||||
List *
|
||||
get_actual_clauses(List *restrictinfo_list)
|
||||
@@ -80,3 +79,81 @@ get_actual_join_clauses(List *restrictinfo_list,
|
||||
*joinquals = lappend(*joinquals, clause->clause);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* remove_redundant_join_clauses
|
||||
*
|
||||
* Given a list of RestrictInfo clauses that are to be applied in a join,
|
||||
* remove any duplicate or redundant clauses.
|
||||
*
|
||||
* We must eliminate duplicates when forming the restrictlist for a joinrel,
|
||||
* since we will see many of the same clauses arriving from both input
|
||||
* relations. Also, if a clause is a mergejoinable clause, it's possible that
|
||||
* it is redundant with previous clauses (see optimizer/README for
|
||||
* discussion). We detect that case and omit the redundant clause from the
|
||||
* result list.
|
||||
*
|
||||
* We can detect redundant mergejoinable clauses very cheaply by using their
|
||||
* left and right pathkeys, which uniquely identify the sets of equijoined
|
||||
* variables in question. All the members of a pathkey set that are in the
|
||||
* left relation have already been forced to be equal; likewise for those in
|
||||
* the right relation. So, we need to have only one clause that checks
|
||||
* equality between any set member on the left and any member on the right;
|
||||
* by transitivity, all the rest are then equal.
|
||||
*
|
||||
* Weird special case: if we have two clauses that seem redundant
|
||||
* except one is pushed down into an outer join and the other isn't,
|
||||
* then they're not really redundant, because one constrains the
|
||||
* joined rows after addition of null fill rows, and the other doesn't.
|
||||
*
|
||||
* The result is a fresh List, but it points to the same member nodes
|
||||
* as were in the input.
|
||||
*/
|
||||
List *
|
||||
remove_redundant_join_clauses(Query *root, List *restrictinfo_list,
|
||||
JoinType jointype)
|
||||
{
|
||||
List *result = NIL;
|
||||
List *item;
|
||||
|
||||
foreach(item, restrictinfo_list)
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
|
||||
|
||||
/* eliminate duplicates */
|
||||
if (member(rinfo, result))
|
||||
continue;
|
||||
|
||||
/* check for redundant merge clauses */
|
||||
if (rinfo->mergejoinoperator != InvalidOid)
|
||||
{
|
||||
bool redundant = false;
|
||||
List *olditem;
|
||||
|
||||
cache_mergeclause_pathkeys(root, rinfo);
|
||||
|
||||
foreach(olditem, result)
|
||||
{
|
||||
RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
|
||||
|
||||
if (oldrinfo->mergejoinoperator != InvalidOid &&
|
||||
rinfo->left_pathkey == oldrinfo->left_pathkey &&
|
||||
rinfo->right_pathkey == oldrinfo->right_pathkey &&
|
||||
(rinfo->ispusheddown == oldrinfo->ispusheddown ||
|
||||
!IS_OUTER_JOIN(jointype)))
|
||||
{
|
||||
redundant = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (redundant)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* otherwise, add it to result list */
|
||||
result = lappend(result, rinfo);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
Reference in New Issue
Block a user