1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* orindxpath.c
|
2004-01-04 01:07:32 +01:00
|
|
|
* Routines to find index paths that match a set of OR clauses
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2003-08-04 04:40:20 +02:00
|
|
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2004-01-04 01:07:32 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.55 2004/01/04 00:07:32 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-02-05 19:26:09 +01:00
|
|
|
#include "postgres.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "optimizer/cost.h"
|
2000-02-15 21:49:31 +01:00
|
|
|
#include "optimizer/pathnode.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/paths.h"
|
|
|
|
#include "optimizer/restrictinfo.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
static IndexPath *best_or_subclause_indices(Query *root, RelOptInfo *rel,
|
|
|
|
List *subclauses);
|
|
|
|
static bool best_or_subclause_index(Query *root,
|
|
|
|
RelOptInfo *rel,
|
|
|
|
Expr *subclause,
|
2001-05-20 22:28:20 +02:00
|
|
|
IndexOptInfo **retIndexInfo,
|
2000-04-12 19:17:23 +02:00
|
|
|
List **retIndexQual,
|
|
|
|
Cost *retStartupCost,
|
|
|
|
Cost *retTotalCost);
|
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
|
2004-01-04 01:07:32 +01:00
|
|
|
* Creates multi-scan index paths for indices that match OR clauses.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2000-02-05 19:26:09 +01:00
|
|
|
* 'rel' is the relation entry for which the paths are to be created
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2000-02-15 21:49:31 +01:00
|
|
|
* Returns nothing, but adds paths to rel->pathlist via add_path().
|
2004-01-04 01:07:32 +01:00
|
|
|
*
|
|
|
|
* Note: create_index_paths() must have been run already, since it does
|
|
|
|
* the heavy lifting to determine whether partial indexes may be used.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-02-15 21:49:31 +01:00
|
|
|
void
|
2001-06-05 19:13:52 +02:00
|
|
|
create_or_index_paths(Query *root, RelOptInfo *rel)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-01-04 01:07:32 +01:00
|
|
|
List *i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
/*
|
|
|
|
* Check each restriction clause to see if it is an OR clause, and if so,
|
|
|
|
* try to make a path using it.
|
|
|
|
*/
|
|
|
|
foreach(i, rel->baserestrictinfo)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-01-04 01:07:32 +01:00
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
if (restriction_is_or_clause(rinfo))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-01-04 01:07:32 +01:00
|
|
|
IndexPath *pathnode;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
pathnode = best_or_subclause_indices(root,
|
|
|
|
rel,
|
|
|
|
((BoolExpr *) rinfo->orclause)->args);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
if (pathnode)
|
|
|
|
add_path(rel, (Path *) pathnode);
|
|
|
|
}
|
|
|
|
}
|
2003-06-16 00:51:45 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
/*
|
|
|
|
* Also consider join clauses that are ORs. Although a join clause
|
|
|
|
* must reference other relations overall, an OR of ANDs clause might
|
|
|
|
* contain sub-clauses that reference just our relation and can be
|
|
|
|
* used to build a non-join indexscan. For example consider
|
|
|
|
* WHERE (a.x = 42 AND b.y = 43) OR (a.x = 44 AND b.z = 45);
|
|
|
|
* We could build an OR indexscan on a.x using those subclauses.
|
|
|
|
*
|
|
|
|
* XXX don't enable this code quite yet. Although the plans it creates
|
|
|
|
* are correct, and possibly even useful, we are totally confused about
|
|
|
|
* the number of rows returned, leading to poor choices of join plans
|
|
|
|
* above the indexscan. Need to restructure the way join sizes are
|
|
|
|
* calculated before this will really work.
|
|
|
|
*/
|
|
|
|
#ifdef NOT_YET
|
|
|
|
foreach(i, rel->joininfo)
|
|
|
|
{
|
|
|
|
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
|
|
|
List *j;
|
1998-09-21 17:41:28 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
foreach(j, joininfo->jinfo_restrictinfo)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
|
2000-02-15 21:49:31 +01:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
if (restriction_is_or_clause(rinfo))
|
|
|
|
{
|
|
|
|
IndexPath *pathnode;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
pathnode = best_or_subclause_indices(root,
|
|
|
|
rel,
|
|
|
|
((BoolExpr *) rinfo->orclause)->args);
|
2000-02-15 21:49:31 +01:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
if (pathnode)
|
|
|
|
add_path(rel, (Path *) pathnode);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
2004-01-04 01:07:32 +01:00
|
|
|
#endif
|
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
|
2004-01-04 01:07:32 +01:00
|
|
|
* Determine the best index to be used in conjunction with each subclause
|
|
|
|
* of an OR clause, and build a Path for a multi-index scan.
|
2000-02-15 21:49:31 +01:00
|
|
|
*
|
2004-01-04 01:07:32 +01:00
|
|
|
* 'rel' is the node of the relation to be scanned
|
|
|
|
* 'subclauses' are the subclauses of the OR clause (must be the modified
|
|
|
|
* form that includes sub-RestrictInfo clauses)
|
2000-02-15 21:49:31 +01:00
|
|
|
*
|
2004-01-04 01:07:32 +01:00
|
|
|
* Returns an IndexPath if successful, or NULL if it is not possible to
|
|
|
|
* find an index for each OR subclause.
|
2000-02-15 21:49:31 +01:00
|
|
|
*
|
|
|
|
* NOTE: we choose each scan on the basis of its total cost, ignoring startup
|
|
|
|
* cost. This is reasonable as long as all index types have zero or small
|
|
|
|
* startup cost, but we might have to work harder if any index types with
|
|
|
|
* nontrivial startup cost are ever invented.
|
2004-01-04 01:07:32 +01:00
|
|
|
*
|
|
|
|
* This routine also creates the indexqual list that will be needed by
|
|
|
|
* the executor. The indexqual 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).
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2004-01-04 01:07:32 +01:00
|
|
|
static IndexPath *
|
1997-09-08 23:56:23 +02:00
|
|
|
best_or_subclause_indices(Query *root,
|
1999-05-26 00:43:53 +02:00
|
|
|
RelOptInfo *rel,
|
2004-01-04 01:07:32 +01:00
|
|
|
List *subclauses)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-05-29 00:32:50 +02:00
|
|
|
FastList infos;
|
|
|
|
FastList quals;
|
2004-01-04 01:07:32 +01:00
|
|
|
Cost path_startup_cost;
|
|
|
|
Cost path_total_cost;
|
1999-05-25 18:15:34 +02:00
|
|
|
List *slist;
|
2004-01-04 01:07:32 +01:00
|
|
|
IndexPath *pathnode;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastListInit(&infos);
|
|
|
|
FastListInit(&quals);
|
2004-01-04 01:07:32 +01:00
|
|
|
path_startup_cost = 0;
|
|
|
|
path_total_cost = 0;
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
/* Gather info for each OR subclause */
|
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);
|
2001-05-20 22:28:20 +02:00
|
|
|
IndexOptInfo *best_indexinfo;
|
2000-02-05 19:26:09 +01:00
|
|
|
List *best_indexqual;
|
2000-02-15 21:49:31 +01:00
|
|
|
Cost best_startup_cost;
|
|
|
|
Cost best_total_cost;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
if (!best_or_subclause_index(root, rel, subclause,
|
|
|
|
&best_indexinfo, &best_indexqual,
|
|
|
|
&best_startup_cost, &best_total_cost))
|
|
|
|
return NULL; /* failed to match this subclause */
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastAppend(&infos, best_indexinfo);
|
|
|
|
FastAppend(&quals, best_indexqual);
|
2004-01-04 01:07:32 +01:00
|
|
|
/*
|
|
|
|
* Path startup_cost is the startup cost for the first index scan only;
|
|
|
|
* startup costs for later scans will be paid later on, so they just
|
|
|
|
* get reflected in total_cost.
|
|
|
|
*
|
|
|
|
* Total cost is sum of the per-scan costs.
|
|
|
|
*/
|
2001-10-28 07:26:15 +01:00
|
|
|
if (slist == subclauses) /* first scan? */
|
2004-01-04 01:07:32 +01:00
|
|
|
path_startup_cost = best_startup_cost;
|
|
|
|
path_total_cost += best_total_cost;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2003-05-29 00:32:50 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
/* We succeeded, so build an IndexPath node */
|
|
|
|
pathnode = makeNode(IndexPath);
|
|
|
|
|
|
|
|
pathnode->path.pathtype = T_IndexScan;
|
|
|
|
pathnode->path.parent = rel;
|
|
|
|
pathnode->path.startup_cost = path_startup_cost;
|
|
|
|
pathnode->path.total_cost = path_total_cost;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
pathnode->path.pathkeys = NIL;
|
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
pathnode->indexinfo = FastListValue(&infos);
|
|
|
|
pathnode->indexqual = FastListValue(&quals);
|
2004-01-04 01:07:32 +01:00
|
|
|
|
|
|
|
/* It's not an innerjoin path. */
|
|
|
|
pathnode->indexjoinclauses = NIL;
|
|
|
|
|
|
|
|
/* We don't actually care what order the index scans in. */
|
|
|
|
pathnode->indexscandir = NoMovementScanDirection;
|
|
|
|
|
|
|
|
/* XXX this may be wrong when using join OR clauses... */
|
|
|
|
pathnode->rows = rel->rows;
|
|
|
|
|
|
|
|
return pathnode;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* best_or_subclause_index
|
2000-02-15 21:49:31 +01:00
|
|
|
* Determines which is the best index to be used with a subclause of an
|
2004-01-04 01:07:32 +01:00
|
|
|
* OR clause by estimating the cost of using each index and selecting
|
2000-02-15 21:49:31 +01:00
|
|
|
* the least expensive (considering total cost only, for now).
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2004-01-04 01:07:32 +01:00
|
|
|
* Returns FALSE if no index exists that can be used with this OR subclause;
|
|
|
|
* in that case the output parameters are not set.
|
|
|
|
*
|
|
|
|
* 'rel' is the node of the relation to be scanned
|
2000-02-05 19:26:09 +01:00
|
|
|
* 'subclause' is the OR subclause being considered
|
2004-01-04 01:07:32 +01:00
|
|
|
*
|
2001-05-20 22:28:20 +02:00
|
|
|
* '*retIndexInfo' gets the IndexOptInfo of the best index
|
2000-02-05 19:26:09 +01:00
|
|
|
* '*retIndexQual' gets a list of the indexqual conditions for the best index
|
2000-02-15 21:49:31 +01:00
|
|
|
* '*retStartupCost' gets the startup cost of a scan with that index
|
|
|
|
* '*retTotalCost' gets the total cost of a scan with that index
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2004-01-04 01:07:32 +01:00
|
|
|
static bool
|
1997-09-08 23:56:23 +02:00
|
|
|
best_or_subclause_index(Query *root,
|
1999-05-26 00:43:53 +02:00
|
|
|
RelOptInfo *rel,
|
2000-02-05 19:26:09 +01:00
|
|
|
Expr *subclause,
|
2001-10-25 07:50:21 +02:00
|
|
|
IndexOptInfo **retIndexInfo, /* return value */
|
2000-02-05 19:26:09 +01:00
|
|
|
List **retIndexQual, /* return value */
|
2000-02-15 21:49:31 +01:00
|
|
|
Cost *retStartupCost, /* return value */
|
|
|
|
Cost *retTotalCost) /* return value */
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-01-04 01:07:32 +01:00
|
|
|
bool found = false;
|
1999-07-25 01:21:14 +02:00
|
|
|
List *ilist;
|
1998-08-31 09:19:56 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
foreach(ilist, rel->indexlist)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-01-09 01:26:47 +01:00
|
|
|
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
2004-01-04 01:07:32 +01:00
|
|
|
List *qualrinfos;
|
|
|
|
List *indexquals;
|
2000-02-15 21:49:31 +01:00
|
|
|
Path subclause_path;
|
2000-01-09 01:26:47 +01:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
/* Ignore partial indexes that do not match the query */
|
|
|
|
if (index->indpred != NIL && !index->predOK)
|
|
|
|
continue;
|
1999-07-26 01:07:26 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
/* Collect index clauses usable with this index */
|
|
|
|
qualrinfos = group_clauses_by_indexkey_for_or(rel, index, subclause);
|
2000-02-05 19:26:09 +01:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
/* Ignore index if it doesn't match the subclause at all */
|
|
|
|
if (qualrinfos == NIL)
|
|
|
|
continue;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
/* Convert RestrictInfo nodes to indexquals the executor can handle */
|
|
|
|
indexquals = expand_indexqual_conditions(index, qualrinfos);
|
|
|
|
|
|
|
|
cost_index(&subclause_path, root, rel, index, indexquals, false);
|
|
|
|
|
|
|
|
if (!found || subclause_path.total_cost < *retTotalCost)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-05-20 22:28:20 +02:00
|
|
|
*retIndexInfo = index;
|
2004-01-04 01:07:32 +01:00
|
|
|
*retIndexQual = indexquals;
|
2000-02-15 21:49:31 +01:00
|
|
|
*retStartupCost = subclause_path.startup_cost;
|
|
|
|
*retTotalCost = subclause_path.total_cost;
|
2004-01-04 01:07:32 +01:00
|
|
|
found = true;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-08-02 00:12:13 +02:00
|
|
|
}
|
2004-01-04 01:07:32 +01:00
|
|
|
|
|
|
|
return found;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|