Commit cb02610e authored by Tom Lane's avatar Tom Lane

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.
parent 3fb6f134
......@@ -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
*
......
......@@ -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);
}
......
......@@ -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,
......
......@@ -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;
......
......@@ -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
......
......@@ -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;
/*
......
......@@ -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;
}
......@@ -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);
......
......@@ -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;
......
......@@ -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 */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment