aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path/orindxpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/path/orindxpath.c')
-rw-r--r--src/backend/optimizer/path/orindxpath.c268
1 files changed, 148 insertions, 120 deletions
diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c
index e77b156af22..bea2a8e4161 100644
--- a/src/backend/optimizer/path/orindxpath.c
+++ b/src/backend/optimizer/path/orindxpath.c
@@ -1,14 +1,14 @@
/*-------------------------------------------------------------------------
*
* orindxpath.c
- * Routines to find index paths that match a set of 'or' clauses
+ * Routines to find index paths that match a set of OR clauses
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.54 2003/11/29 19:51:50 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.55 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,11 +21,11 @@
#include "optimizer/restrictinfo.h"
-static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
- List *subclauses, List *indices,
- IndexPath *pathnode);
-static void best_or_subclause_index(Query *root, RelOptInfo *rel,
- Expr *subclause, List *indices,
+static IndexPath *best_or_subclause_indices(Query *root, RelOptInfo *rel,
+ List *subclauses);
+static bool best_or_subclause_index(Query *root,
+ RelOptInfo *rel,
+ Expr *subclause,
IndexOptInfo **retIndexInfo,
List **retIndexQual,
Cost *retStartupCost,
@@ -34,133 +34,123 @@ static void best_or_subclause_index(Query *root, RelOptInfo *rel,
/*
* create_or_index_paths
- * Creates index paths for indices that match 'or' clauses.
- * create_index_paths() must already have been called.
+ * Creates multi-scan index paths for indices that match OR clauses.
*
* 'rel' is the relation entry for which the paths are to be created
*
* Returns nothing, but adds paths to rel->pathlist via add_path().
+ *
+ * Note: create_index_paths() must have been run already, since it does
+ * the heavy lifting to determine whether partial indexes may be used.
*/
void
create_or_index_paths(Query *root, RelOptInfo *rel)
{
- List *rlist;
+ List *i;
- foreach(rlist, rel->baserestrictinfo)
+ /*
+ * 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)
{
- RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(rlist);
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
- /*
- * 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(restrictinfo) &&
- restrictinfo->subclauseindices)
+ if (restriction_is_or_clause(rinfo))
{
- bool all_indexable = true;
- List *temp;
-
- foreach(temp, restrictinfo->subclauseindices)
- {
- if (lfirst(temp) == NIL)
- {
- all_indexable = false;
- break;
- }
- }
- if (all_indexable)
- {
- /*
- * OK, build an IndexPath for this OR clause, using the
- * best available index for each subclause.
- */
- IndexPath *pathnode = makeNode(IndexPath);
+ IndexPath *pathnode;
- pathnode->path.pathtype = T_IndexScan;
- pathnode->path.parent = rel;
+ pathnode = best_or_subclause_indices(root,
+ rel,
+ ((BoolExpr *) rinfo->orclause)->args);
- /*
- * 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;
+ if (pathnode)
+ add_path(rel, (Path *) pathnode);
+ }
+ }
- /* It's not an innerjoin path. */
- pathnode->indexjoinclauses = NIL;
+ /*
+ * 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;
- /* We don't actually care what order the index scans in. */
- pathnode->indexscandir = NoMovementScanDirection;
+ foreach(j, joininfo->jinfo_restrictinfo)
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
- pathnode->rows = rel->rows;
+ if (restriction_is_or_clause(rinfo))
+ {
+ IndexPath *pathnode;
- best_or_subclause_indices(root,
- rel,
- ((BoolExpr *) restrictinfo->clause)->args,
- restrictinfo->subclauseindices,
- pathnode);
+ pathnode = best_or_subclause_indices(root,
+ rel,
+ ((BoolExpr *) rinfo->orclause)->args);
- add_path(rel, (Path *) pathnode);
+ if (pathnode)
+ add_path(rel, (Path *) pathnode);
}
}
}
+#endif
}
/*
* 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'.
- * Returns a list of IndexOptInfo nodes, one per scan.
- *
- * 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).
- *
- * '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 IndexOptInfo nodes that matched
- * each subclause of the 'or' clause
- * 'pathnode' is the IndexPath node being built.
+ * 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.
*
- * Results are returned by setting these fields of the passed pathnode:
- * 'indexinfo' gets a list of the index IndexOptInfo nodes, one per scan
- * 'indexqual' gets the constructed indexquals for the path (a list
- * of sublists of clauses, one sublist per scan of the base rel)
- * 'startup_cost' and 'total_cost' get the complete path costs.
+ * '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)
*
- * '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.
+ * Returns an IndexPath if successful, or NULL if it is not possible to
+ * find an index for each OR subclause.
*
* 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.
+ *
+ * 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).
*/
-static void
+static IndexPath *
best_or_subclause_indices(Query *root,
RelOptInfo *rel,
- List *subclauses,
- List *indices,
- IndexPath *pathnode)
+ List *subclauses)
{
FastList infos;
FastList quals;
+ Cost path_startup_cost;
+ Cost path_total_cost;
List *slist;
+ IndexPath *pathnode;
FastListInit(&infos);
FastListInit(&quals);
- pathnode->path.startup_cost = 0;
- pathnode->path.total_cost = 0;
+ path_startup_cost = 0;
+ path_total_cost = 0;
+ /* Gather info for each OR subclause */
foreach(slist, subclauses)
{
Expr *subclause = lfirst(slist);
@@ -169,78 +159,116 @@ best_or_subclause_indices(Query *root,
Cost best_startup_cost;
Cost best_total_cost;
- best_or_subclause_index(root, rel, subclause, lfirst(indices),
- &best_indexinfo, &best_indexqual,
- &best_startup_cost, &best_total_cost);
-
- Assert(best_indexinfo != NULL);
+ 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 */
FastAppend(&infos, best_indexinfo);
FastAppend(&quals, best_indexqual);
+ /*
+ * 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.
+ */
if (slist == subclauses) /* first scan? */
- pathnode->path.startup_cost = best_startup_cost;
- pathnode->path.total_cost += best_total_cost;
-
- indices = lnext(indices);
+ path_startup_cost = best_startup_cost;
+ path_total_cost += best_total_cost;
}
+ /* 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;
+
pathnode->indexinfo = FastListValue(&infos);
pathnode->indexqual = FastListValue(&quals);
+
+ /* 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;
}
/*
* 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
+ * OR clause by estimating the cost of using each index and selecting
* the least expensive (considering total cost only, for now).
*
- * 'rel' is the node of the relation on which the index is defined
+ * 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
* 'subclause' is the OR subclause being considered
- * 'indices' is a list of IndexOptInfo nodes that match the subclause
+ *
* '*retIndexInfo' gets the IndexOptInfo of the best index
* '*retIndexQual' gets a list of the indexqual conditions for the best index
* '*retStartupCost' gets the startup cost of a scan with that index
* '*retTotalCost' gets the total cost of a scan with that index
*/
-static void
+static bool
best_or_subclause_index(Query *root,
RelOptInfo *rel,
Expr *subclause,
- List *indices,
IndexOptInfo **retIndexInfo, /* return value */
List **retIndexQual, /* return value */
Cost *retStartupCost, /* return value */
Cost *retTotalCost) /* return value */
{
- bool first_time = true;
+ bool found = false;
List *ilist;
- /* if we don't match anything, return zeros */
- *retIndexInfo = NULL;
- *retIndexQual = NIL;
- *retStartupCost = 0;
- *retTotalCost = 0;
-
- foreach(ilist, indices)
+ foreach(ilist, rel->indexlist)
{
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
- List *indexqual;
+ List *qualrinfos;
+ List *indexquals;
Path subclause_path;
- Assert(IsA(index, IndexOptInfo));
+ /* Ignore partial indexes that do not match the query */
+ if (index->indpred != NIL && !index->predOK)
+ continue;
- /* Convert this 'or' subclause to an indexqual list */
- indexqual = extract_or_indexqual_conditions(rel, index, subclause);
+ /* Collect index clauses usable with this index */
+ qualrinfos = group_clauses_by_indexkey_for_or(rel, index, subclause);
- cost_index(&subclause_path, root, rel, index, indexqual, false);
+ /* Ignore index if it doesn't match the subclause at all */
+ if (qualrinfos == NIL)
+ continue;
- if (first_time || subclause_path.total_cost < *retTotalCost)
+ /* 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)
{
*retIndexInfo = index;
- *retIndexQual = indexqual;
+ *retIndexQual = indexquals;
*retStartupCost = subclause_path.startup_cost;
*retTotalCost = subclause_path.total_cost;
- first_time = false;
+ found = true;
}
}
+
+ return found;
}