Adjust nestloop-with-inner-indexscan plan generation so that we catch

some cases of redundant clauses that were formerly not caught.  We have
to special-case this because the clauses involved never get attached to
the same join restrictlist and so the existing logic does not notice
that they are redundant.
This commit is contained in:
Tom Lane 2003-06-15 22:51:45 +00:00
parent 3fb6f1347f
commit cb02610e50
10 changed files with 284 additions and 137 deletions

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.49 2003/05/28 22:32:49 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.50 2003/06/15 22:51:45 tgl Exp $
*
* NOTES
* XXX a few of the following functions are duplicated to handle
@ -358,6 +358,21 @@ llast(List *l)
return lfirst(l);
}
/*
* llastnode
*
* Get the last node of l ... NIL if empty list
*/
List *
llastnode(List *l)
{
if (l == NIL)
return NIL;
while (lnext(l) != NIL)
l = lnext(l);
return l;
}
/*
* freeList
*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.207 2003/06/06 15:04:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.208 2003/06/15 22:51:45 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@ -933,6 +933,7 @@ _outIndexPath(StringInfo str, IndexPath *node)
WRITE_NODE_FIELD(indexinfo);
WRITE_NODE_FIELD(indexqual);
WRITE_NODE_FIELD(indexjoinclauses);
WRITE_ENUM_FIELD(indexscandir, ScanDirection);
WRITE_FLOAT_FIELD(rows, "%.2f");
}
@ -1034,6 +1035,7 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
{
WRITE_NODE_TYPE("RESTRICTINFO");
/* NB: this isn't a complete set of fields */
WRITE_NODE_FIELD(clause);
WRITE_BOOL_FIELD(ispusheddown);
WRITE_NODE_FIELD(subclauseindices);
@ -1042,6 +1044,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
WRITE_OID_FIELD(mergejoinoperator);
WRITE_OID_FIELD(left_sortop);
WRITE_OID_FIELD(right_sortop);
WRITE_NODE_FIELD(left_pathkey);
WRITE_NODE_FIELD(right_pathkey);
WRITE_OID_FIELD(hashjoinoperator);
}

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.143 2003/05/28 22:32:49 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.144 2003/06/15 22:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -59,10 +59,10 @@ static bool match_or_subclause_to_indexkey(RelOptInfo *rel,
IndexOptInfo *index,
Expr *clause);
static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index);
static List *group_clauses_by_indexkey_for_join(RelOptInfo *rel,
IndexOptInfo *index,
Relids outer_relids,
bool isouterjoin);
static List *group_clauses_by_indexkey_for_join(Query *root,
RelOptInfo *rel, IndexOptInfo *index,
Relids outer_relids,
JoinType jointype, bool isouterjoin);
static bool match_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
int indexcol, Oid opclass, Expr *clause);
static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
@ -583,8 +583,10 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
* will already have been generated for it.)
*/
static List *
group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
Relids outer_relids, bool isouterjoin)
group_clauses_by_indexkey_for_join(Query *root,
RelOptInfo *rel, IndexOptInfo *index,
Relids outer_relids,
JoinType jointype, bool isouterjoin)
{
FastList clausegroup_list;
bool jfound = false;
@ -629,6 +631,24 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
}
}
/*
* If we found join clauses in more than one joininfo list, we may
* now have clauses that are known redundant. Get rid of 'em.
* (There is no point in looking at restriction clauses, because
* remove_redundant_join_clauses will never think they are
* redundant, so we do this before adding restriction clauses to
* the clause group.)
*/
if (FastListValue(&clausegroup) != NIL)
{
List *nl;
nl = remove_redundant_join_clauses(root,
FastListValue(&clausegroup),
jointype);
FastListFromList(&clausegroup, nl);
}
/* We can also use plain restriction clauses for the rel */
foreach(i, rel->baserestrictinfo)
{
@ -1461,9 +1481,11 @@ best_inner_indexscan(Query *root, RelOptInfo *rel,
List *clausegroups;
/* find useful clauses for this index and outerjoin set */
clausegroups = group_clauses_by_indexkey_for_join(rel,
clausegroups = group_clauses_by_indexkey_for_join(root,
rel,
index,
index_outer_relids,
jointype,
isouterjoin);
if (clausegroups)
{
@ -1520,7 +1542,7 @@ make_innerjoin_index_path(Query *root,
*allclauses,
*l;
/* XXX this code ought to be merged with create_index_path? */
/* XXX perhaps this code should be merged with create_index_path? */
pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel;
@ -1535,12 +1557,25 @@ make_innerjoin_index_path(Query *root,
/* Convert RestrictInfo nodes to indexquals the executor can handle */
indexquals = expand_indexqual_conditions(index, clausegroups);
/*
* Also make a flattened list of the RestrictInfo nodes; createplan.c
* will need this later. We assume here that we can destructively
* modify the passed-in clausegroups list structure.
*/
allclauses = NIL;
foreach(l, clausegroups)
{
/* nconc okay here since same clause couldn't be in two sublists */
allclauses = nconc(allclauses, (List *) lfirst(l));
}
/*
* Note that we are making a pathnode for a single-scan indexscan;
* therefore, both indexinfo and indexqual should be single-element lists.
* therefore, indexinfo and indexqual should be single-element lists.
*/
pathnode->indexinfo = makeList1(index);
pathnode->indexqual = makeList1(indexquals);
pathnode->indexjoinclauses = makeList1(allclauses);
/* We don't actually care what order the index scans in ... */
pathnode->indexscandir = NoMovementScanDirection;
@ -1558,17 +1593,9 @@ make_innerjoin_index_path(Query *root,
* linking them into different lists, it should be sufficient to use
* pointer comparison to remove duplicates.)
*
* We assume we can destructively modify the input sublists.
*
* Always assume the join type is JOIN_INNER; even if some of the
* join clauses come from other contexts, that's not our problem.
*/
allclauses = NIL;
foreach(l, clausegroups)
{
/* nconc okay here since same clause couldn't be in two sublists */
allclauses = nconc(allclauses, (List *) lfirst(l));
}
allclauses = set_ptrUnion(rel->baserestrictinfo, allclauses);
pathnode->rows = rel->tuples *
restrictlist_selectivity(root,

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.50 2003/05/28 22:32:49 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.51 2003/06/15 22:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -89,6 +89,9 @@ create_or_index_paths(Query *root, RelOptInfo *rel)
*/
pathnode->path.pathkeys = NIL;
/* It's not an innerjoin path. */
pathnode->indexjoinclauses = NIL;
/* We don't actually care what order the index scans in. */
pathnode->indexscandir = NoMovementScanDirection;

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.144 2003/05/28 23:06:16 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.145 2003/06/15 22:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -51,17 +51,11 @@ static SubqueryScan *create_subqueryscan_plan(Path *best_path,
List *tlist, List *scan_clauses);
static FunctionScan *create_functionscan_plan(Path *best_path,
List *tlist, List *scan_clauses);
static NestLoop *create_nestloop_plan(Query *root,
NestPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
static NestLoop *create_nestloop_plan(Query *root, NestPath *best_path,
Plan *outer_plan, Plan *inner_plan);
static MergeJoin *create_mergejoin_plan(Query *root,
MergePath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
static MergeJoin *create_mergejoin_plan(Query *root, MergePath *best_path,
Plan *outer_plan, Plan *inner_plan);
static HashJoin *create_hashjoin_plan(Query *root,
HashPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
static HashJoin *create_hashjoin_plan(Query *root, HashPath *best_path,
Plan *outer_plan, Plan *inner_plan);
static void fix_indxqual_references(List *indexquals, IndexPath *index_path,
List **fixed_indexquals,
@ -356,59 +350,35 @@ disuse_physical_tlist(Plan *plan, Path *path)
static Join *
create_join_plan(Query *root, JoinPath *best_path)
{
List *join_tlist = best_path->path.parent->targetlist;
Plan *outer_plan;
Plan *inner_plan;
List *joinclauses;
List *otherclauses;
Join *plan;
outer_plan = create_plan(root, best_path->outerjoinpath);
inner_plan = create_plan(root, best_path->innerjoinpath);
if (IS_OUTER_JOIN(best_path->jointype))
{
get_actual_join_clauses(best_path->joinrestrictinfo,
&joinclauses, &otherclauses);
}
else
{
/* We can treat all clauses alike for an inner join */
joinclauses = get_actual_clauses(best_path->joinrestrictinfo);
otherclauses = NIL;
}
switch (best_path->path.pathtype)
{
case T_MergeJoin:
plan = (Join *) create_mergejoin_plan(root,
(MergePath *) best_path,
join_tlist,
joinclauses,
otherclauses,
outer_plan,
inner_plan);
break;
case T_HashJoin:
plan = (Join *) create_hashjoin_plan(root,
(HashPath *) best_path,
join_tlist,
joinclauses,
otherclauses,
outer_plan,
inner_plan);
break;
case T_NestLoop:
plan = (Join *) create_nestloop_plan(root,
(NestPath *) best_path,
join_tlist,
joinclauses,
otherclauses,
outer_plan,
inner_plan);
break;
default:
elog(ERROR, "create_join_plan: unknown node type: %d",
elog(ERROR, "unsupported node type %d",
best_path->path.pathtype);
plan = NULL; /* keep compiler quiet */
break;
@ -869,15 +839,16 @@ create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses)
static NestLoop *
create_nestloop_plan(Query *root,
NestPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
Plan *outer_plan,
Plan *inner_plan)
{
List *tlist = best_path->path.parent->targetlist;
List *joinrestrictclauses = best_path->joinrestrictinfo;
List *joinclauses;
List *otherclauses;
NestLoop *join_plan;
if (IsA(inner_plan, IndexScan))
if (IsA(best_path->innerjoinpath, IndexPath))
{
/*
* An index is being used to reduce the number of tuples scanned
@ -888,22 +859,41 @@ create_nestloop_plan(Query *root,
* (otherwise, several different sets of clauses are being ORed
* together).
*
* Note we must compare against indxqualorig not the "fixed" indxqual
* (which has index attnos instead of relation attnos, and may have
* been commuted as well).
* We can also remove any join clauses that are redundant with those
* being used in the index scan; prior redundancy checks will not
* have caught this case because the join clauses would never have
* been put in the same joininfo list.
*
* This would be a waste of time if the indexpath was an ordinary
* indexpath and not a special innerjoin path. We will skip it in
* that case since indexjoinclauses is NIL in an ordinary indexpath.
*/
IndexScan *innerscan = (IndexScan *) inner_plan;
List *indxqualorig = innerscan->indxqualorig;
IndexPath *innerpath = (IndexPath *) best_path->innerjoinpath;
List *indexjoinclauses = innerpath->indexjoinclauses;
if (length(indxqualorig) == 1) /* single indexscan? */
if (length(indexjoinclauses) == 1) /* single indexscan? */
{
/* No work needed if indxqual refers only to its own relation... */
if (NumRelids((Node *) indxqualorig) > 1)
joinclauses = set_difference(joinclauses,
lfirst(indxqualorig));
joinrestrictclauses =
select_nonredundant_join_clauses(root,
joinrestrictclauses,
lfirst(indexjoinclauses),
best_path->jointype);
}
}
/* Get the join qual clauses (in plain expression form) */
if (IS_OUTER_JOIN(best_path->jointype))
{
get_actual_join_clauses(joinrestrictclauses,
&joinclauses, &otherclauses);
}
else
{
/* We can treat all clauses alike for an inner join */
joinclauses = get_actual_clauses(joinrestrictclauses);
otherclauses = NIL;
}
join_plan = make_nestloop(tlist,
joinclauses,
otherclauses,
@ -919,15 +909,28 @@ create_nestloop_plan(Query *root,
static MergeJoin *
create_mergejoin_plan(Query *root,
MergePath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
Plan *outer_plan,
Plan *inner_plan)
{
List *tlist = best_path->jpath.path.parent->targetlist;
List *joinclauses;
List *otherclauses;
List *mergeclauses;
MergeJoin *join_plan;
/* Get the join qual clauses (in plain expression form) */
if (IS_OUTER_JOIN(best_path->jpath.jointype))
{
get_actual_join_clauses(best_path->jpath.joinrestrictinfo,
&joinclauses, &otherclauses);
}
else
{
/* We can treat all clauses alike for an inner join */
joinclauses = get_actual_clauses(best_path->jpath.joinrestrictinfo);
otherclauses = NIL;
}
/*
* Remove the mergeclauses from the list of join qual clauses, leaving
* the list of quals that must be checked as qpquals.
@ -986,18 +989,31 @@ create_mergejoin_plan(Query *root,
static HashJoin *
create_hashjoin_plan(Query *root,
HashPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
Plan *outer_plan,
Plan *inner_plan)
{
List *tlist = best_path->jpath.path.parent->targetlist;
List *joinclauses;
List *otherclauses;
List *hashclauses;
HashJoin *join_plan;
Hash *hash_plan;
List *innerhashkeys;
List *hcl;
/* Get the join qual clauses (in plain expression form) */
if (IS_OUTER_JOIN(best_path->jpath.jointype))
{
get_actual_join_clauses(best_path->jpath.joinrestrictinfo,
&joinclauses, &otherclauses);
}
else
{
/* We can treat all clauses alike for an inner join */
joinclauses = get_actual_clauses(best_path->jpath.joinrestrictinfo);
otherclauses = NIL;
}
/*
* Remove the hashclauses from the list of join qual clauses, leaving
* the list of quals that must be checked as qpquals.
@ -1056,13 +1072,9 @@ create_hashjoin_plan(Query *root,
* Adjust indexqual clauses to the form the executor's indexqual
* machinery needs, and check for recheckable (lossy) index conditions.
*
* We have four tasks here:
* We have three tasks here:
* * Index keys must be represented by Var nodes with varattno set to the
* index's attribute number, not the attribute number in the original rel.
* * indxpath.c may have selected an index that is binary-compatible with
* the actual expression operator, but not exactly the same datatype.
* We must replace the expression's operator with the binary-compatible
* equivalent operator that the index will recognize.
* * If the index key is on the right, commute the clause to put it on the
* left. (Someday the executor might not need this, but for now it does.)
* * If the indexable operator is marked 'amopreqcheck' in pg_amop, then

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.89 2003/05/26 00:11:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.90 2003/06/15 22:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -362,6 +362,9 @@ create_index_path(Query *root,
pathnode->indexinfo = makeList1(index);
pathnode->indexqual = makeList1(indexquals);
/* It's not an innerjoin path. */
pathnode->indexjoinclauses = NIL;
pathnode->indexscandir = indexscandir;
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.16 2003/01/24 03:58:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.17 2003/06/15 22:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,6 +20,12 @@
#include "optimizer/var.h"
static bool join_clause_is_redundant(Query *root,
RestrictInfo *rinfo,
List *reference_list,
JoinType jointype);
/*
* restriction_is_or_clause
*
@ -94,6 +100,76 @@ get_actual_join_clauses(List *restrictinfo_list,
* discussion). We detect that case and omit the redundant clause from the
* result list.
*
* 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);
/* drop it if redundant with any prior clause */
if (join_clause_is_redundant(root, rinfo, result, jointype))
continue;
/* otherwise, add it to result list */
result = lappend(result, rinfo);
}
return result;
}
/*
* select_nonredundant_join_clauses
*
* Given a list of RestrictInfo clauses that are to be applied in a join,
* select the ones that are not redundant with any clause in the
* reference_list.
*
* This is similar to remove_redundant_join_clauses, but we are looking for
* redundancies with a separate list of clauses (i.e., clauses that have
* already been applied below the join itself).
*
* Note that we assume the given restrictinfo_list has already been checked
* for local redundancies, so we don't check again.
*/
List *
select_nonredundant_join_clauses(Query *root,
List *restrictinfo_list,
List *reference_list,
JoinType jointype)
{
List *result = NIL;
List *item;
foreach(item, restrictinfo_list)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
/* drop it if redundant with any reference clause */
if (join_clause_is_redundant(root, rinfo, reference_list, jointype))
continue;
/* otherwise, add it to result list */
result = lappend(result, rinfo);
}
return result;
}
/*
* join_clause_is_redundant
* Returns true if rinfo is redundant with any clause in reference_list.
*
* This is the guts of both remove_redundant_join_clauses and
* select_nonredundant_join_clauses. See the docs above for motivation.
*
* 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
@ -113,69 +189,59 @@ get_actual_join_clauses(List *restrictinfo_list,
* 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)
static bool
join_clause_is_redundant(Query *root,
RestrictInfo *rinfo,
List *reference_list,
JoinType jointype)
{
List *result = NIL;
List *item;
/* always consider exact duplicates redundant */
/* XXX would it be sufficient to use ptrMember here? */
if (member(rinfo, reference_list))
return true;
foreach(item, restrictinfo_list)
/* check for redundant merge clauses */
if (rinfo->mergejoinoperator != InvalidOid)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
bool redundant = false;
List *refitem;
/* always eliminate duplicates */
if (member(rinfo, result))
continue;
cache_mergeclause_pathkeys(root, rinfo);
/* check for redundant merge clauses */
if (rinfo->mergejoinoperator != InvalidOid)
/* do the cheap tests first */
foreach(refitem, reference_list)
{
bool redundant = false;
List *olditem;
RestrictInfo *refrinfo = (RestrictInfo *) lfirst(refitem);
cache_mergeclause_pathkeys(root, rinfo);
/* do the cheap tests first */
foreach(olditem, result)
if (refrinfo->mergejoinoperator != InvalidOid &&
rinfo->left_pathkey == refrinfo->left_pathkey &&
rinfo->right_pathkey == refrinfo->right_pathkey &&
(rinfo->ispusheddown == refrinfo->ispusheddown ||
!IS_OUTER_JOIN(jointype)))
{
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)
{
/*
* It looks redundant, now check for "var = const" case.
* If left_relids/right_relids are set, then there are
* definitely vars on both sides; else we must check the
* hard way.
*/
if (rinfo->left_relids)
continue; /* var = var, so redundant */
if (contain_var_clause(get_leftop(rinfo->clause)) &&
contain_var_clause(get_rightop(rinfo->clause)))
continue; /* var = var, so redundant */
/* else var = const, not redundant */
redundant = true;
break;
}
}
/* otherwise, add it to result list */
result = lappend(result, rinfo);
if (redundant)
{
/*
* It looks redundant, now check for "var = const" case.
* If left_relids/right_relids are set, then there are
* definitely vars on both sides; else we must check the
* hard way.
*/
if (rinfo->left_relids)
return true; /* var = var, so redundant */
if (contain_var_clause(get_leftop(rinfo->clause)) &&
contain_var_clause(get_rightop(rinfo->clause)))
return true; /* var = var, so redundant */
/* else var = const, not redundant */
}
}
return result;
/* otherwise, not redundant */
return false;
}

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_list.h,v 1.36 2003/05/28 22:32:50 tgl Exp $
* $Id: pg_list.h,v 1.37 2003/06/15 22:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -140,6 +140,8 @@ typedef struct FastList
} FastList;
#define FastListInit(fl) ( (fl)->head = (fl)->tail = NIL )
#define FastListFromList(fl, l) \
( (fl)->head = (l), (fl)->tail = llastnode((fl)->head) )
#define FastListValue(fl) ( (fl)->head )
@ -166,6 +168,7 @@ extern void FastConcFast(FastList *fl, FastList *fl2);
extern void *nth(int n, List *l);
extern int length(List *list);
extern void *llast(List *list);
extern List *llastnode(List *list);
extern bool member(void *datum, List *list);
extern bool ptrMember(void *datum, List *list);
extern bool intMember(int datum, List *list);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: relation.h,v 1.80 2003/05/28 16:04:02 tgl Exp $
* $Id: relation.h,v 1.81 2003/06/15 22:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -340,6 +340,15 @@ typedef struct Path
* Also note that indexquals lists do not contain RestrictInfo nodes,
* just bare clause expressions.
*
* 'indexjoinclauses' is NIL for an ordinary indexpath (one that does not
* use any join clauses in the index conditions). For an innerjoin indexpath,
* it has the same structure as 'indexqual', but references the RestrictInfo
* nodes from which the indexqual was built, rather than the bare clause
* expressions. (Note: there isn't necessarily a one-to-one correspondence
* between RestrictInfos and expressions, because of expansion of special
* indexable operators.) We need this so that we can eliminate redundant
* join clauses when plans are built.
*
* 'indexscandir' is one of:
* ForwardScanDirection: forward scan of an ordered index
* BackwardScanDirection: backward scan of an ordered index
@ -360,6 +369,7 @@ typedef struct IndexPath
Path path;
List *indexinfo;
List *indexqual;
List *indexjoinclauses;
ScanDirection indexscandir;
double rows; /* estimated number of result tuples */
} IndexPath;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: restrictinfo.h,v 1.16 2002/11/24 21:52:15 tgl Exp $
* $Id: restrictinfo.h,v 1.17 2003/06/15 22:51:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,5 +23,9 @@ extern void get_actual_join_clauses(List *restrictinfo_list,
extern List *remove_redundant_join_clauses(Query *root,
List *restrictinfo_list,
JoinType jointype);
extern List *select_nonredundant_join_clauses(Query *root,
List *restrictinfo_list,
List *reference_list,
JoinType jointype);
#endif /* RESTRICTINFO_H */