1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-11 10:01:57 +03:00

Planner speedup hacking. Avoid saving useless pathkeys, so that path

comparison does not consider paths different when they differ only in
uninteresting aspects of sort order.  (We had a special case of this
consideration for indexscans already, but generalize it to apply to
ordered join paths too.)  Be stricter about what is a canonical pathkey
to allow faster pathkey comparison.  Cache canonical pathkeys and
dispersion stats for left and right sides of a RestrictInfo's clause,
to avoid repeated computation.  Total speedup will depend on number of
tables in a query, but I see about 4x speedup of planning phase for
a sample seven-table query.
This commit is contained in:
Tom Lane
2000-12-14 22:30:45 +00:00
parent db11f4382a
commit ea166f1146
16 changed files with 622 additions and 365 deletions

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.99 2000/11/25 20:33:51 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.100 2000/12/14 22:30:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -87,11 +87,6 @@ static void indexable_joinclauses(RelOptInfo *rel, IndexOptInfo *index,
List **clausegroups, List **outerrelids);
static List *index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
List *clausegroup_list, List *outerrelids_list);
static bool useful_for_mergejoin(RelOptInfo *rel, IndexOptInfo *index,
List *joininfo_list);
static bool useful_for_ordering(Query *root, RelOptInfo *rel,
IndexOptInfo *index,
ScanDirection scandir);
static bool match_index_to_operand(int indexkey, Var *operand,
RelOptInfo *rel, IndexOptInfo *index);
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
@ -125,31 +120,31 @@ static Const *string_to_const(const char *str, Oid datatype);
* attributes are available and fixed during any one scan of the indexpath.
*
* An IndexPath is generated and submitted to add_path() for each index
* this 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* passed to add_path(), but are
* appended to the "innerjoin" list of the relation for later consideration
* in nested-loop joins.
* this routine deems potentially interesting for the current query.
* An innerjoin path is also generated for each interesting combination of
* outer join relations. The innerjoin paths are *not* passed to add_path(),
* but are appended to the "innerjoin" list of the relation for later
* consideration in nested-loop joins.
*
* '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'
*/
void
create_index_paths(Query *root,
RelOptInfo *rel,
List *indices,
List *restrictinfo_list,
List *joininfo_list)
List *indices)
{
List *restrictinfo_list = rel->baserestrictinfo;
List *joininfo_list = rel->joininfo;
List *ilist;
foreach(ilist, indices)
{
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
List *restrictclauses;
List *index_pathkeys;
List *useful_pathkeys;
bool index_is_ordered;
List *joinclausegroups;
List *joinouterrelids;
@ -179,9 +174,7 @@ create_index_paths(Query *root,
match_index_orclauses(rel, index, restrictinfo_list);
/*
* 2. If the keys of this index match any of the available
* non-'or' restriction clauses, then create a path using those
* clauses as indexquals.
* 2. Match the index against non-'or' restriction clauses.
*/
restrictclauses = group_clauses_by_indexkey(rel,
index,
@ -189,43 +182,50 @@ create_index_paths(Query *root,
index->classlist,
restrictinfo_list);
if (restrictclauses != NIL)
add_path(rel, (Path *) create_index_path(root, rel, index,
restrictclauses,
NoMovementScanDirection));
/*
* 3. Compute pathkeys describing index's ordering, if any,
* then see how many of them are actually useful for this query.
*/
index_pathkeys = build_index_pathkeys(root, rel, index,
ForwardScanDirection);
index_is_ordered = (index_pathkeys != NIL);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
/*
* 3. If this index can be used for a mergejoin, then create 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. Similarly, if the index matches
* the ordering that is needed for the overall query result, make
* an index path for it even if there is no other reason to do so.
* 4. Generate an indexscan path if there are relevant restriction
* clauses OR the index ordering is potentially useful for later
* merging or final output ordering.
*/
if (restrictclauses == NIL)
{
if (useful_for_mergejoin(rel, index, joininfo_list) ||
useful_for_ordering(root, rel, index, ForwardScanDirection))
add_path(rel, (Path *)
create_index_path(root, rel, index,
restrictclauses,
ForwardScanDirection));
}
/*
* Currently, backwards scan is never considered except for the
* case of matching a query result ordering. Possibly should
* consider it in other places?
*/
if (useful_for_ordering(root, rel, index, BackwardScanDirection))
if (restrictclauses != NIL || useful_pathkeys != NIL)
add_path(rel, (Path *)
create_index_path(root, rel, index,
restrictclauses,
BackwardScanDirection));
useful_pathkeys,
index_is_ordered ?
ForwardScanDirection :
NoMovementScanDirection));
/*
* 4. Create an innerjoin index path for each combination of other
* 5. If the index is ordered, a backwards scan might be interesting.
* Currently this is only possible for a DESC query result ordering.
*/
if (index_is_ordered)
{
index_pathkeys = build_index_pathkeys(root, rel, index,
BackwardScanDirection);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
if (useful_pathkeys != NIL)
add_path(rel, (Path *)
create_index_path(root, rel, index,
restrictclauses,
useful_pathkeys,
BackwardScanDirection));
}
/*
* 6. 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 sets of
@ -904,88 +904,6 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
return InvalidOid;
}
/*
* useful_for_mergejoin
* Determine whether the given index can support a mergejoin based
* on any available join clause.
*
* We look to see whether the first indexkey of the index matches the
* left or right sides of any of the mergejoinable clauses and provides
* the ordering needed for that side. If so, the index is useful.
* Matching a second or later indexkey is not useful unless there is
* also a mergeclause for the first indexkey, so we need not consider
* secondary indexkeys at this stage.
*
* 'rel' is the relation for which 'index' is defined
* 'joininfo_list' is the list of JoinInfo nodes for 'rel'
*/
static bool
useful_for_mergejoin(RelOptInfo *rel,
IndexOptInfo *index,
List *joininfo_list)
{
int *indexkeys = index->indexkeys;
Oid *ordering = index->ordering;
List *i;
if (!indexkeys || indexkeys[0] == 0 ||
!ordering || ordering[0] == InvalidOid)
return false; /* unordered index is not useful */
foreach(i, joininfo_list)
{
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
List *j;
foreach(j, joininfo->jinfo_restrictinfo)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
if (restrictinfo->mergejoinoperator)
{
if (restrictinfo->left_sortop == ordering[0] &&
match_index_to_operand(indexkeys[0],
get_leftop(restrictinfo->clause),
rel, index))
return true;
if (restrictinfo->right_sortop == ordering[0] &&
match_index_to_operand(indexkeys[0],
get_rightop(restrictinfo->clause),
rel, index))
return true;
}
}
}
return false;
}
/*
* useful_for_ordering
* Determine whether the given index can produce an ordering matching
* the order that is wanted for the query result.
*
* 'rel' is the relation for which 'index' is defined
* 'scandir' is the contemplated scan direction
*/
static bool
useful_for_ordering(Query *root,
RelOptInfo *rel,
IndexOptInfo *index,
ScanDirection scandir)
{
List *index_pathkeys;
if (root->query_pathkeys == NIL)
return false; /* no special ordering requested */
index_pathkeys = build_index_pathkeys(root, rel, index, scandir);
if (index_pathkeys == NIL)
return false; /* unordered index */
return pathkeys_contained_in(root->query_pathkeys, index_pathkeys);
}
/****************************************************************************
* ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ----
****************************************************************************/