Further cleanups of indexqual processing: simplify control
logic in indxpath.c, avoid generation of redundant indexscan paths for the same relation and index.
This commit is contained in:
parent
037cac7ca6
commit
04578a9180
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.66 1999/07/27 03:51:01 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.67 1999/07/30 04:07:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -67,8 +67,7 @@ static void indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
|
||||
List **clausegroups, List **outerrelids);
|
||||
static List *index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
List *clausegroup_list, List *outerrelids_list);
|
||||
static List *create_index_path_group(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
List *clausegroup_list, bool join);
|
||||
static bool useful_for_mergejoin(RelOptInfo *index, List *clausegroup_list);
|
||||
static bool match_index_to_operand(int indexkey, Expr *operand,
|
||||
RelOptInfo *rel, RelOptInfo *index);
|
||||
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index);
|
||||
@ -84,19 +83,40 @@ static List *prefix_quals(Var *leftop, Oid expr_op,
|
||||
* create_index_paths()
|
||||
* Generate all interesting index paths for the given relation.
|
||||
*
|
||||
* To be considered for an index scan, an index must match one or more
|
||||
* restriction clauses or join clauses from the query's qual condition.
|
||||
* To be considered for an index scan, an index must match one or more
|
||||
* restriction clauses or join clauses from the query's qual condition.
|
||||
*
|
||||
* Note: an index scan might also be used simply to order the result,
|
||||
* either for use in a mergejoin or to satisfy an ORDER BY request.
|
||||
* That possibility is handled elsewhere.
|
||||
* There are two basic kinds of index scans. A "plain" index scan uses
|
||||
* only restriction clauses (possibly none at all) in its indexqual,
|
||||
* so it can be applied in any context. An "innerjoin" index scan uses
|
||||
* join clauses (plus restriction clauses, if available) in its indexqual.
|
||||
* Therefore it can only be used as the inner relation of a nestloop
|
||||
* join against an outer rel that includes all the other rels mentioned
|
||||
* in its join clauses. In that context, values for the other rels'
|
||||
* attributes are available and fixed during any one scan of the indexpath.
|
||||
*
|
||||
* This routine's return value is a list of plain IndexPaths for each
|
||||
* index the routine deems potentially interesting for the current query
|
||||
* (at most one IndexPath per index on the given relation). An innerjoin
|
||||
* path is also generated for each interesting combination of outer join
|
||||
* relations. The innerjoin paths are *not* in the return list, but are
|
||||
* appended to the "innerjoin" list of the relation itself.
|
||||
*
|
||||
* XXX An index scan might also be used simply to order the result. We
|
||||
* probably should create an index path for any index that matches the
|
||||
* query's ORDER BY condition, even if it doesn't seem useful for join
|
||||
* or restriction clauses. But currently, such a path would never
|
||||
* survive the path selection process, so there's no point. The selection
|
||||
* process needs to award bonus scores to indexscans that produce a
|
||||
* suitably-ordered result...
|
||||
*
|
||||
* 'rel' is the relation for which we want to generate index paths
|
||||
* 'indices' is a list of available indexes for 'rel'
|
||||
* 'restrictinfo_list' is a list of restrictinfo nodes for 'rel'
|
||||
* 'joininfo_list' is a list of joininfo nodes for 'rel'
|
||||
*
|
||||
* Returns a list of IndexPath access path descriptors.
|
||||
* Returns a list of IndexPath access path descriptors. Additional
|
||||
* IndexPath nodes may also be added to the rel->innerjoin list.
|
||||
*/
|
||||
List *
|
||||
create_index_paths(Query *root,
|
||||
@ -111,7 +131,7 @@ create_index_paths(Query *root,
|
||||
foreach(ilist, indices)
|
||||
{
|
||||
RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
|
||||
List *scanclausegroups;
|
||||
List *restrictclauses;
|
||||
List *joinclausegroups;
|
||||
List *joinouterrelids;
|
||||
|
||||
@ -140,9 +160,8 @@ create_index_paths(Query *root,
|
||||
* of the index), but our poor brains are hurting already...
|
||||
*
|
||||
* We don't even think about special handling of 'or' clauses that
|
||||
* involve more than one relation, since they can't be processed by
|
||||
* a single indexscan path anyway. Currently, cnfify() is certain
|
||||
* to have restructured any such toplevel 'or' clauses anyway.
|
||||
* involve more than one relation (ie, are join clauses).
|
||||
* Can we do anything useful with those?
|
||||
*/
|
||||
match_index_orclauses(rel,
|
||||
index,
|
||||
@ -155,26 +174,33 @@ create_index_paths(Query *root,
|
||||
* restriction clauses, then create a path using those clauses
|
||||
* as indexquals.
|
||||
*/
|
||||
scanclausegroups = group_clauses_by_indexkey(rel,
|
||||
index,
|
||||
index->indexkeys,
|
||||
index->classlist,
|
||||
restrictinfo_list);
|
||||
restrictclauses = group_clauses_by_indexkey(rel,
|
||||
index,
|
||||
index->indexkeys,
|
||||
index->classlist,
|
||||
restrictinfo_list);
|
||||
|
||||
if (scanclausegroups != NIL)
|
||||
retval = nconc(retval,
|
||||
create_index_path_group(root,
|
||||
rel,
|
||||
index,
|
||||
scanclausegroups,
|
||||
false));
|
||||
if (restrictclauses != NIL)
|
||||
retval = lappend(retval,
|
||||
create_index_path(root, rel, index,
|
||||
restrictclauses));
|
||||
|
||||
/*
|
||||
* 3. If this index can be used with any join clause, then create
|
||||
* pathnodes for each group of usable clauses. An index can be
|
||||
* used with a join clause if its ordering is useful for a
|
||||
* mergejoin, or if the index can possibly be used for scanning
|
||||
* the inner relation of a nestloop join.
|
||||
* an index path for it even if there were no restriction clauses.
|
||||
* (If there were, there is no need to make another index path.)
|
||||
* This will allow the index to be considered as a base for a
|
||||
* mergejoin in later processing.
|
||||
* Also, create an innerjoin index path for each combination of
|
||||
* other rels used in available join clauses. These paths will
|
||||
* be considered as the inner side of nestloop joins against
|
||||
* those sets of other rels.
|
||||
* indexable_joinclauses() finds clauses that are potentially
|
||||
* applicable to either case. useful_for_mergejoin() tests to
|
||||
* see whether any of the join clauses might support a mergejoin.
|
||||
* index_innerjoin() builds an innerjoin index path for each
|
||||
* potential set of outer rels, which we add to the rel's
|
||||
* innerjoin list.
|
||||
*/
|
||||
indexable_joinclauses(rel, index,
|
||||
joininfo_list, restrictinfo_list,
|
||||
@ -183,12 +209,13 @@ create_index_paths(Query *root,
|
||||
|
||||
if (joinclausegroups != NIL)
|
||||
{
|
||||
retval = nconc(retval,
|
||||
create_index_path_group(root,
|
||||
rel,
|
||||
index,
|
||||
joinclausegroups,
|
||||
true));
|
||||
/* no need to create a plain path if we already did */
|
||||
if (restrictclauses == NIL &&
|
||||
useful_for_mergejoin(index, joinclausegroups))
|
||||
retval = lappend(retval,
|
||||
create_index_path(root, rel, index,
|
||||
NIL));
|
||||
|
||||
rel->innerjoin = nconc(rel->innerjoin,
|
||||
index_innerjoin(root, rel, index,
|
||||
joinclausegroups,
|
||||
@ -344,21 +371,18 @@ match_index_orclause(RelOptInfo *rel,
|
||||
* 'classes' are the classes of the index operators on those keys.
|
||||
* 'restrictinfo_list' is the list of available restriction clauses for 'rel'.
|
||||
*
|
||||
* Returns NIL if no clauses can be used with this index.
|
||||
* Otherwise, a list containing a single sublist is returned (indicating
|
||||
* to create_index_path_group() that a single IndexPath should be created).
|
||||
* The sublist contains the RestrictInfo nodes for all clauses that can be
|
||||
* Returns a list of all the RestrictInfo nodes for clauses that can be
|
||||
* used with this index.
|
||||
*
|
||||
* The sublist is ordered by index key (but as far as I can tell, this is
|
||||
* The list is ordered by index key (but as far as I can tell, this is
|
||||
* an implementation artifact of this routine, and is not depended on by
|
||||
* any user of the returned list --- tgl 7/99).
|
||||
*
|
||||
* Note that in a multi-key index, we stop if we find a key that cannot be
|
||||
* used with any clause. For example, given an index on (A,B,C), we might
|
||||
* return ((C1 C2 C3 C4)) if we find that clauses C1 and C2 use column A,
|
||||
* clauses C3 and C4 use column B, and no clauses use column C. But if no
|
||||
* clauses match B we will return ((C1 C2)), whether or not there are
|
||||
* return (C1 C2 C3 C4) if we find that clauses C1 and C2 use column A,
|
||||
* clauses C3 and C4 use column B, and no clauses use column C. But if
|
||||
* no clauses match B we will return (C1 C2), whether or not there are
|
||||
* clauses matching column C, because the executor couldn't use them anyway.
|
||||
*/
|
||||
static List *
|
||||
@ -407,9 +431,7 @@ group_clauses_by_indexkey(RelOptInfo *rel,
|
||||
} while (!DoneMatchingIndexKeys(indexkeys, index));
|
||||
|
||||
/* clausegroup_list holds all matched clauses ordered by indexkeys */
|
||||
if (clausegroup_list != NIL)
|
||||
return lcons(clausegroup_list, NIL);
|
||||
return NIL;
|
||||
return clausegroup_list;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -418,9 +440,9 @@ group_clauses_by_indexkey(RelOptInfo *rel,
|
||||
*
|
||||
* This is much like group_clauses_by_indexkey(), but we consider both
|
||||
* join and restriction clauses. For each indexkey in the index, we
|
||||
* accept both join and restriction clauses that match it (since both
|
||||
* accept both join and restriction clauses that match it, since both
|
||||
* will make useful indexquals if the index is being used to scan the
|
||||
* inner side of a join). But there must be at least one matching
|
||||
* inner side of a nestloop join. But there must be at least one matching
|
||||
* join clause, or we return NIL indicating that this index isn't useful
|
||||
* for joining.
|
||||
*/
|
||||
@ -486,22 +508,18 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
||||
|
||||
} while (!DoneMatchingIndexKeys(indexkeys, index));
|
||||
|
||||
/* clausegroup_list holds all matched clauses ordered by indexkeys */
|
||||
|
||||
if (clausegroup_list != NIL)
|
||||
/*
|
||||
* if no join clause was matched then there ain't clauses for
|
||||
* joins at all.
|
||||
*/
|
||||
if (!jfound)
|
||||
{
|
||||
/*
|
||||
* if no join clause was matched then there ain't clauses for
|
||||
* joins at all.
|
||||
*/
|
||||
if (!jfound)
|
||||
{
|
||||
freeList(clausegroup_list);
|
||||
return NIL;
|
||||
}
|
||||
return lcons(clausegroup_list, NIL);
|
||||
freeList(clausegroup_list);
|
||||
return NIL;
|
||||
}
|
||||
return NIL;
|
||||
|
||||
/* clausegroup_list holds all matched clauses ordered by indexkeys */
|
||||
return clausegroup_list;
|
||||
}
|
||||
|
||||
|
||||
@ -1150,41 +1168,22 @@ indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
|
||||
foreach(i, joininfo_list)
|
||||
{
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
||||
List *clausegroups;
|
||||
List *clausegroup;
|
||||
|
||||
if (joininfo->jinfo_restrictinfo == NIL)
|
||||
continue;
|
||||
clausegroups = group_clauses_by_ikey_for_joins(rel,
|
||||
index,
|
||||
index->indexkeys,
|
||||
index->classlist,
|
||||
clausegroup = group_clauses_by_ikey_for_joins(rel,
|
||||
index,
|
||||
index->indexkeys,
|
||||
index->classlist,
|
||||
joininfo->jinfo_restrictinfo,
|
||||
restrictinfo_list);
|
||||
restrictinfo_list);
|
||||
|
||||
/*----------
|
||||
* This code knows that group_clauses_by_ikey_for_joins() returns
|
||||
* either NIL or a list containing a single sublist of clauses.
|
||||
* The line
|
||||
* cg_list = nconc(cg_list, clausegroups);
|
||||
* is better read as
|
||||
* cg_list = lappend(cg_list, lfirst(clausegroups));
|
||||
* That is, we are appending the only sublist returned by
|
||||
* group_clauses_by_ikey_for_joins() to the list of clause sublists
|
||||
* that this routine will return. By using nconc() we recycle
|
||||
* a cons cell that would be wasted ... whoever wrote this code
|
||||
* was too clever by half...
|
||||
*----------
|
||||
*/
|
||||
if (clausegroups != NIL)
|
||||
if (clausegroup != NIL)
|
||||
{
|
||||
cg_list = nconc(cg_list, clausegroups);
|
||||
cg_list = lappend(cg_list, clausegroup);
|
||||
relid_list = lappend(relid_list, joininfo->unjoined_relids);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure above clever code didn't screw up */
|
||||
Assert(length(cg_list) == length(relid_list));
|
||||
|
||||
*clausegroups = cg_list;
|
||||
*outerrelids = relid_list;
|
||||
}
|
||||
@ -1200,7 +1199,7 @@ indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
|
||||
*
|
||||
* 'rel' is the relation for which 'index' is defined
|
||||
* 'clausegroup_list' is a list of lists of restrictinfo nodes which can use
|
||||
* 'index' on their inner relation.
|
||||
* 'index'. Each sublist refers to the same set of outer rels.
|
||||
* 'outerrelids_list' is a list of the required outer rels for each group
|
||||
* of join clauses.
|
||||
*
|
||||
@ -1245,10 +1244,12 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
* therefore, both indexid and indexqual should be single-element
|
||||
* lists.
|
||||
*/
|
||||
Assert(length(index->relids) == 1);
|
||||
pathnode->indexid = index->relids;
|
||||
pathnode->indexkeys = index->indexkeys;
|
||||
pathnode->indexqual = lcons(indexquals, NIL);
|
||||
|
||||
pathnode->indexkeys = index->indexkeys;
|
||||
|
||||
/* joinid saves the rels needed on the outer side of the join */
|
||||
pathnode->path.joinid = lfirst(outerrelids_list);
|
||||
|
||||
@ -1268,59 +1269,38 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
}
|
||||
|
||||
/*
|
||||
* create_index_path_group
|
||||
* Creates a list of index path nodes for each group of clauses
|
||||
* (restriction or join) that can be used in conjunction with an index.
|
||||
*
|
||||
* 'rel' is the relation for which 'index' is defined
|
||||
* 'clausegroup_list' is the list of clause groups (lists of restrictinfo
|
||||
* nodes) grouped by mergejoinorder
|
||||
* 'join' is a flag indicating whether or not the clauses are join
|
||||
* clauses
|
||||
*
|
||||
* Returns a list of new index path nodes.
|
||||
* useful_for_mergejoin
|
||||
* Determine whether the given index can support a mergejoin based
|
||||
* on any join clause within the given list. The clauses have already
|
||||
* been found to be relevant to the index by indexable_joinclauses.
|
||||
* We just need to check whether any are mergejoin material.
|
||||
*
|
||||
* 'index' is the index of interest.
|
||||
* 'clausegroup_list' is a list of clause groups (sublists of restrictinfo
|
||||
* nodes)
|
||||
*/
|
||||
static List *
|
||||
create_index_path_group(Query *root,
|
||||
RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
List *clausegroup_list,
|
||||
bool join)
|
||||
static bool
|
||||
useful_for_mergejoin(RelOptInfo *index,
|
||||
List *clausegroup_list)
|
||||
{
|
||||
List *path_list = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, clausegroup_list)
|
||||
{
|
||||
List *clausegroup = lfirst(i);
|
||||
bool usable = true;
|
||||
List *j;
|
||||
|
||||
if (join)
|
||||
foreach(j, clausegroup)
|
||||
{
|
||||
List *j;
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
|
||||
|
||||
foreach(j, clausegroup)
|
||||
{
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
|
||||
if (!(is_joinable((Node *) restrictinfo->clause) &&
|
||||
equal_path_merge_ordering(index->ordering,
|
||||
restrictinfo->mergejoinorder)))
|
||||
{
|
||||
usable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usable)
|
||||
{
|
||||
path_list = lappend(path_list,
|
||||
create_index_path(root, rel, index,
|
||||
clausegroup, join));
|
||||
if (is_joinable((Node *) restrictinfo->clause) &&
|
||||
equal_path_merge_ordering(index->ordering,
|
||||
restrictinfo->mergejoinorder))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return path_list;
|
||||
return false;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.50 1999/07/30 00:56:17 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.51 1999/07/30 04:07:25 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -300,23 +300,20 @@ create_seqscan_path(RelOptInfo *rel)
|
||||
|
||||
/*
|
||||
* create_index_path
|
||||
* Creates a single path node for an index scan.
|
||||
* Creates a path node for an index scan.
|
||||
*
|
||||
* 'rel' is the parent rel
|
||||
* 'index' is the pathnode for the index on 'rel'
|
||||
* 'restriction_clauses' is a list of restriction clause nodes.
|
||||
* 'is_join_scan' is a flag indicating whether or not the index is being
|
||||
* considered because of its sort order.
|
||||
* 'index' is an index on 'rel'
|
||||
* 'restriction_clauses' is a list of RestrictInfo nodes
|
||||
* to be used as index qual conditions in the scan.
|
||||
*
|
||||
* Returns the new path node.
|
||||
*
|
||||
*/
|
||||
IndexPath *
|
||||
create_index_path(Query *root,
|
||||
RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
List *restriction_clauses,
|
||||
bool is_join_scan)
|
||||
List *restriction_clauses)
|
||||
{
|
||||
IndexPath *pathnode = makeNode(IndexPath);
|
||||
|
||||
@ -361,20 +358,11 @@ create_index_path(Query *root,
|
||||
else
|
||||
pathnode->path.pathkeys = NULL;
|
||||
|
||||
if (is_join_scan || restriction_clauses == NULL)
|
||||
if (restriction_clauses == NIL)
|
||||
{
|
||||
/*
|
||||
* Indices used for joins or sorting result nodes don't restrict
|
||||
* the result at all, they simply order it, so compute the scan
|
||||
* cost accordingly -- use a selectivity of 1.0.
|
||||
*
|
||||
* is the statement above really true? what about IndexScan as the
|
||||
* inner of a join?
|
||||
*
|
||||
* I think it's OK --- this routine is only used to make index paths
|
||||
* for mergejoins and sorts. Index paths used as the inner side of
|
||||
* a nestloop join do provide restriction, but they are not made
|
||||
* with this code. See index_innerjoin() in indxpath.c.
|
||||
* We have no restriction clauses, so compute scan cost using
|
||||
* selectivity of 1.0.
|
||||
*/
|
||||
pathnode->path.path_cost = cost_index(lfirsti(index->relids),
|
||||
index->pages,
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pathnode.h,v 1.18 1999/07/15 15:21:22 momjian Exp $
|
||||
* $Id: pathnode.h,v 1.19 1999/07/30 04:07:22 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -24,7 +24,7 @@ extern List *add_pathlist(RelOptInfo *parent_rel, List *unique_paths,
|
||||
List *new_paths);
|
||||
extern Path *create_seqscan_path(RelOptInfo *rel);
|
||||
extern IndexPath *create_index_path(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
List *restriction_clauses, bool is_join_scan);
|
||||
List *restriction_clauses);
|
||||
extern NestPath *create_nestloop_path(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
||||
Path *outer_path, Path *inner_path, List *pathkeys);
|
||||
extern MergePath *create_mergejoin_path(RelOptInfo *joinrel, int outersize,
|
||||
|
Loading…
x
Reference in New Issue
Block a user