postgresql/src/backend/optimizer/path/orindxpath.c

264 lines
7.3 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* orindxpath.c
* Routines to find index paths that match a set of 'or' clauses
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.32 1999/08/16 02:17:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
1999-07-16 07:00:38 +02:00
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
1999-07-16 07:00:38 +02:00
#include "optimizer/internal.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
1999-07-16 07:00:38 +02:00
#include "optimizer/restrictinfo.h"
#include "parser/parsetree.h"
static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
List *subclauses, List *indices,
List **indexquals,
List **indexids,
Cost *cost, Cost *selec);
static void best_or_subclause_index(Query *root, RelOptInfo *rel,
List *indexqual, List *indices,
int *retIndexid,
Cost *retCost, Cost *retSelec);
/*
* create_or_index_paths
* Creates index paths for indices that match 'or' clauses.
* create_index_paths() must already have been called.
*
* 'rel' is the relation entry for which the paths are to be defined on
* 'clauses' is the list of available restriction clause nodes
*
* Returns a list of index path nodes.
*
*/
List *
create_or_index_paths(Query *root,
1999-05-26 00:43:53 +02:00
RelOptInfo *rel, List *clauses)
{
List *path_list = NIL;
1998-08-02 00:12:13 +02:00
List *clist;
1998-08-02 00:12:13 +02:00
foreach(clist, clauses)
{
RestrictInfo *clausenode = (RestrictInfo *) lfirst(clist);
/*
* Check to see if this clause is an 'or' clause, and, if so,
* whether or not each of the subclauses within the 'or' clause
* has been matched by an index. The information used was
* saved by create_index_paths().
*/
if (restriction_is_or_clause(clausenode) &&
clausenode->subclauseindices)
{
bool all_indexable = true;
List *temp;
foreach(temp, clausenode->subclauseindices)
{
if (lfirst(temp) == NIL)
1998-08-02 00:12:13 +02:00
{
all_indexable = false;
1998-08-02 00:12:13 +02:00
break;
}
}
if (all_indexable)
{
/*
* OK, build an IndexPath for this OR clause, using the
* best available index for each subclause.
*/
IndexPath *pathnode = makeNode(IndexPath);
List *indexquals;
List *indexids;
Cost cost;
1999-05-25 18:15:34 +02:00
Cost selec;
best_or_subclause_indices(root,
rel,
clausenode->clause->args,
clausenode->subclauseindices,
&indexquals,
&indexids,
&cost,
1999-03-08 15:01:57 +01:00
&selec);
pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel;
1999-05-25 18:15:34 +02:00
/*
* This is an IndexScan, but the overall result will consist
* of tuples extracted in multiple passes (one for each
* subclause of the OR), so the result cannot be claimed
* to have any particular ordering.
1998-09-21 17:41:28 +02:00
*/
1999-05-25 18:15:34 +02:00
pathnode->path.pathkeys = NIL;
1998-09-21 17:41:28 +02:00
pathnode->indexid = indexids;
pathnode->indexqual = indexquals;
pathnode->joinrelids = NIL; /* no join clauses here */
pathnode->path.path_cost = cost;
clausenode->selectivity = (Cost) selec;
path_list = lappend(path_list, pathnode);
}
}
}
return path_list;
}
/*
* best_or_subclause_indices
* Determines the best index to be used in conjunction with each subclause
* of an 'or' clause and the cost of scanning a relation using these
* indices. The cost is the sum of the individual index costs, since
* the executor will perform a scan for each subclause of the 'or'.
*
* This routine also creates the indexquals and indexids lists that will
* be needed by the executor. The indexquals list has one entry for each
* scan of the base rel, which is a sublist of indexqual conditions to
* apply in that scan. The implicit semantics are AND across each sublist
* of quals, and OR across the toplevel list (note that the executor
* takes care not to return any single tuple more than once). The indexids
* list gives the index to be used in each scan.
*
* 'rel' is the node of the relation on which the indexes are defined
* 'subclauses' are the subclauses of the 'or' clause
* 'indices' is a list of sublists of the index nodes that matched each
* subclause of the 'or' clause
* '*indexquals' gets the constructed indexquals for the path (a list
* of sublists of clauses, one sublist per scan of the base rel)
* '*indexids' gets a list of the index IDs for each scan of the rel
* '*cost' gets the total cost of the path
* '*selec' gets the total selectivity of the path.
*/
static void
best_or_subclause_indices(Query *root,
1999-05-26 00:43:53 +02:00
RelOptInfo *rel,
List *subclauses,
List *indices,
List **indexquals, /* return value */
List **indexids, /* return value */
Cost *cost, /* return value */
Cost *selec) /* return value */
{
1999-05-25 18:15:34 +02:00
List *slist;
*indexquals = NIL;
*indexids = NIL;
1999-03-08 15:01:57 +01:00
*cost = (Cost) 0.0;
*selec = (Cost) 0.0;
1999-05-25 18:15:34 +02:00
foreach(slist, subclauses)
{
Expr *subclause = lfirst(slist);
List *indexqual;
int best_indexid;
Cost best_cost;
Cost best_selec;
/* Convert this 'or' subclause to an indexqual list */
indexqual = make_ands_implicit(subclause);
/* 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, &best_selec);
*indexquals = lappend(*indexquals, indexqual);
1999-03-08 15:01:57 +01:00
*indexids = lappendi(*indexids, best_indexid);
*cost += best_cost;
/* We approximate the selectivity as the sum of the clause
* selectivities (but not more than 1).
* XXX This is too pessimistic, isn't it?
*/
1999-03-08 15:01:57 +01:00
*selec += best_selec;
if (*selec > (Cost) 1.0)
*selec = (Cost) 1.0;
1998-08-02 00:12:13 +02:00
indices = lnext(indices);
}
}
/*
* best_or_subclause_index
* Determines which is the best index to be used with a subclause of
* an 'or' clause by estimating the cost of using each index and selecting
* the least expensive.
*
* 'rel' is the node of the relation on which the index is defined
* 'indexqual' is the indexqual list derived from the subclause
* 'indices' is a list of index nodes that match the subclause
* '*retIndexid' gets the ID of the best index
* '*retCost' gets the cost of a scan with that index
* '*retSelec' gets the selectivity of that scan
*/
static void
best_or_subclause_index(Query *root,
1999-05-26 00:43:53 +02:00
RelOptInfo *rel,
List *indexqual,
List *indices,
int *retIndexid, /* return value */
Cost *retCost, /* return value */
Cost *retSelec) /* return value */
{
bool first_run = true;
List *ilist;
1998-08-31 09:19:56 +02:00
/* if we don't match anything, return zeros */
*retIndexid = 0;
*retCost = (Cost) 0.0;
*retSelec = (Cost) 0.0;
foreach(ilist, indices)
{
RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
Oid indexid = (Oid) lfirsti(index->relids);
Cost subcost;
float npages;
float selec;
index_selectivity(root,
lfirsti(rel->relids),
indexid,
indexqual,
&npages,
&selec);
subcost = cost_index(indexid,
(int) npages,
(Cost) selec,
rel->pages,
rel->tuples,
index->pages,
index->tuples,
false);
1998-08-02 00:12:13 +02:00
if (first_run || subcost < *retCost)
{
*retIndexid = indexid;
*retCost = subcost;
*retSelec = selec;
1998-08-02 00:12:13 +02:00
first_run = false;
}
1998-08-02 00:12:13 +02:00
}
}