1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* orindxpath.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Routines to find index paths that match a set of 'or' clauses
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1999-07-27 05:51:11 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.31 1999/07/27 03:51:02 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-07-16 07:00:38 +02:00
|
|
|
|
|
|
|
#include "nodes/nodeFuncs.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "optimizer/clauses.h"
|
|
|
|
#include "optimizer/cost.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/internal.h"
|
|
|
|
#include "optimizer/paths.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "optimizer/plancat.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/restrictinfo.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "parser/parsetree.h"
|
|
|
|
|
|
|
|
|
1999-07-25 01:21:14 +02:00
|
|
|
static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
|
|
|
|
List *subclauses, List *indices,
|
1999-07-27 05:51:11 +02:00
|
|
|
List **indexquals,
|
1999-07-25 01:21:14 +02:00
|
|
|
List **indexids,
|
|
|
|
Cost *cost, Cost *selec);
|
|
|
|
static void best_or_subclause_index(Query *root, RelOptInfo *rel,
|
1999-07-27 05:51:11 +02:00
|
|
|
List *indexqual, List *indices,
|
|
|
|
int *retIndexid,
|
|
|
|
Cost *retCost, Cost *retSelec);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* create_or_index_paths
|
1997-09-07 07:04:48 +02:00
|
|
|
* Creates index paths for indices that match 'or' clauses.
|
1999-07-25 01:21:14 +02:00
|
|
|
* create_index_paths() must already have been called.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* 'rel' is the relation entry for which the paths are to be defined on
|
|
|
|
* 'clauses' is the list of available restriction clause nodes
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-07-25 01:21:14 +02:00
|
|
|
* Returns a list of index path nodes.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
1997-09-08 23:56:23 +02:00
|
|
|
create_or_index_paths(Query *root,
|
1999-05-26 00:43:53 +02:00
|
|
|
RelOptInfo *rel, List *clauses)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-07-25 01:21:14 +02:00
|
|
|
List *path_list = NIL;
|
1998-08-02 00:12:13 +02:00
|
|
|
List *clist;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-02 00:12:13 +02:00
|
|
|
foreach(clist, clauses)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-07-25 01:21:14 +02:00
|
|
|
RestrictInfo *clausenode = (RestrictInfo *) lfirst(clist);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see if this clause is an 'or' clause, and, if so,
|
|
|
|
* whether or not each of the subclauses within the 'or' clause
|
1999-07-25 01:21:14 +02:00
|
|
|
* has been matched by an index. The information used was
|
|
|
|
* saved by create_index_paths().
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-07-25 01:21:14 +02:00
|
|
|
if (restriction_is_or_clause(clausenode) &&
|
1997-09-07 07:04:48 +02:00
|
|
|
clausenode->indexids)
|
|
|
|
{
|
1999-07-25 01:21:14 +02:00
|
|
|
bool all_indexable = true;
|
|
|
|
List *temp;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-07-25 01:21:14 +02:00
|
|
|
foreach(temp, clausenode->indexids)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-07-25 01:21:14 +02:00
|
|
|
if (lfirst(temp) == NIL)
|
1998-08-02 00:12:13 +02:00
|
|
|
{
|
1999-07-25 01:21:14 +02:00
|
|
|
all_indexable = false;
|
1998-08-02 00:12:13 +02:00
|
|
|
break;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-07-25 01:21:14 +02:00
|
|
|
if (all_indexable)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* OK, build an IndexPath for this OR clause, using the
|
|
|
|
* best available index for each subclause.
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
IndexPath *pathnode = makeNode(IndexPath);
|
1999-07-27 05:51:11 +02:00
|
|
|
List *indexquals;
|
1999-07-25 01:21:14 +02:00
|
|
|
List *indexids;
|
1997-09-08 04:41:22 +02:00
|
|
|
Cost cost;
|
1999-05-25 18:15:34 +02:00
|
|
|
Cost selec;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
best_or_subclause_indices(root,
|
|
|
|
rel,
|
|
|
|
clausenode->clause->args,
|
|
|
|
clausenode->indexids,
|
1999-07-27 05:51:11 +02:00
|
|
|
&indexquals,
|
1997-09-07 07:04:48 +02:00
|
|
|
&indexids,
|
|
|
|
&cost,
|
1999-03-08 15:01:57 +01:00
|
|
|
&selec);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
pathnode->path.pathtype = T_IndexScan;
|
|
|
|
pathnode->path.parent = rel;
|
1999-02-11 15:59:09 +01:00
|
|
|
pathnode->path.pathorder = makeNode(PathOrder);
|
1999-05-25 18:15:34 +02:00
|
|
|
pathnode->path.pathorder->ordtype = SORTOP_ORDER;
|
|
|
|
|
|
|
|
/*
|
1999-07-25 01:21:14 +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.pathorder->ord.sortop = NULL;
|
|
|
|
pathnode->path.pathkeys = NIL;
|
1998-09-21 17:41:28 +02:00
|
|
|
|
1999-07-27 05:51:11 +02:00
|
|
|
pathnode->indexqual = indexquals;
|
1997-09-07 07:04:48 +02:00
|
|
|
pathnode->indexid = indexids;
|
|
|
|
pathnode->path.path_cost = cost;
|
1999-07-25 01:21:14 +02:00
|
|
|
clausenode->selectivity = (Cost) selec;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-07-25 01:21:14 +02:00
|
|
|
path_list = lappend(path_list, pathnode);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-07-25 01:21:14 +02:00
|
|
|
return path_list;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* best_or_subclause_indices
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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
|
1999-07-25 01:21:14 +02:00
|
|
|
* indices. The cost is the sum of the individual index costs, since
|
|
|
|
* the executor will perform a scan for each subclause of the 'or'.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-07-27 05:51:11 +02:00
|
|
|
* 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.
|
|
|
|
*
|
1999-07-25 01:21:14 +02:00
|
|
|
* 'rel' is the node of the relation on which the indexes are defined
|
1996-07-09 08:22:35 +02:00
|
|
|
* 'subclauses' are the subclauses of the 'or' clause
|
1999-07-25 01:21:14 +02:00
|
|
|
* 'indices' is a list of sublists of the index nodes that matched each
|
|
|
|
* subclause of the 'or' clause
|
1999-07-27 05:51:11 +02:00
|
|
|
* '*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
|
1999-07-25 01:21:14 +02:00
|
|
|
* '*cost' gets the total cost of the path
|
|
|
|
* '*selec' gets the total selectivity of the path.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
static void
|
1997-09-08 23:56:23 +02:00
|
|
|
best_or_subclause_indices(Query *root,
|
1999-05-26 00:43:53 +02:00
|
|
|
RelOptInfo *rel,
|
1997-09-08 23:56:23 +02:00
|
|
|
List *subclauses,
|
|
|
|
List *indices,
|
1999-07-27 05:51:11 +02:00
|
|
|
List **indexquals, /* return value */
|
1997-09-08 23:56:23 +02:00
|
|
|
List **indexids, /* return value */
|
1999-07-25 01:21:14 +02:00
|
|
|
Cost *cost, /* return value */
|
|
|
|
Cost *selec) /* return value */
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
List *slist;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1999-07-27 05:51:11 +02:00
|
|
|
*indexquals = NIL;
|
1999-07-25 01:21:14 +02:00
|
|
|
*indexids = NIL;
|
1999-03-08 15:01:57 +01:00
|
|
|
*cost = (Cost) 0.0;
|
1999-07-27 05:51:11 +02:00
|
|
|
*selec = (Cost) 0.0;
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
foreach(slist, subclauses)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-07-27 05:51:11 +02:00
|
|
|
Expr *subclause = lfirst(slist);
|
|
|
|
List *indexqual;
|
1997-09-08 04:41:22 +02:00
|
|
|
int best_indexid;
|
|
|
|
Cost best_cost;
|
|
|
|
Cost best_selec;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-07-27 05:51:11 +02:00
|
|
|
/* 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),
|
1997-09-07 07:04:48 +02:00
|
|
|
&best_indexid, &best_cost, &best_selec);
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1999-07-27 05:51:11 +02:00
|
|
|
*indexquals = lappend(*indexquals, indexqual);
|
1999-03-08 15:01:57 +01:00
|
|
|
*indexids = lappendi(*indexids, best_indexid);
|
|
|
|
*cost += best_cost;
|
1999-07-27 05:51:11 +02:00
|
|
|
/* 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;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-08-02 00:12:13 +02:00
|
|
|
indices = lnext(indices);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* best_or_subclause_index
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* 'rel' is the node of the relation on which the index is defined
|
1999-07-27 05:51:11 +02:00
|
|
|
* 'indexqual' is the indexqual list derived from the subclause
|
1996-07-09 08:22:35 +02:00
|
|
|
* 'indices' is a list of index nodes that match the subclause
|
1999-07-25 01:21:14 +02:00
|
|
|
* '*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
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
static void
|
1997-09-08 23:56:23 +02:00
|
|
|
best_or_subclause_index(Query *root,
|
1999-05-26 00:43:53 +02:00
|
|
|
RelOptInfo *rel,
|
1999-07-27 05:51:11 +02:00
|
|
|
List *indexqual,
|
1997-09-08 23:56:23 +02:00
|
|
|
List *indices,
|
1997-09-07 07:04:48 +02:00
|
|
|
int *retIndexid, /* return value */
|
1997-09-08 23:56:23 +02:00
|
|
|
Cost *retCost, /* return value */
|
|
|
|
Cost *retSelec) /* return value */
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
bool first_run = true;
|
1999-07-25 01:21:14 +02:00
|
|
|
List *ilist;
|
1998-08-31 09:19:56 +02:00
|
|
|
|
|
|
|
/* if we don't match anything, return zeros */
|
|
|
|
*retIndexid = 0;
|
1999-07-25 01:21:14 +02:00
|
|
|
*retCost = (Cost) 0.0;
|
|
|
|
*retSelec = (Cost) 0.0;
|
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
foreach(ilist, indices)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
|
1999-07-25 01:21:14 +02:00
|
|
|
Oid indexid = (Oid) lfirsti(index->relids);
|
1997-09-08 04:41:22 +02:00
|
|
|
Cost subcost;
|
1999-07-26 01:07:26 +02:00
|
|
|
float npages;
|
|
|
|
float selec;
|
|
|
|
|
|
|
|
index_selectivity(root,
|
|
|
|
lfirsti(rel->relids),
|
|
|
|
indexid,
|
1999-07-27 05:51:11 +02:00
|
|
|
indexqual,
|
1997-09-07 07:04:48 +02:00
|
|
|
&npages,
|
|
|
|
&selec);
|
|
|
|
|
1999-07-25 01:21:14 +02:00
|
|
|
subcost = cost_index(indexid,
|
1997-09-07 07:04:48 +02:00
|
|
|
(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)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-07-25 01:21:14 +02:00
|
|
|
*retIndexid = indexid;
|
1997-09-07 07:04:48 +02:00
|
|
|
*retCost = subcost;
|
|
|
|
*retSelec = selec;
|
1998-08-02 00:12:13 +02:00
|
|
|
first_run = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-08-02 00:12:13 +02:00
|
|
|
}
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|