Further cleanup for OR-of-AND WHERE-clauses. orindxpath can now handle
extracting from an AND subclause just those opclauses that are relevant for a particular index. For example, we can now consider using an index on x to process WHERE (x = 1 AND y = 2) OR (x = 2 AND y = 4) OR ...
This commit is contained in:
parent
dd14cd63be
commit
78296c2797
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.78 2000/01/26 05:56:34 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.79 2000/02/05 18:26:09 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -51,15 +51,12 @@ typedef enum {
|
|||||||
} Prefix_Status;
|
} Prefix_Status;
|
||||||
|
|
||||||
static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index,
|
static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index,
|
||||||
int indexkey, Oid opclass,
|
|
||||||
List *restrictinfo_list);
|
List *restrictinfo_list);
|
||||||
static List *match_index_orclause(RelOptInfo *rel, IndexOptInfo *index,
|
static List *match_index_orclause(RelOptInfo *rel, IndexOptInfo *index,
|
||||||
int indexkey, Oid opclass,
|
|
||||||
List *or_clauses,
|
List *or_clauses,
|
||||||
List *other_matching_indices);
|
List *other_matching_indices);
|
||||||
static bool match_or_subclause_to_indexkey(RelOptInfo *rel,
|
static bool match_or_subclause_to_indexkey(RelOptInfo *rel,
|
||||||
IndexOptInfo *index,
|
IndexOptInfo *index,
|
||||||
int indexkey, Oid opclass,
|
|
||||||
Expr *clause);
|
Expr *clause);
|
||||||
static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index,
|
static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index,
|
||||||
int *indexkeys, Oid *classes,
|
int *indexkeys, Oid *classes,
|
||||||
@ -174,20 +171,11 @@ create_index_paths(Query *root,
|
|||||||
* so we can't build a path for an 'or' clause until all indexes have
|
* so we can't build a path for an 'or' clause until all indexes have
|
||||||
* been matched against it.)
|
* been matched against it.)
|
||||||
*
|
*
|
||||||
* We currently only look to match the first key of each index against
|
|
||||||
* 'or' subclauses. There are cases where a later key of a multi-key
|
|
||||||
* index could be used (if other top-level clauses match earlier keys
|
|
||||||
* of the index), but our poor brains are hurting already...
|
|
||||||
*
|
|
||||||
* We don't even think about special handling of 'or' clauses that
|
* We don't even think about special handling of 'or' clauses that
|
||||||
* involve more than one relation (ie, are join clauses).
|
* involve more than one relation (ie, are join clauses).
|
||||||
* Can we do anything useful with those?
|
* Can we do anything useful with those?
|
||||||
*/
|
*/
|
||||||
match_index_orclauses(rel,
|
match_index_orclauses(rel, index, restrictinfo_list);
|
||||||
index,
|
|
||||||
index->indexkeys[0],
|
|
||||||
index->classlist[0],
|
|
||||||
restrictinfo_list);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 2. If the keys of this index match any of the available non-'or'
|
* 2. If the keys of this index match any of the available non-'or'
|
||||||
@ -267,15 +255,11 @@ create_index_paths(Query *root,
|
|||||||
*
|
*
|
||||||
* 'rel' is the node of the relation on which the index is defined.
|
* 'rel' is the node of the relation on which the index is defined.
|
||||||
* 'index' is the index node.
|
* 'index' is the index node.
|
||||||
* 'indexkey' is the (single) key of the index that we will consider.
|
|
||||||
* 'class' is the class of the operator corresponding to 'indexkey'.
|
|
||||||
* 'restrictinfo_list' is the list of available restriction clauses.
|
* 'restrictinfo_list' is the list of available restriction clauses.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
match_index_orclauses(RelOptInfo *rel,
|
match_index_orclauses(RelOptInfo *rel,
|
||||||
IndexOptInfo *index,
|
IndexOptInfo *index,
|
||||||
int indexkey,
|
|
||||||
Oid opclass,
|
|
||||||
List *restrictinfo_list)
|
List *restrictinfo_list)
|
||||||
{
|
{
|
||||||
List *i;
|
List *i;
|
||||||
@ -292,7 +276,6 @@ match_index_orclauses(RelOptInfo *rel,
|
|||||||
*/
|
*/
|
||||||
restrictinfo->subclauseindices =
|
restrictinfo->subclauseindices =
|
||||||
match_index_orclause(rel, index,
|
match_index_orclause(rel, index,
|
||||||
indexkey, opclass,
|
|
||||||
restrictinfo->clause->args,
|
restrictinfo->clause->args,
|
||||||
restrictinfo->subclauseindices);
|
restrictinfo->subclauseindices);
|
||||||
}
|
}
|
||||||
@ -308,6 +291,9 @@ match_index_orclauses(RelOptInfo *rel,
|
|||||||
* index's specified operator class, and
|
* index's specified operator class, and
|
||||||
* (2) one operand of the subclause matches the index key.
|
* (2) one operand of the subclause matches the index key.
|
||||||
*
|
*
|
||||||
|
* If a subclause is an 'and' clause, then it matches if any of its
|
||||||
|
* subclauses is an opclause that matches.
|
||||||
|
*
|
||||||
* 'or_clauses' is the list of subclauses within the 'or' clause
|
* 'or_clauses' is the list of subclauses within the 'or' clause
|
||||||
* 'other_matching_indices' is the list of information on other indices
|
* 'other_matching_indices' is the list of information on other indices
|
||||||
* that have already been matched to subclauses within this
|
* that have already been matched to subclauses within this
|
||||||
@ -323,8 +309,6 @@ match_index_orclauses(RelOptInfo *rel,
|
|||||||
static List *
|
static List *
|
||||||
match_index_orclause(RelOptInfo *rel,
|
match_index_orclause(RelOptInfo *rel,
|
||||||
IndexOptInfo *index,
|
IndexOptInfo *index,
|
||||||
int indexkey,
|
|
||||||
Oid opclass,
|
|
||||||
List *or_clauses,
|
List *or_clauses,
|
||||||
List *other_matching_indices)
|
List *other_matching_indices)
|
||||||
{
|
{
|
||||||
@ -350,8 +334,7 @@ match_index_orclause(RelOptInfo *rel,
|
|||||||
{
|
{
|
||||||
Expr *clause = lfirst(clist);
|
Expr *clause = lfirst(clist);
|
||||||
|
|
||||||
if (match_or_subclause_to_indexkey(rel, index, indexkey, opclass,
|
if (match_or_subclause_to_indexkey(rel, index, clause))
|
||||||
clause))
|
|
||||||
{
|
{
|
||||||
/* OK to add this index to sublist for this subclause */
|
/* OK to add this index to sublist for this subclause */
|
||||||
lfirst(matching_indices) = lcons(index,
|
lfirst(matching_indices) = lcons(index,
|
||||||
@ -368,33 +351,84 @@ match_index_orclause(RelOptInfo *rel,
|
|||||||
* See if a subclause of an OR clause matches an index.
|
* See if a subclause of an OR clause matches an index.
|
||||||
*
|
*
|
||||||
* We accept the subclause if it is an operator clause that matches the
|
* We accept the subclause if it is an operator clause that matches the
|
||||||
* index, or if it is an AND clause all of whose members are operators
|
* index, or if it is an AND clause any of whose members is an opclause
|
||||||
* that match the index. (XXX Would accepting a single match be useful?)
|
* that matches the index.
|
||||||
|
*
|
||||||
|
* We currently only look to match the first key of an index against
|
||||||
|
* 'or' subclauses. There are cases where a later key of a multi-key
|
||||||
|
* index could be used (if other top-level clauses match earlier keys
|
||||||
|
* of the index), but our poor brains are hurting already...
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
match_or_subclause_to_indexkey(RelOptInfo *rel,
|
match_or_subclause_to_indexkey(RelOptInfo *rel,
|
||||||
IndexOptInfo *index,
|
IndexOptInfo *index,
|
||||||
int indexkey,
|
|
||||||
Oid opclass,
|
|
||||||
Expr *clause)
|
Expr *clause)
|
||||||
{
|
{
|
||||||
|
int indexkey = index->indexkeys[0];
|
||||||
|
Oid opclass = index->classlist[0];
|
||||||
|
|
||||||
if (and_clause((Node *) clause))
|
if (and_clause((Node *) clause))
|
||||||
{
|
{
|
||||||
List *item;
|
List *item;
|
||||||
|
|
||||||
foreach(item, clause->args)
|
foreach(item, clause->args)
|
||||||
{
|
{
|
||||||
if (! match_clause_to_indexkey(rel, index, indexkey, opclass,
|
if (match_clause_to_indexkey(rel, index, indexkey, opclass,
|
||||||
lfirst(item), false))
|
lfirst(item), false))
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return match_clause_to_indexkey(rel, index, indexkey, opclass,
|
return match_clause_to_indexkey(rel, index, indexkey, opclass,
|
||||||
clause, false);
|
clause, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given an OR subclause that has previously been determined to match
|
||||||
|
* the specified index, extract a list of specific opclauses that can be
|
||||||
|
* used as indexquals.
|
||||||
|
*
|
||||||
|
* In the simplest case this just means making a one-element list of the
|
||||||
|
* given opclause. However, if the OR subclause is an AND, we have to
|
||||||
|
* scan it to find the opclause(s) that match the index. (There should
|
||||||
|
* be at least one, if match_or_subclause_to_indexkey succeeded, but there
|
||||||
|
* could be more.) Also, we apply expand_indexqual_conditions() to convert
|
||||||
|
* any special matching opclauses to indexable operators.
|
||||||
|
*
|
||||||
|
* The passed-in clause is not changed.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
extract_or_indexqual_conditions(RelOptInfo *rel,
|
||||||
|
IndexOptInfo *index,
|
||||||
|
Expr *orsubclause)
|
||||||
|
{
|
||||||
|
List *quals = NIL;
|
||||||
|
int indexkey = index->indexkeys[0];
|
||||||
|
Oid opclass = index->classlist[0];
|
||||||
|
|
||||||
|
if (and_clause((Node *) orsubclause))
|
||||||
|
{
|
||||||
|
List *item;
|
||||||
|
|
||||||
|
foreach(item, orsubclause->args)
|
||||||
|
{
|
||||||
|
if (match_clause_to_indexkey(rel, index, indexkey, opclass,
|
||||||
|
lfirst(item), false))
|
||||||
|
quals = lappend(quals, lfirst(item));
|
||||||
|
}
|
||||||
|
if (quals == NIL)
|
||||||
|
elog(ERROR, "extract_or_indexqual_conditions: no matching clause");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* we assume the caller passed a valid indexable qual */
|
||||||
|
quals = lcons(orsubclause, NIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return expand_indexqual_conditions(quals);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* ---- ROUTINES TO CHECK RESTRICTIONS ----
|
* ---- ROUTINES TO CHECK RESTRICTIONS ----
|
||||||
@ -614,8 +648,8 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
|||||||
*
|
*
|
||||||
* Returns true if the clause can be used with this index key.
|
* Returns true if the clause can be used with this index key.
|
||||||
*
|
*
|
||||||
* NOTE: returns false if clause is an OR or AND clause; to the extent
|
* NOTE: returns false if clause is an OR or AND clause; it is the
|
||||||
* we cope with those at all, it is done by higher-level routines.
|
* responsibility of higher-level routines to cope with those.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
match_clause_to_indexkey(RelOptInfo *rel,
|
match_clause_to_indexkey(RelOptInfo *rel,
|
||||||
|
@ -8,15 +8,13 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.35 2000/01/26 05:56:34 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.36 2000/02/05 18:26:09 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "optimizer/cost.h"
|
#include "optimizer/cost.h"
|
||||||
@ -33,7 +31,8 @@ static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
|
|||||||
List **indexids,
|
List **indexids,
|
||||||
Cost *cost);
|
Cost *cost);
|
||||||
static void best_or_subclause_index(Query *root, RelOptInfo *rel,
|
static void best_or_subclause_index(Query *root, RelOptInfo *rel,
|
||||||
List *indexqual, List *indices,
|
Expr *subclause, List *indices,
|
||||||
|
List **retIndexQual,
|
||||||
Oid *retIndexid,
|
Oid *retIndexid,
|
||||||
Cost *retCost);
|
Cost *retCost);
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ static void best_or_subclause_index(Query *root, RelOptInfo *rel,
|
|||||||
* Creates index paths for indices that match 'or' clauses.
|
* Creates index paths for indices that match 'or' clauses.
|
||||||
* create_index_paths() must already have been called.
|
* create_index_paths() must already have been called.
|
||||||
*
|
*
|
||||||
* 'rel' is the relation entry for which the paths are to be defined on
|
* 'rel' is the relation entry for which the paths are to be created
|
||||||
* 'clauses' is the list of available restriction clause nodes
|
* 'clauses' is the list of available restriction clause nodes
|
||||||
*
|
*
|
||||||
* Returns a list of index path nodes.
|
* Returns a list of index path nodes.
|
||||||
@ -164,21 +163,16 @@ best_or_subclause_indices(Query *root,
|
|||||||
foreach(slist, subclauses)
|
foreach(slist, subclauses)
|
||||||
{
|
{
|
||||||
Expr *subclause = lfirst(slist);
|
Expr *subclause = lfirst(slist);
|
||||||
List *indexqual;
|
List *best_indexqual;
|
||||||
Oid best_indexid;
|
Oid best_indexid;
|
||||||
Cost best_cost;
|
Cost best_cost;
|
||||||
|
|
||||||
/* Convert this 'or' subclause to an indexqual list */
|
best_or_subclause_index(root, rel, subclause, lfirst(indices),
|
||||||
indexqual = make_ands_implicit(subclause);
|
&best_indexqual, &best_indexid, &best_cost);
|
||||||
/* expand special operators to indexquals the executor can handle */
|
|
||||||
indexqual = expand_indexqual_conditions(indexqual);
|
|
||||||
|
|
||||||
best_or_subclause_index(root, rel, indexqual, lfirst(indices),
|
|
||||||
&best_indexid, &best_cost);
|
|
||||||
|
|
||||||
Assert(best_indexid != InvalidOid);
|
Assert(best_indexid != InvalidOid);
|
||||||
|
|
||||||
*indexquals = lappend(*indexquals, indexqual);
|
*indexquals = lappend(*indexquals, best_indexqual);
|
||||||
*indexids = lappendi(*indexids, best_indexid);
|
*indexids = lappendi(*indexids, best_indexid);
|
||||||
*cost += best_cost;
|
*cost += best_cost;
|
||||||
|
|
||||||
@ -193,41 +187,49 @@ best_or_subclause_indices(Query *root,
|
|||||||
* the least expensive.
|
* the least expensive.
|
||||||
*
|
*
|
||||||
* 'rel' is the node of the relation on which the index is defined
|
* 'rel' is the node of the relation on which the index is defined
|
||||||
* 'indexqual' is the indexqual list derived from the subclause
|
* 'subclause' is the OR subclause being considered
|
||||||
* 'indices' is a list of IndexOptInfo nodes that match the subclause
|
* 'indices' is a list of IndexOptInfo nodes that match the subclause
|
||||||
|
* '*retIndexQual' gets a list of the indexqual conditions for the best index
|
||||||
* '*retIndexid' gets the OID of the best index
|
* '*retIndexid' gets the OID of the best index
|
||||||
* '*retCost' gets the cost of a scan with that index
|
* '*retCost' gets the cost of a scan with that index
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
best_or_subclause_index(Query *root,
|
best_or_subclause_index(Query *root,
|
||||||
RelOptInfo *rel,
|
RelOptInfo *rel,
|
||||||
List *indexqual,
|
Expr *subclause,
|
||||||
List *indices,
|
List *indices,
|
||||||
|
List **retIndexQual, /* return value */
|
||||||
Oid *retIndexid, /* return value */
|
Oid *retIndexid, /* return value */
|
||||||
Cost *retCost) /* return value */
|
Cost *retCost) /* return value */
|
||||||
{
|
{
|
||||||
bool first_run = true;
|
bool first_time = true;
|
||||||
List *ilist;
|
List *ilist;
|
||||||
|
|
||||||
/* if we don't match anything, return zeros */
|
/* if we don't match anything, return zeros */
|
||||||
|
*retIndexQual = NIL;
|
||||||
*retIndexid = InvalidOid;
|
*retIndexid = InvalidOid;
|
||||||
*retCost = 0.0;
|
*retCost = 0.0;
|
||||||
|
|
||||||
foreach(ilist, indices)
|
foreach(ilist, indices)
|
||||||
{
|
{
|
||||||
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
||||||
|
List *indexqual;
|
||||||
Cost subcost;
|
Cost subcost;
|
||||||
|
|
||||||
Assert(IsA(index, IndexOptInfo));
|
Assert(IsA(index, IndexOptInfo));
|
||||||
|
|
||||||
|
/* Convert this 'or' subclause to an indexqual list */
|
||||||
|
indexqual = extract_or_indexqual_conditions(rel, index, subclause);
|
||||||
|
|
||||||
subcost = cost_index(root, rel, index, indexqual,
|
subcost = cost_index(root, rel, index, indexqual,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
if (first_run || subcost < *retCost)
|
if (first_time || subcost < *retCost)
|
||||||
{
|
{
|
||||||
|
*retIndexQual = indexqual;
|
||||||
*retIndexid = index->indexoid;
|
*retIndexid = index->indexoid;
|
||||||
*retCost = subcost;
|
*retCost = subcost;
|
||||||
first_run = false;
|
first_time = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: paths.h,v 1.39 2000/01/26 05:58:20 momjian Exp $
|
* $Id: paths.h,v 1.40 2000/02/05 18:26:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -38,8 +38,18 @@ extern List *create_index_paths(Query *root, RelOptInfo *rel, List *indices,
|
|||||||
List *joininfo_list);
|
List *joininfo_list);
|
||||||
extern Oid indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
extern Oid indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||||
bool indexkey_on_left);
|
bool indexkey_on_left);
|
||||||
|
extern List *extract_or_indexqual_conditions(RelOptInfo *rel,
|
||||||
|
IndexOptInfo *index,
|
||||||
|
Expr *orsubclause);
|
||||||
extern List *expand_indexqual_conditions(List *indexquals);
|
extern List *expand_indexqual_conditions(List *indexquals);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* orindxpath.c
|
||||||
|
* additional routines for indexable OR clauses
|
||||||
|
*/
|
||||||
|
extern List *create_or_index_paths(Query *root, RelOptInfo *rel,
|
||||||
|
List *clauses);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tidpath.h
|
* tidpath.h
|
||||||
* routines to generate tid paths
|
* routines to generate tid paths
|
||||||
@ -52,12 +62,6 @@ extern List *create_tidscan_paths(Query *root, RelOptInfo *rel);
|
|||||||
*/
|
*/
|
||||||
extern void update_rels_pathlist_for_joins(Query *root, List *joinrels);
|
extern void update_rels_pathlist_for_joins(Query *root, List *joinrels);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* orindxpath.c
|
|
||||||
*/
|
|
||||||
extern List *create_or_index_paths(Query *root, RelOptInfo *rel, List *clauses);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pathkeys.c
|
* pathkeys.c
|
||||||
* utilities for matching and building path keys
|
* utilities for matching and building path keys
|
||||||
@ -100,7 +104,7 @@ extern bool nonoverlap_sets(List *s1, List *s2);
|
|||||||
extern bool is_subset(List *s1, List *s2);
|
extern bool is_subset(List *s1, List *s2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prototypes for path/prune.c
|
* prune.c
|
||||||
*/
|
*/
|
||||||
extern void merge_rels_with_same_relids(List *rel_list);
|
extern void merge_rels_with_same_relids(List *rel_list);
|
||||||
extern void rels_set_cheapest(Query *root, List *rel_list);
|
extern void rels_set_cheapest(Query *root, List *rel_list);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user