aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/nodes/copyfuncs.c4
-rw-r--r--src/backend/nodes/outfuncs.c4
-rw-r--r--src/backend/optimizer/path/indxpath.c430
-rw-r--r--src/backend/optimizer/path/orindxpath.c268
-rw-r--r--src/backend/optimizer/plan/initsplan.c64
-rw-r--r--src/backend/optimizer/prep/prepunion.c19
-rw-r--r--src/backend/optimizer/util/plancat.c3
-rw-r--r--src/backend/optimizer/util/restrictinfo.c127
-rw-r--r--src/include/nodes/relation.h21
-rw-r--r--src/include/optimizer/paths.h8
-rw-r--r--src/include/optimizer/restrictinfo.h3
11 files changed, 448 insertions, 503 deletions
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 8b96f77a731..7cc052182d5 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.270 2003/12/30 23:53:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.271 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1172,7 +1172,7 @@ _copyRestrictInfo(RestrictInfo *from)
COPY_SCALAR_FIELD(canjoin);
COPY_BITMAPSET_FIELD(left_relids);
COPY_BITMAPSET_FIELD(right_relids);
- COPY_NODE_FIELD(subclauseindices); /* XXX probably bad */
+ COPY_NODE_FIELD(orclause);
COPY_SCALAR_FIELD(eval_cost);
COPY_SCALAR_FIELD(this_selec);
COPY_SCALAR_FIELD(mergejoinoperator);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6b8a8a4a874..e697196a287 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.223 2003/12/30 23:53:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.224 2004/01/04 00:07:32 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@@ -1077,7 +1077,7 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
WRITE_BOOL_FIELD(canjoin);
WRITE_BITMAPSET_FIELD(left_relids);
WRITE_BITMAPSET_FIELD(right_relids);
- WRITE_NODE_FIELD(subclauseindices);
+ WRITE_NODE_FIELD(orclause);
WRITE_OID_FIELD(mergejoinoperator);
WRITE_OID_FIELD(left_sortop);
WRITE_OID_FIELD(right_sortop);
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index c2fe837db65..353fcc6130f 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.151 2003/12/30 23:53:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.152 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -50,14 +50,6 @@
(indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)
-static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index,
- List *restrictinfo_list);
-static List *match_index_orclause(RelOptInfo *rel, IndexOptInfo *index,
- List *or_clauses,
- List *other_matching_indices);
-static bool match_or_subclause_to_indexkey(RelOptInfo *rel,
- IndexOptInfo *index,
- Expr *clause);
static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index);
static List *group_clauses_by_indexkey_for_join(Query *root,
RelOptInfo *rel, IndexOptInfo *index,
@@ -65,7 +57,7 @@ static List *group_clauses_by_indexkey_for_join(Query *root,
JoinType jointype, bool isouterjoin);
static bool match_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
int indexcol, Oid opclass,
- Expr *clause, RestrictInfo *rinfo);
+ RestrictInfo *rinfo);
static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
int indexcol, Oid opclass,
RestrictInfo *rinfo);
@@ -145,33 +137,20 @@ create_index_paths(Query *root, RelOptInfo *rel)
* predicate test.
*/
if (index->indpred != NIL)
+ {
if (!pred_test(index->indpred, restrictinfo_list, joininfo_list))
continue;
+ index->predOK = true; /* set flag for orindxpaths.c */
+ }
/*
- * 1. Try matching the index against subclauses of restriction
- * 'or' clauses (ie, 'or' clauses that reference only this
- * relation). The restrictinfo nodes for the 'or' clauses are
- * marked with lists of the matching indices. No paths are
- * actually created now; that will be done in orindxpath.c after
- * all indexes for the rel have been examined. (We need to do it
- * that way because we can potentially use a different index for
- * each subclause of an 'or', so we can't build a path for an 'or'
- * clause until all indexes have been matched against it.)
- *
- * We don't even think about special handling of 'or' clauses that
- * involve more than one relation (ie, are join clauses). Can we
- * do anything useful with those?
- */
- match_index_orclauses(rel, index, restrictinfo_list);
-
- /*
- * 2. Match the index against non-'or' restriction clauses.
+ * 1. Match the index against non-OR restriction clauses.
+ * (OR clauses will be considered later by orindxpath.c.)
*/
restrictclauses = group_clauses_by_indexkey(rel, index);
/*
- * 3. Compute pathkeys describing index's ordering, if any, then
+ * 2. Compute pathkeys describing index's ordering, if any, then
* see how many of them are actually useful for this query.
*/
index_pathkeys = build_index_pathkeys(root, rel, index,
@@ -181,7 +160,7 @@ create_index_paths(Query *root, RelOptInfo *rel)
index_pathkeys);
/*
- * 4. Generate an indexscan path if there are relevant restriction
+ * 3. Generate an indexscan path if there are relevant restriction
* clauses OR the index ordering is potentially useful for later
* merging or final output ordering.
*
@@ -201,7 +180,7 @@ create_index_paths(Query *root, RelOptInfo *rel)
NoMovementScanDirection));
/*
- * 5. If the index is ordered, a backwards scan might be
+ * 4. If the index is ordered, a backwards scan might be
* interesting. Currently this is only possible for a DESC query
* result ordering.
*/
@@ -220,7 +199,7 @@ create_index_paths(Query *root, RelOptInfo *rel)
}
/*
- * 6. Examine join clauses to see which ones are potentially
+ * 5. Examine join clauses to see which ones are potentially
* usable with this index, and generate the set of all other
* relids that participate in such join clauses. We'll use this
* set later to recognize outer rels that are equivalent for
@@ -238,269 +217,6 @@ create_index_paths(Query *root, RelOptInfo *rel)
/****************************************************************************
- * ---- ROUTINES TO PROCESS 'OR' CLAUSES ----
- ****************************************************************************/
-
-
-/*
- * match_index_orclauses
- * Attempt to match an index against subclauses within 'or' clauses.
- * Each subclause that does match is marked with the index's node.
- *
- * Essentially, this adds 'index' to the list of subclause indices in
- * the RestrictInfo field of each of the 'or' clauses where it matches.
- * NOTE: we can use storage in the RestrictInfo for this purpose because
- * this processing is only done on single-relation restriction clauses.
- * Therefore, we will never have indexes for more than one relation
- * mentioned in the same RestrictInfo node's list.
- *
- * 'rel' is the node of the relation on which the index is defined.
- * 'index' is the index node.
- * 'restrictinfo_list' is the list of available restriction clauses.
- */
-static void
-match_index_orclauses(RelOptInfo *rel,
- IndexOptInfo *index,
- List *restrictinfo_list)
-{
- List *i;
-
- foreach(i, restrictinfo_list)
- {
- RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
-
- if (restriction_is_or_clause(restrictinfo))
- {
- /*
- * Add this index to the subclause index list for each
- * subclause that it matches.
- */
- restrictinfo->subclauseindices =
- match_index_orclause(rel, index,
- ((BoolExpr *) restrictinfo->clause)->args,
- restrictinfo->subclauseindices);
- }
- }
-}
-
-/*
- * match_index_orclause
- * Attempts to match an index against the subclauses of an 'or' clause.
- *
- * A match means that:
- * (1) the operator within the subclause can be used with the
- * index's specified operator class, and
- * (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
- * 'other_matching_indices' is the list of information on other indices
- * that have already been matched to subclauses within this
- * particular 'or' clause (i.e., a list previously generated by
- * this routine), or NIL if this routine has not previously been
- * run for this 'or' clause.
- *
- * Returns a list of the form ((a b c) (d e f) nil (g h) ...) where
- * a,b,c are nodes of indices that match the first subclause in
- * 'or-clauses', d,e,f match the second subclause, no indices
- * match the third, g,h match the fourth, etc.
- */
-static List *
-match_index_orclause(RelOptInfo *rel,
- IndexOptInfo *index,
- List *or_clauses,
- List *other_matching_indices)
-{
- List *matching_indices;
- List *index_list;
- List *clist;
-
- /*
- * first time through, we create list of same length as OR clause,
- * containing an empty sublist for each subclause.
- */
- if (!other_matching_indices)
- {
- matching_indices = NIL;
- foreach(clist, or_clauses)
- matching_indices = lcons(NIL, matching_indices);
- }
- else
- matching_indices = other_matching_indices;
-
- index_list = matching_indices;
-
- foreach(clist, or_clauses)
- {
- Expr *clause = lfirst(clist);
-
- if (match_or_subclause_to_indexkey(rel, index, clause))
- {
- /* OK to add this index to sublist for this subclause */
- lfirst(matching_indices) = lcons(index,
- lfirst(matching_indices));
- }
-
- matching_indices = lnext(matching_indices);
- }
-
- return index_list;
-}
-
-/*
- * See if a subclause of an OR clause matches an index.
- *
- * We accept the subclause if it is an operator clause that matches the
- * index, or if it is an AND clause any of whose members is an opclause
- * that matches the index.
- *
- * For multi-key indexes, we only look for matches to the first key;
- * without such a match the index is useless. If the clause is an AND
- * then we may be able to extract additional subclauses to use with the
- * later indexkeys, but we need not worry about that until
- * extract_or_indexqual_conditions() is called (if it ever is).
- */
-static bool
-match_or_subclause_to_indexkey(RelOptInfo *rel,
- IndexOptInfo *index,
- Expr *clause)
-{
- Oid opclass = index->classlist[0];
-
- if (and_clause((Node *) clause))
- {
- List *item;
-
- foreach(item, ((BoolExpr *) clause)->args)
- {
- if (match_clause_to_indexcol(rel, index, 0, opclass,
- lfirst(item), NULL))
- return true;
- }
- return false;
- }
- else
- return match_clause_to_indexcol(rel, index, 0, opclass,
- clause, NULL);
-}
-
-/*----------
- * 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 can look at other restriction clauses of the rel to discover
- * additional candidate indexquals: for example, consider
- * ... where (a = 11 or a = 12) and b = 42;
- * If we are dealing with an index on (a,b) then we can include the clause
- * b = 42 in the indexqual list generated for each of the OR subclauses.
- * Essentially, we are making an index-specific transformation from CNF to
- * DNF. (NOTE: when we do this, we end up with a slightly inefficient plan
- * because create_indexscan_plan is not very bright about figuring out which
- * restriction clauses are implied by the generated indexqual condition.
- * Currently we'll end up rechecking both the OR clause and the transferred
- * restriction clause as qpquals. FIXME someday.)
- *
- * Also, we apply expand_indexqual_condition() 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)
-{
- FastList quals;
- int indexcol = 0;
- Oid *classes = index->classlist;
-
- FastListInit(&quals);
-
- /*
- * Extract relevant indexclauses in indexkey order. This is
- * essentially just like group_clauses_by_indexkey() except that the
- * input and output are lists of bare clauses, not of RestrictInfo
- * nodes, and that we expand special operators immediately.
- */
- do
- {
- Oid curClass = classes[0];
- FastList clausegroup;
- List *item;
-
- FastListInit(&clausegroup);
- if (and_clause((Node *) orsubclause))
- {
- foreach(item, ((BoolExpr *) orsubclause)->args)
- {
- Expr *subsubclause = (Expr *) lfirst(item);
-
- if (match_clause_to_indexcol(rel, index,
- indexcol, curClass,
- subsubclause, NULL))
- FastConc(&clausegroup,
- expand_indexqual_condition(subsubclause,
- curClass));
- }
- }
- else if (match_clause_to_indexcol(rel, index,
- indexcol, curClass,
- orsubclause, NULL))
- FastConc(&clausegroup,
- expand_indexqual_condition(orsubclause,
- curClass));
-
- /*
- * If we found no clauses for this indexkey in the OR subclause
- * itself, try looking in the rel's top-level restriction list.
- */
- if (FastListValue(&clausegroup) == NIL)
- {
- foreach(item, rel->baserestrictinfo)
- {
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
-
- if (match_clause_to_indexcol(rel, index,
- indexcol, curClass,
- rinfo->clause, rinfo))
- FastConc(&clausegroup,
- expand_indexqual_condition(rinfo->clause,
- curClass));
- }
- }
-
- /*
- * If still no clauses match this key, we're done; we don't want
- * to look at keys to its right.
- */
- if (FastListValue(&clausegroup) == NIL)
- break;
-
- FastConcFast(&quals, &clausegroup);
-
- indexcol++;
- classes++;
-
- } while (!DoneMatchingIndexKeys(classes));
-
- if (FastListValue(&quals) == NIL)
- elog(ERROR, "no matching OR clause");
-
- return FastListValue(&quals);
-}
-
-
-/****************************************************************************
* ---- ROUTINES TO CHECK RESTRICTIONS ----
****************************************************************************/
@@ -552,7 +268,6 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
index,
indexcol,
curClass,
- rinfo->clause,
rinfo))
FastAppend(&clausegroup, rinfo);
}
@@ -628,7 +343,6 @@ group_clauses_by_indexkey_for_join(Query *root,
index,
indexcol,
curClass,
- rinfo->clause,
rinfo))
FastAppend(&clausegroup, rinfo);
}
@@ -708,6 +422,114 @@ group_clauses_by_indexkey_for_join(Query *root,
/*
+ * group_clauses_by_indexkey_for_or
+ * Generate a list of sublists of clauses that can be used with an index
+ * to find rows matching an OR subclause.
+ *
+ * This is essentially just like group_clauses_by_indexkey() except that
+ * we can use the given clause (or any AND subclauses of it) as well as
+ * top-level restriction clauses of the relation. Furthermore, we demand
+ * that at least one such use be made, otherwise we fail and return NIL.
+ * (Any path we made without such a use would be redundant with non-OR
+ * indexscans. Compare also group_clauses_by_indexkey_for_join.)
+ *
+ * XXX When we generate an indexqual list that uses both the OR subclause
+ * and top-level restriction clauses, we end up with a slightly inefficient
+ * plan because create_indexscan_plan is not very bright about figuring out
+ * which restriction clauses are implied by the generated indexqual condition.
+ * Currently we'll end up rechecking both the OR clause and the top-level
+ * restriction clause as qpquals. FIXME someday.
+ */
+List *
+group_clauses_by_indexkey_for_or(RelOptInfo *rel,
+ IndexOptInfo *index,
+ Expr *orsubclause)
+{
+ FastList clausegroup_list;
+ bool matched = false;
+ int indexcol = 0;
+ Oid *classes = index->classlist;
+
+ FastListInit(&clausegroup_list);
+ do
+ {
+ Oid curClass = classes[0];
+ FastList clausegroup;
+ List *item;
+
+ FastListInit(&clausegroup);
+
+ /* Try to match the OR subclause to the index key */
+ if (IsA(orsubclause, RestrictInfo))
+ {
+ if (match_clause_to_indexcol(rel, index,
+ indexcol, curClass,
+ (RestrictInfo *) orsubclause))
+ {
+ FastAppend(&clausegroup, orsubclause);
+ matched = true;
+ }
+ }
+ else if (and_clause((Node *) orsubclause))
+ {
+ foreach(item, ((BoolExpr *) orsubclause)->args)
+ {
+ RestrictInfo *subsubclause = (RestrictInfo *) lfirst(item);
+
+ if (IsA(subsubclause, RestrictInfo) &&
+ match_clause_to_indexcol(rel, index,
+ indexcol, curClass,
+ subsubclause))
+ {
+ FastAppend(&clausegroup, subsubclause);
+ matched = true;
+ }
+ }
+ }
+
+ /*
+ * If we found no clauses for this indexkey in the OR subclause
+ * itself, try looking in the rel's top-level restriction list.
+ *
+ * XXX should we always search the top-level list? Slower but
+ * could sometimes yield a better plan.
+ */
+ if (FastListValue(&clausegroup) == NIL)
+ {
+ foreach(item, rel->baserestrictinfo)
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
+
+ if (match_clause_to_indexcol(rel, index,
+ indexcol, curClass,
+ rinfo))
+ FastAppend(&clausegroup, rinfo);
+ }
+ }
+
+ /*
+ * If still no clauses match this key, we're done; we don't want
+ * to look at keys to its right.
+ */
+ if (FastListValue(&clausegroup) == NIL)
+ break;
+
+ FastAppend(&clausegroup_list, FastListValue(&clausegroup));
+
+ indexcol++;
+ classes++;
+
+ } while (!DoneMatchingIndexKeys(classes));
+
+ /* if OR clause was not used then forget it, per comments above */
+ if (!matched)
+ return NIL;
+
+ return FastListValue(&clausegroup_list);
+}
+
+
+/*
* match_clause_to_indexcol()
* Determines whether a restriction clause matches a column of an index.
*
@@ -729,8 +551,7 @@ group_clauses_by_indexkey_for_join(Query *root,
* 'index' is an index on 'rel'.
* 'indexcol' is a column number of 'index' (counting from 0).
* 'opclass' is the corresponding operator class.
- * 'clause' is the clause to be tested.
- * 'rinfo' is the clause's RestrictInfo, if available (NULL if not).
+ * 'rinfo' is the clause to be tested (as a RestrictInfo node).
*
* Returns true if the clause can be used with this index key.
*
@@ -742,9 +563,9 @@ match_clause_to_indexcol(RelOptInfo *rel,
IndexOptInfo *index,
int indexcol,
Oid opclass,
- Expr *clause,
RestrictInfo *rinfo)
{
+ Expr *clause = rinfo->clause;
Node *leftop,
*rightop;
@@ -760,13 +581,9 @@ match_clause_to_indexcol(RelOptInfo *rel,
* Check for clauses of the form: (indexkey operator constant) or
* (constant operator indexkey). Anything that is a "pseudo constant"
* expression will do.
- *
- * If we have the RestrictInfo available, we can make a more efficient
- * test for pseudo-constness.
*/
if (match_index_to_operand(leftop, indexcol, rel, index) &&
- (rinfo ? is_pseudo_constant_clause_relids(rightop, rinfo->right_relids)
- : is_pseudo_constant_clause(rightop)))
+ is_pseudo_constant_clause_relids(rightop, rinfo->right_relids))
{
if (is_indexable_operator(clause, opclass, true))
return true;
@@ -781,8 +598,7 @@ match_clause_to_indexcol(RelOptInfo *rel,
}
if (match_index_to_operand(rightop, indexcol, rel, index) &&
- (rinfo ? is_pseudo_constant_clause_relids(leftop, rinfo->left_relids)
- : is_pseudo_constant_clause(leftop)))
+ is_pseudo_constant_clause_relids(leftop, rinfo->left_relids))
{
if (is_indexable_operator(clause, opclass, false))
return true;
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;
}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 3da001e997a..e170058c935 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.94 2003/12/30 23:53:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.95 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,6 +23,7 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
@@ -373,31 +374,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
Relids outerjoin_nonnullable,
Relids qualscope)
{
- RestrictInfo *restrictinfo = makeNode(RestrictInfo);
- RelOptInfo *rel;
Relids relids;
List *vars;
bool can_be_equijoin;
-
- restrictinfo->clause = (Expr *) clause;
- restrictinfo->canjoin = false; /* set below, if join clause */
- restrictinfo->left_relids = NULL;
- restrictinfo->right_relids = NULL;
- restrictinfo->subclauseindices = NIL;
- restrictinfo->eval_cost.startup = -1; /* not computed until
- * needed */
- restrictinfo->this_selec = -1; /* not computed until needed */
- restrictinfo->mergejoinoperator = InvalidOid;
- restrictinfo->left_sortop = InvalidOid;
- restrictinfo->right_sortop = InvalidOid;
- restrictinfo->left_pathkey = NIL; /* not computable yet */
- restrictinfo->right_pathkey = NIL;
- restrictinfo->left_mergescansel = -1; /* not computed until
- * needed */
- restrictinfo->right_mergescansel = -1;
- restrictinfo->hashjoinoperator = InvalidOid;
- restrictinfo->left_bucketsize = -1; /* not computed until needed */
- restrictinfo->right_bucketsize = -1;
+ RestrictInfo *restrictinfo;
+ RelOptInfo *rel;
/*
* Retrieve all relids and vars contained within the clause.
@@ -508,18 +489,17 @@ distribute_qual_to_rels(Query *root, Node *clause,
* same joinrel. A qual originating from WHERE is always considered
* "pushed down".
*/
- restrictinfo->ispusheddown = ispusheddown || !bms_equal(relids,
- qualscope);
+ if (!ispusheddown)
+ ispusheddown = !bms_equal(relids, qualscope);
/*
- * If it's a binary opclause, set up left/right relids info.
+ * Build the RestrictInfo node itself.
*/
- if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2)
- {
- restrictinfo->left_relids = pull_varnos(get_leftop((Expr *) clause));
- restrictinfo->right_relids = pull_varnos(get_rightop((Expr *) clause));
- }
+ restrictinfo = make_restrictinfo((Expr *) clause, ispusheddown);
+ /*
+ * Figure out where to attach it.
+ */
switch (bms_membership(relids))
{
case BMS_SINGLETON:
@@ -553,7 +533,8 @@ distribute_qual_to_rels(Query *root, Node *clause,
* into a join rel's restriction list.)
*/
if (!isdeduced ||
- !qual_is_redundant(root, restrictinfo, rel->baserestrictinfo))
+ !qual_is_redundant(root, restrictinfo,
+ rel->baserestrictinfo))
{
/* Add clause to rel's restriction list */
rel->baserestrictinfo = lappend(rel->baserestrictinfo,
@@ -564,23 +545,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
/*
* 'clause' is a join clause, since there is more than one rel
- * in the relid set. Set additional RestrictInfo fields for
- * joining. First, does it look like a normal join clause,
- * i.e., a binary operator relating expressions that come from
- * distinct relations? If so we might be able to use it in a
- * join algorithm.
+ * in the relid set.
*/
- if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2)
- {
- if (!bms_is_empty(restrictinfo->left_relids) &&
- !bms_is_empty(restrictinfo->right_relids) &&
- !bms_overlap(restrictinfo->left_relids,
- restrictinfo->right_relids))
- restrictinfo->canjoin = true;
- }
/*
- * Now check for hash or mergejoinable operators.
+ * Check for hash or mergejoinable operators.
*
* We don't bother setting the hashjoin info if we're not going
* to need it. We do want to know about mergejoinable ops in
@@ -624,7 +593,8 @@ distribute_qual_to_rels(Query *root, Node *clause,
* equivalence for future use. (We can skip this for a deduced
* clause, since the keys are already known equivalent in that case.)
*/
- if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid &&
+ if (can_be_equijoin &&
+ restrictinfo->mergejoinoperator != InvalidOid &&
!isdeduced)
add_equijoined_keys(root, restrictinfo);
}
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 1cecdb6c423..6866e95e4b3 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.105 2003/11/29 19:51:51 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.106 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -881,12 +881,9 @@ adjust_inherited_attrs_mutator(Node *node,
newinfo->clause = (Expr *)
adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
- /*
- * We do NOT want to copy the original subclauseindices list,
- * since the new rel will have different indices. The list will
- * be rebuilt when needed during later planning.
- */
- newinfo->subclauseindices = NIL;
+ /* and the modified version, if an OR clause */
+ newinfo->orclause = (Expr *)
+ adjust_inherited_attrs_mutator((Node *) oldinfo->orclause, context);
/*
* Adjust left/right relid sets too.
@@ -898,9 +895,13 @@ adjust_inherited_attrs_mutator(Node *node,
context->old_rt_index,
context->new_rt_index);
- newinfo->eval_cost.startup = -1; /* reset these too */
+ /*
+ * Reset cached derivative fields, since these might need to have
+ * different values when considering the child relation.
+ */
+ newinfo->eval_cost.startup = -1;
newinfo->this_selec = -1;
- newinfo->left_pathkey = NIL; /* and these */
+ newinfo->left_pathkey = NIL;
newinfo->right_pathkey = NIL;
newinfo->left_mergescansel = -1;
newinfo->right_mergescansel = -1;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index e025fd7bde8..37067dd4d42 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.90 2003/11/29 19:51:51 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.91 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -146,6 +146,7 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
ChangeVarNodes((Node *) info->indexprs, 1, varno, 0);
if (info->indpred && varno != 1)
ChangeVarNodes((Node *) info->indpred, 1, varno, 0);
+ info->predOK = false; /* set later in indxpath.c */
info->unique = index->indisunique;
/* initialize cached join info to empty */
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index f77966a2262..a7c9ec85f72 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.21 2003/12/30 23:53:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.22 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,6 +20,7 @@
#include "optimizer/var.h"
+static Expr *make_sub_restrictinfos(Expr *clause, bool ispusheddown);
static bool join_clause_is_redundant(Query *root,
RestrictInfo *rinfo,
List *reference_list,
@@ -27,6 +28,127 @@ static bool join_clause_is_redundant(Query *root,
/*
+ * make_restrictinfo
+ *
+ * Build a RestrictInfo node containing the given subexpression.
+ *
+ * The ispusheddown flag must be supplied by the caller. We initialize
+ * fields that depend only on the given subexpression, leaving others that
+ * depend on context (or may never be needed at all) to be filled later.
+ */
+RestrictInfo *
+make_restrictinfo(Expr *clause, bool ispusheddown)
+{
+ RestrictInfo *restrictinfo = makeNode(RestrictInfo);
+
+ restrictinfo->clause = clause;
+ restrictinfo->ispusheddown = ispusheddown;
+ restrictinfo->canjoin = false; /* may get set below */
+
+ /*
+ * If it's a binary opclause, set up left/right relids info.
+ */
+ if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2)
+ {
+ restrictinfo->left_relids = pull_varnos(get_leftop(clause));
+ restrictinfo->right_relids = pull_varnos(get_rightop(clause));
+
+ /*
+ * Does it look like a normal join clause, i.e., a binary operator
+ * relating expressions that come from distinct relations? If so
+ * we might be able to use it in a join algorithm. Note that this
+ * is a purely syntactic test that is made regardless of context.
+ */
+ if (!bms_is_empty(restrictinfo->left_relids) &&
+ !bms_is_empty(restrictinfo->right_relids) &&
+ !bms_overlap(restrictinfo->left_relids,
+ restrictinfo->right_relids))
+ restrictinfo->canjoin = true;
+ }
+ else
+ {
+ /* Not a binary opclause, so mark both relid sets as empty */
+ restrictinfo->left_relids = NULL;
+ restrictinfo->right_relids = NULL;
+ }
+
+ /*
+ * If it's an OR clause, set up a modified copy with RestrictInfos
+ * inserted above each subclause of the top-level AND/OR structure.
+ */
+ if (or_clause((Node *) clause))
+ {
+ restrictinfo->orclause = make_sub_restrictinfos(clause, ispusheddown);
+ }
+ else
+ {
+ /* Shouldn't be an AND clause, else flatten_andors messed up */
+ Assert(!and_clause((Node *) clause));
+
+ restrictinfo->orclause = NULL;
+ }
+
+ /*
+ * Fill in all the cacheable fields with "not yet set" markers.
+ * None of these will be computed until/unless needed. Note in
+ * particular that we don't mark a binary opclause as mergejoinable
+ * or hashjoinable here; that happens only if it appears in the right
+ * context (top level of a joinclause list).
+ */
+ restrictinfo->eval_cost.startup = -1;
+ restrictinfo->this_selec = -1;
+
+ restrictinfo->mergejoinoperator = InvalidOid;
+ restrictinfo->left_sortop = InvalidOid;
+ restrictinfo->right_sortop = InvalidOid;
+
+ restrictinfo->left_pathkey = NIL;
+ restrictinfo->right_pathkey = NIL;
+
+ restrictinfo->left_mergescansel = -1;
+ restrictinfo->right_mergescansel = -1;
+
+ restrictinfo->hashjoinoperator = InvalidOid;
+
+ restrictinfo->left_bucketsize = -1;
+ restrictinfo->right_bucketsize = -1;
+
+ return restrictinfo;
+}
+
+/*
+ * Recursively insert sub-RestrictInfo nodes into a boolean expression.
+ */
+static Expr *
+make_sub_restrictinfos(Expr *clause, bool ispusheddown)
+{
+ if (or_clause((Node *) clause))
+ {
+ List *orlist = NIL;
+ List *temp;
+
+ foreach(temp, ((BoolExpr *) clause)->args)
+ orlist = lappend(orlist,
+ make_sub_restrictinfos(lfirst(temp),
+ ispusheddown));
+ return make_orclause(orlist);
+ }
+ else if (and_clause((Node *) clause))
+ {
+ List *andlist = NIL;
+ List *temp;
+
+ foreach(temp, ((BoolExpr *) clause)->args)
+ andlist = lappend(andlist,
+ make_sub_restrictinfos(lfirst(temp),
+ ispusheddown));
+ return make_andclause(andlist);
+ }
+ else
+ return (Expr *) make_restrictinfo(clause, ispusheddown);
+}
+
+/*
* restriction_is_or_clause
*
* Returns t iff the restrictinfo node contains an 'or' clause.
@@ -34,8 +156,7 @@ static bool join_clause_is_redundant(Query *root,
bool
restriction_is_or_clause(RestrictInfo *restrictinfo)
{
- if (restrictinfo != NULL &&
- or_clause((Node *) restrictinfo->clause))
+ if (restrictinfo->orclause != NULL)
return true;
else
return false;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 0d129f38ea1..af852725737 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.88 2003/12/30 23:53:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.89 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -271,6 +271,8 @@ typedef struct IndexOptInfo
List *indexprs; /* expressions for non-simple index
* columns */
List *indpred; /* predicate if a partial index, else NIL */
+
+ bool predOK; /* true if predicate matches query */
bool unique; /* true if a unique index */
/* cached info about inner indexscan paths for index */
@@ -410,6 +412,8 @@ typedef struct AppendPath
* variable-free targetlist or to gate execution of a subplan with a
* one-time (variable-free) qual condition. Note that in the former case
* path.parent will be NULL; in the latter case it is copied from the subpath.
+ *
+ * Note that constantqual is a list of bare clauses, not RestrictInfos.
*/
typedef struct ResultPath
{
@@ -478,9 +482,7 @@ typedef JoinPath NestPath;
* A mergejoin path has these fields.
*
* path_mergeclauses lists the clauses (in the form of RestrictInfos)
- * that will be used in the merge. (Before 7.0, this was a list of bare
- * clause expressions, but we can save on list memory and cost_qual_eval
- * work by leaving it in the form of a RestrictInfo list.)
+ * that will be used in the merge.
*
* Note that the mergeclauses are a subset of the parent relation's
* restriction-clause list. Any join clauses that are not mergejoinable
@@ -586,6 +588,12 @@ typedef struct HashPath
* qual-expression-evaluation code. (But we are still entitled to count
* their selectivity when estimating the result tuple count, if we
* can guess what it is...)
+ *
+ * When the referenced clause is an OR clause, we generate a modified copy
+ * in which additional RestrictInfo nodes are inserted below the top-level
+ * OR/AND structure. This is a convenience for OR indexscan processing:
+ * indexquals taken from either the top level or an OR subclause will have
+ * associated RestrictInfo nodes.
*/
typedef struct RestrictInfo
@@ -609,9 +617,8 @@ typedef struct RestrictInfo
Relids left_relids; /* relids in left side of clause */
Relids right_relids; /* relids in right side of clause */
- /* only used if clause is an OR clause: */
- List *subclauseindices; /* indexes matching subclauses */
- /* subclauseindices is a List of Lists of IndexOptInfos */
+ /* This field is NULL unless clause is an OR clause: */
+ Expr *orclause; /* modified clause with RestrictInfos */
/* cache space for costs (currently only used for join clauses) */
QualCost eval_cost; /* eval cost of clause; -1 if not yet set */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 6e5ea0551f2..3e60d608020 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.70 2003/11/29 22:41:07 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.71 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,9 +38,9 @@ extern void debug_print_rel(Query *root, RelOptInfo *rel);
extern void create_index_paths(Query *root, RelOptInfo *rel);
extern Path *best_inner_indexscan(Query *root, RelOptInfo *rel,
Relids outer_relids, JoinType jointype);
-extern List *extract_or_indexqual_conditions(RelOptInfo *rel,
- IndexOptInfo *index,
- Expr *orsubclause);
+extern List *group_clauses_by_indexkey_for_or(RelOptInfo *rel,
+ IndexOptInfo *index,
+ Expr *orsubclause);
extern List *expand_indexqual_conditions(IndexOptInfo *index,
List *clausegroups);
diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h
index 640e5e16aa9..362ed26a61b 100644
--- a/src/include/optimizer/restrictinfo.h
+++ b/src/include/optimizer/restrictinfo.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.20 2003/11/29 22:41:07 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.21 2004/01/04 00:07:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,6 +16,7 @@
#include "nodes/relation.h"
+extern RestrictInfo *make_restrictinfo(Expr *clause, bool ispusheddown);
extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
extern List *get_actual_clauses(List *restrictinfo_list);
extern void get_actual_join_clauses(List *restrictinfo_list,