*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.86 1999/07/17 20:17:05 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.87 1999/07/24 23:21:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Node_Copy(from, newnode, clause);
newnode->selectivity = from->selectivity;
- newnode->notclause = from->notclause;
Node_Copy(from, newnode, indexids);
Node_Copy(from, newnode, mergejoinorder);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.43 1999/07/17 20:17:05 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.44 1999/07/24 23:21:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return false;
if (a->selectivity != b->selectivity)
return false;
- if (a->notclause != b->notclause)
- return false;
#ifdef EqualMergeOrderExists
if (!EqualMergeOrder(a->mergejoinorder, b->mergejoinorder))
return false;
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: outfuncs.c,v 1.90 1999/07/18 19:02:49 tgl Exp $
+ * $Id: outfuncs.c,v 1.91 1999/07/24 23:21:07 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
_outNode(str, node->clause);
appendStringInfo(str,
- " :selectivity %f :notclause %s :indexids ",
- node->selectivity,
- node->notclause ? "true" : "false");
+ " :selectivity %f :indexids ",
+ node->selectivity);
_outNode(str, node->indexids);
appendStringInfo(str, " :mergejoinorder ");
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.69 1999/07/17 20:17:08 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.70 1999/07/24 23:21:08 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
local_node->selectivity = atof(token);
- token = lsptok(NULL, &length); /* get :notclause */
- token = lsptok(NULL, &length); /* now read it */
-
- if (!strncmp(token, "true", 4))
- local_node->notclause = true;
- else
- local_node->notclause = false;
-
token = lsptok(NULL, &length); /* get :indexids */
local_node->indexids = nodeRead(true); /* now read it */
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.50 1999/07/17 20:17:11 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.51 1999/07/24 23:21:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* set_base_rel_pathlist
* Finds all paths available for scanning each relation entry in
* 'rels'. Sequential scan and any available indices are considered
- * if possible(indices are not considered for lower nesting levels).
- * All unique paths are attached to the relation's 'pathlist' field.
+ * if possible (indices are not considered for lower nesting levels).
+ * All useful paths are attached to the relation's 'pathlist' field.
*
* MODIFIES: rels
*/
foreach(temp, rels)
{
+ RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
+ List *indices = find_relation_indices(root, rel);
List *sequential_scan_list;
List *rel_index_scan_list;
List *or_index_scan_list;
- RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
sequential_scan_list = lcons(create_seqscan_path(rel), NIL);
rel_index_scan_list = create_index_paths(root,
rel,
- find_relation_indices(root, rel),
+ indices,
rel->restrictinfo,
rel->joininfo);
- or_index_scan_list = create_or_index_paths(root, rel, rel->restrictinfo);
+ /* Note: create_or_index_paths depends on create_index_paths
+ * to have marked OR restriction clauses with relevant indices;
+ * this is why it doesn't need to be given the full list of indices.
+ */
+ or_index_scan_list = create_or_index_paths(root, rel,
+ rel->restrictinfo);
+
+ /* add_pathlist will discard any paths that are dominated by
+ * another available path, keeping only those paths that are
+ * superior along at least one dimension of cost or sortedness.
+ */
rel->pathlist = add_pathlist(rel,
sequential_scan_list,
nconc(rel_index_scan_list,
rel->size = compute_rel_size(rel);
rel->width = compute_rel_width(rel);
}
- return;
}
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.23 1999/07/16 04:59:14 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.24 1999/07/24 23:21:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "utils/lsyscache.h"
-static Cost compute_selec(Query *root, List *clauses, List *or_selectivities);
-
/****************************************************************************
* ROUTINES TO SET CLAUSE SELECTIVITIES
****************************************************************************/
/*
* set_clause_selectivities -
- * Sets the selectivity field for each of clause in 'restrictinfo-list'
- * to 'new-selectivity'. If the selectivity has already been set, reset
- * it only if the new one is better.
- *
- * Returns nothing of interest.
- *
+ * Sets the selectivity field for each clause in 'restrictinfo-list'
+ * to 'new-selectivity'. If the selectivity has already been set,
+ * change it only if the new one is better.
*/
void
set_clause_selectivities(List *restrictinfo_list, Cost new_selectivity)
{
- List *temp;
- RestrictInfo *clausenode;
- Cost cost_clause;
+ List *rlist;
- foreach(temp, restrictinfo_list)
+ foreach(rlist, restrictinfo_list)
{
- clausenode = (RestrictInfo *) lfirst(temp);
- cost_clause = clausenode->selectivity;
+ RestrictInfo *clausenode = (RestrictInfo *) lfirst(rlist);
+ Cost cost_clause = clausenode->selectivity;
+
if (cost_clause <= 0 || new_selectivity < cost_clause)
clausenode->selectivity = new_selectivity;
}
Cost
product_selec(List *restrictinfo_list)
{
- Cost result = 1.0;
+ Cost result = (Cost) 1.0;
+ List *rlist;
- if (restrictinfo_list != NIL)
+ foreach(rlist, restrictinfo_list)
{
- List *xclausenode = NIL;
- Cost temp;
-
- foreach(xclausenode, restrictinfo_list)
- {
- temp = ((RestrictInfo *) lfirst(xclausenode))->selectivity;
- result = result * temp;
- }
+ result *= ((RestrictInfo *) lfirst(rlist))->selectivity;
}
return result;
}
* Scans through clauses on each relation and assigns a selectivity to
* those clauses that haven't been assigned a selectivity by an index.
*
- * Returns nothing of interest.
- * MODIFIES: selectivities of the various rel's restrictinfo
- * slots.
+ * MODIFIES: selectivities of the various rel's restrictinfo slots.
*/
void
set_rest_relselec(Query *root, List *rel_list)
{
- RelOptInfo *rel;
List *x;
foreach(x, rel_list)
{
- rel = (RelOptInfo *) lfirst(x);
+ RelOptInfo *rel = (RelOptInfo *) lfirst(x);
set_rest_selec(root, rel->restrictinfo);
}
}
* set_rest_selec -
* Sets the selectivity fields for those clauses within a single
* relation's 'restrictinfo-list' that haven't already been set.
- *
- * Returns nothing of interest.
- *
*/
void
set_rest_selec(Query *root, List *restrictinfo_list)
{
- List *temp = NIL;
- RestrictInfo *clausenode = (RestrictInfo *) NULL;
- Cost cost_clause;
+ List *rlist;
- foreach(temp, restrictinfo_list)
+ foreach(rlist, restrictinfo_list)
{
- clausenode = (RestrictInfo *) lfirst(temp);
- cost_clause = clausenode->selectivity;
+ RestrictInfo *clause = (RestrictInfo *) lfirst(rlist);
- /*
- * Check to see if the selectivity of this clause or any 'or'
- * subclauses (if any) haven't been set yet.
- */
- if (cost_clause <= 0 || valid_or_clause(clausenode))
+ if (clause->selectivity <= 0)
{
- clausenode->selectivity = compute_clause_selec(root,
- (Node *) clausenode->clause,
- lcons(makeFloat(cost_clause), NIL));
+ clause->selectivity =
+ compute_clause_selec(root, (Node *) clause->clause);
}
}
}
/*
* compute_clause_selec -
- * Given a clause, this routine will compute the selectivity of the
- * clause by calling 'compute_selec' with the appropriate parameters
- * and possibly use that return value to compute the real selectivity
- * of a clause.
- *
- * 'or-selectivities' are selectivities that have already been assigned
- * to subclauses of an 'or' clause.
- *
- * Returns a flonum corresponding to the clause selectivity.
- *
- */
-Cost
-compute_clause_selec(Query *root, Node *clause, List *or_selectivities)
-{
- if (is_opclause(clause))
- return compute_selec(root, lcons(clause, NIL), or_selectivities);
- else if (not_clause(clause))
- {
-
- /*
- * 'not' gets "1.0 - selectivity-of-inner-clause".
- */
- return (1.000000 - compute_selec(root,
- lcons(get_notclausearg((Expr *) clause),
- NIL),
- or_selectivities));
- }
- else if (or_clause(clause))
- {
-
- /*
- * Both 'or' and 'and' clauses are evaluated as described in
- * (compute_selec).
- */
- return compute_selec(root, ((Expr *) clause)->args, or_selectivities);
- }
- else
- return compute_selec(root, lcons(clause, NIL), or_selectivities);
-}
-
-/*
- * compute_selec -
* Computes the selectivity of a clause.
- *
- * If there is more than one clause in the argument 'clauses', then the
- * desired selectivity is that of an 'or' clause. Selectivities for an
- * 'or' clause such as (OR a b) are computed by finding the selectivity
- * of a (s1) and b (s2) and computing s1+s2 - s1*s2.
- *
- * In addition, if the clause is an 'or' clause, individual selectivities
- * may have already been assigned by indices to subclauses. These values
- * are contained in the list 'or-selectivities'.
- *
- * Returns the clause selectivity as a flonum.
- *
*/
-static Cost
-compute_selec(Query *root, List *clauses, List *or_selectivities)
+Cost
+compute_clause_selec(Query *root, Node *clause)
{
- Cost s1 = 0;
- List *clause = lfirst(clauses);
+ Cost s1 = 1.0; /* default for any unhandled clause type */
if (clause == NULL)
- s1 = 1.0;
- else if (IsA(clause, Param))
- {
- /* XXX How're we handling this before?? -ay */
- s1 = 1.0;
- }
- else if (IsA(clause, Const))
- s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0;
- else if (IsA(clause, Var))
+ return s1;
+ if (IsA(clause, Var))
{
- Oid relid = getrelid(((Var *) clause)->varno,
- root->rtable);
-
/*
* we have a bool Var. This is exactly equivalent to the clause:
* reln.attribute = 't' so we compute the selectivity as if that
* is what we have. The magic #define constants are a hack. I
* didn't want to have to do system cache look ups to find out all
* of that info.
+ *
+ * XXX why are we using varno and varoattno? Seems like it should
+ * be varno/varattno or varnoold/varoattno, not mix & match...
*/
+ Oid relid = getrelid(((Var *) clause)->varno,
+ root->rtable);
s1 = restriction_selectivity(F_EQSEL,
BooleanEqualOperator,
"t",
_SELEC_CONSTANT_RIGHT_);
}
- else if (or_selectivities)
+ else if (IsA(clause, Param))
{
- /* If s1 has already been assigned by an index, use that value. */
- List *this_sel = lfirst(or_selectivities);
-
- s1 = floatVal(this_sel);
+ /* XXX any way to do better? */
+ s1 = 1.0;
}
- else if (is_funcclause((Node *) clause))
+ else if (IsA(clause, Const))
+ {
+ /* bool constant is pretty easy... */
+ s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0;
+ }
+ else if (not_clause(clause))
+ {
+ /* inverse of the selectivity of the underlying clause */
+ s1 = 1.0 - compute_clause_selec(root,
+ (Node *) get_notclausearg((Expr *) clause));
+ }
+ else if (and_clause(clause))
+ {
+ /* Use the product of the selectivities of the subclauses.
+ * XXX this is probably too optimistic, since the subclauses
+ * are very likely not independent...
+ */
+ List *arg;
+ s1 = 1.0;
+ foreach(arg, ((Expr *) clause)->args)
+ {
+ Cost s2 = compute_clause_selec(root, (Node *) lfirst(arg));
+ s1 = s1 * s2;
+ }
+ }
+ else if (or_clause(clause))
+ {
+ /* Selectivities for an 'or' clause are computed as s1+s2 - s1*s2
+ * to account for the probable overlap of selected tuple sets.
+ * XXX is this too conservative?
+ */
+ List *arg;
+ s1 = 0.0;
+ foreach(arg, ((Expr *) clause)->args)
+ {
+ Cost s2 = compute_clause_selec(root, (Node *) lfirst(arg));
+ s1 = s1 + s2 - s1 * s2;
+ }
+ }
+ else if (is_funcclause(clause))
{
- /* this isn't an Oper, it's a Func!! */
-
/*
* This is not an operator, so we guess at the selectivity. THIS
* IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE ABLE TO HAVE
* SELECTIVITIES THEMSELVES. -- JMH 7/9/92
*/
- s1 = 0.1;
+ s1 = (Cost) 0.3333333;
}
- else if (not_clause((Node *) clause))
+ else if (is_subplan(clause))
{
- /* negate this baby */
- return 1 - compute_selec(root, ((Expr *) clause)->args, or_selectivities);
- }
- else if (is_subplan((Node *) clause))
- {
-
/*
* Just for the moment! FIX ME! - vadim 02/04/98
*/
s1 = 1.0;
}
- else if (NumRelids((Node *) clause) == 1)
+ else if (is_opclause(clause))
{
-
- /*
- * ...otherwise, calculate s1 from 'clauses'. The clause is not a
- * join clause, since there is only one relid in the clause. The
- * clause selectivity will be based on the operator selectivity
- * and operand values.
- */
- Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
- RegProcedure oprrest = get_oprrest(opno);
- Oid relid;
- int relidx;
- AttrNumber attno;
- Datum constval;
- int flag;
-
- get_relattval((Node *) clause, &relidx, &attno, &constval, &flag);
- relid = getrelid(relidx, root->rtable);
-
- /*
- * if the oprrest procedure is missing for whatever reason, use a
- * selectivity of 0.5
- */
- if (!oprrest)
- s1 = (Cost) (0.5);
- else if (attno == InvalidAttrNumber)
+ if (NumRelids(clause) == 1)
{
+ /* The clause is not a join clause, since there is only one
+ * relid in the clause. The clause selectivity will be based on
+ * the operator selectivity and operand values.
+ */
+ Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
+ RegProcedure oprrest = get_oprrest(opno);
+ Oid relid;
+ int relidx;
+ AttrNumber attno;
+ Datum constval;
+ int flag;
+
+ get_relattval(clause, &relidx, &attno, &constval, &flag);
+ relid = getrelid(relidx, root->rtable);
/*
- * attno can be Invalid if the clause had a function in it,
- * i.e. WHERE myFunc(f) = 10
+ * if the oprrest procedure is missing for whatever reason, use a
+ * selectivity of 0.5
*/
- /* this should be FIXED somehow to use function selectivity */
- s1 = (Cost) (0.5);
+ if (!oprrest)
+ s1 = (Cost) 0.5;
+ else if (attno == InvalidAttrNumber)
+ {
+ /*
+ * attno can be Invalid if the clause had a function in it,
+ * i.e. WHERE myFunc(f) = 10
+ */
+ /* this should be FIXED somehow to use function selectivity */
+ s1 = (Cost) (0.5);
+ }
+ else
+ s1 = (Cost) restriction_selectivity(oprrest,
+ opno,
+ relid,
+ attno,
+ (char *) constval,
+ flag);
}
else
- s1 = (Cost) restriction_selectivity(oprrest,
- opno,
- relid,
- attno,
- (char *) constval,
- flag);
-
- }
- else
- {
-
- /*
- * The clause must be a join clause. The clause selectivity will
- * be based on the relations to be scanned and the attributes they
- * are to be joined on.
- */
- Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
- RegProcedure oprjoin = get_oprjoin(opno);
- int relid1,
- relid2;
- AttrNumber attno1,
- attno2;
+ {
+ /*
+ * The clause must be a join clause. The clause selectivity will
+ * be based on the relations to be scanned and the attributes they
+ * are to be joined on.
+ */
+ Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
+ RegProcedure oprjoin = get_oprjoin(opno);
+ int relid1,
+ relid2;
+ AttrNumber attno1,
+ attno2;
- get_rels_atts((Node *) clause, &relid1, &attno1, &relid2, &attno2);
- relid1 = getrelid(relid1, root->rtable);
- relid2 = getrelid(relid2, root->rtable);
+ get_rels_atts(clause, &relid1, &attno1, &relid2, &attno2);
+ relid1 = getrelid(relid1, root->rtable);
+ relid2 = getrelid(relid2, root->rtable);
- /*
- * if the oprjoin procedure is missing for whatever reason, use a
- * selectivity of 0.5
- */
- if (!oprjoin)
- s1 = (Cost) (0.5);
- else
- s1 = (Cost) join_selectivity(oprjoin,
- opno,
- relid1,
- attno1,
- relid2,
- attno2);
+ /*
+ * if the oprjoin procedure is missing for whatever reason, use a
+ * selectivity of 0.5
+ */
+ if (!oprjoin)
+ s1 = (Cost) (0.5);
+ else
+ s1 = (Cost) join_selectivity(oprjoin,
+ opno,
+ relid1,
+ attno1,
+ relid2,
+ attno2);
+ }
}
- /*
- * A null clause list eliminates no tuples, so return a selectivity of
- * 1.0. If there is only one clause, the selectivity is not that of
- * an 'or' clause, but rather that of the single clause.
- */
-
- if (lnext(clauses) == NIL)
- return s1;
- else
- {
- /* Compute selectivity of the 'or'ed subclauses. */
- /* Added check for taking lnext(NIL). -- JMH 3/9/92 */
- Cost s2;
-
- if (or_selectivities != NIL)
- s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities));
- else
- s2 = compute_selec(root, lnext(clauses), NIL);
- return s1 + s2 - s1 * s2;
- }
+ return s1;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.62 1999/07/23 03:34:49 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.63 1999/07/24 23:21:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
continue;
/*
- * 1. Try matching the index against subclauses of an 'or' clause.
- * The fields of the restrictinfo nodes are marked with lists of
- * the matching indices. No paths are actually created. We
- * currently only look to match the first key. We don't find
- * multi-key index cases where an AND matches the first key, and
- * the OR matches the second key.
+ * 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 currently only look to match the first key of each index against
+ * 'or' subclauses. There are cases where a later key of a multi-key
+ * index could be used (if other top-level clauses match earlier keys
+ * of the index), but our poor brains are hurting already...
+ *
+ * We don't even think about special handling of 'or' clauses that
+ * involve more than one relation, since they can't be processed by
+ * a single indexscan path anyway. Currently, cnfify() is certain
+ * to have restructured any such toplevel 'or' clauses anyway.
*/
match_index_orclauses(rel,
index,
restrictinfo_list);
/*
- * 2. If the keys of this index match any of the available
+ * 2. If the keys of this index match any of the available non-'or'
* restriction clauses, then create a path using those clauses
* as indexquals.
*/
/*
* match_index_orclauses
* Attempt to match an index against subclauses within 'or' clauses.
- * If the index does match, then the clause is marked with information
- * about the index.
+ * Each subclause that does match is marked with the index's node.
*
- * Essentially, this adds 'index' to the list of indices in the
- * RestrictInfo field of each of the clauses which it matches.
+ * 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 *restrictinfo = (RestrictInfo *) lfirst(i);
- if (valid_or_clause(restrictinfo))
+ if (restriction_is_or_clause(restrictinfo))
{
/*
- * Mark the 'or' clause with a list of indices which match
- * each of its subclauses. We add entries to the existing
- * list, if any.
+ * Add this index to the subclause index list for each
+ * subclause that it matches.
*/
restrictinfo->indexids =
match_index_orclause(rel, index,
List *index_list;
List *clist;
- /* first time through, we create empty list of same length as OR clause */
+ /* 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;
pathnode->path.pathorder->ord.sortop = index->ordering;
pathnode->path.pathkeys = NIL;
+ /* Note that we are making a pathnode for a single-scan indexscan;
+ * therefore, both indexid and indexqual should be single-element
+ * lists.
+ */
pathnode->indexid = index->relids;
pathnode->indexkeys = index->indexkeys;
- pathnode->indexqual = clausegroup;
+ pathnode->indexqual = lcons(get_actual_clauses(clausegroup), NIL);
pathnode->path.joinid = ((RestrictInfo *) lfirst(clausegroup))->restrictinfojoinid;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.28 1999/07/16 04:59:15 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.29 1999/07/24 23:21:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parsetree.h"
-static void best_or_subclause_indices(Query *root, RelOptInfo *rel, List *subclauses,
- List *indices, List **indexids, Cost *cost, Cost *selec);
-static void best_or_subclause_index(Query *root, RelOptInfo *rel, Expr *subclause,
- List *indices, int *indexid, Cost *cost, Cost *selec);
+static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
+ List *subclauses, List *indices,
+ List **indexids,
+ Cost *cost, Cost *selec);
+static void best_or_subclause_index(Query *root, RelOptInfo *rel,
+ Expr *subclause, List *indices,
+ int *indexid, Cost *cost, Cost *selec);
/*
* 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 these index path nodes.
+ * Returns a list of index path nodes.
*
*/
List *
create_or_index_paths(Query *root,
RelOptInfo *rel, List *clauses)
{
- List *t_list = NIL;
+ List *path_list = NIL;
List *clist;
foreach(clist, clauses)
{
- RestrictInfo *clausenode = (RestrictInfo *) (lfirst(clist));
+ 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 'Index field was set in
- * (match_or) if no index matches a given subclause, one of the
- * lists of index nodes returned by (get_index) will be 'nil').
+ * has been matched by an index. The information used was
+ * saved by create_index_paths().
*/
- if (valid_or_clause(clausenode) &&
+ if (restriction_is_or_clause(clausenode) &&
clausenode->indexids)
{
- List *temp = NIL;
- List *index_list = NIL;
- bool index_flag = true;
+ bool all_indexable = true;
+ List *temp;
- index_list = clausenode->indexids;
- foreach(temp, index_list)
+ foreach(temp, clausenode->indexids)
{
- if (!lfirst(temp))
+ if (lfirst(temp) == NIL)
{
- index_flag = false;
+ all_indexable = false;
break;
}
}
- /* do they all have indexes? */
- if (index_flag)
- { /* used to be a lisp every function */
+ if (all_indexable)
+ {
+ /*
+ * OK, build an IndexPath for this OR clause, using the
+ * best available index for each subclause.
+ */
IndexPath *pathnode = makeNode(IndexPath);
- List *indexids = NIL;
+ List *indexids;
+ List *orclause;
Cost cost;
Cost selec;
pathnode->path.pathorder->ordtype = SORTOP_ORDER;
/*
- * This is an IndexScan, but it does index lookups based
- * on the order of the fields specified in the WHERE
- * clause, not in any order, so the sortop is NULL.
+ * 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.pathorder->ord.sortop = NULL;
pathnode->path.pathkeys = NIL;
- pathnode->indexqual = lcons(clausenode, NIL);
+ /*
+ * Generate an indexqual list from the OR clause's args.
+ * We want two levels of sublist: the first is implicit OR
+ * and the second is implicit AND. (Currently, we will never
+ * see a sub-AND-clause because of cnfify(), but someday maybe
+ * the code below will do something useful...)
+ */
+ pathnode->indexqual = NIL;
+ foreach(orclause, clausenode->clause->args)
+ {
+ List *sublist;
+ if (and_clause(lfirst(orclause)))
+ sublist = ((Expr *) lfirst(orclause))->args;
+ else
+ sublist = lcons(lfirst(orclause), NIL);
+ pathnode->indexqual = lappend(pathnode->indexqual,
+ sublist);
+ }
pathnode->indexid = indexids;
pathnode->path.path_cost = cost;
+ clausenode->selectivity = (Cost) selec;
/*
* copy restrictinfo list into path for expensive function
if (XfuncMode != XFUNC_OFF)
((Path *) pathnode)->path_cost += xfunc_get_path_cost((Path) pathnode);
#endif
- clausenode->selectivity = (Cost) selec;
- t_list = lappend(t_list, pathnode);
+ path_list = lappend(path_list, pathnode);
}
}
}
- return t_list;
+ 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.
+ * indices. The cost is the sum of the individual index costs, since
+ * the executor will perform a scan for each subclause of the 'or'.
*
- * 'rel' is the node of the relation on which the index is defined
+ * 'rel' is the node of the relation on which the indexes are defined
* 'subclauses' are the subclauses of the 'or' clause
- * 'indices' are those index nodes that matched subclauses of the 'or'
- * clause
- * 'examined_indexids' is a list of those index ids to be used with
- * subclauses that have already been examined
- * 'subcost' is the cost of using the indices in 'examined_indexids'
- * 'selec' is a list of all subclauses that have already been examined
- *
- * Returns a list of the indexids, cost, and selectivities of each
- * subclause, e.g., ((i1 i2 i3) cost (s1 s2 s3)), where 'i' is an OID,
- * 'cost' is a flonum, and 's' is a flonum.
+ * 'indices' is a list of sublists of the index nodes that matched each
+ * subclause of the 'or' clause
+ * '*indexids' gets a list of the best index ID to use for each subclause
+ * '*cost' gets the total cost of the path
+ * '*selec' gets the total selectivity of the path.
*/
static void
best_or_subclause_indices(Query *root,
List *subclauses,
List *indices,
List **indexids, /* return value */
- Cost *cost, /* return value */
- Cost *selec) /* return value */
+ Cost *cost, /* return value */
+ Cost *selec) /* return value */
{
List *slist;
+ *indexids = NIL;
*selec = (Cost) 0.0;
*cost = (Cost) 0.0;
indices = lnext(indices);
}
-
- return;
}
/*
* 'rel' is the node of the relation on which the index is defined
* 'subclause' is the subclause
* 'indices' is a list of index nodes that match the subclause
- *
- * Returns a list (index_id index_subcost index_selectivity)
- * (a fixnum, a fixnum, and a flonum respectively).
- *
+ * '*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,
Cost *retCost, /* return value */
Cost *retSelec) /* return value */
{
- List *ilist;
+ Oid relid = getrelid(lfirsti(rel->relids),
+ root->rtable);
+ Oid opno = ((Oper *) subclause->oper)->opno;
+ AttrNumber attno = (get_leftop(subclause))->varattno;
+ bool constant_on_right = non_null((Expr *) get_rightop(subclause));
+ Datum value;
+ int flag;
+ List *opnos,
+ *attnos,
+ *values,
+ *flags;
bool first_run = true;
+ List *ilist;
/* if we don't match anything, return zeros */
*retIndexid = 0;
- *retCost = 0.0;
- *retSelec = 0.0;
+ *retCost = (Cost) 0.0;
+ *retSelec = (Cost) 0.0;
+
+ if (constant_on_right) /* XXX looks pretty bogus ... tgl */
+ value = ((Const *) get_rightop(subclause))->constvalue;
+ else
+ value = NameGetDatum("");
+ if (constant_on_right)
+ flag = (_SELEC_IS_CONSTANT_ || _SELEC_CONSTANT_RIGHT_);
+ else
+ flag = _SELEC_CONSTANT_RIGHT_;
+
+ /* prebuild lists since we will pass same list to each index */
+ opnos = lconsi(opno, NIL);
+ attnos = lconsi(attno, NIL);
+ values = lconsi(value, NIL);
+ flags = lconsi(flag, NIL);
foreach(ilist, indices)
{
RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
-
- Datum value;
- int flag = 0;
+ Oid indexid = (Oid) lfirsti(index->relids);
Cost subcost;
- AttrNumber attno = (get_leftop(subclause))->varattno;
- Oid opno = ((Oper *) subclause->oper)->opno;
- bool constant_on_right = non_null((Expr *) get_rightop(subclause));
float npages,
selec;
- if (constant_on_right)
- value = ((Const *) get_rightop(subclause))->constvalue;
- else
- value = NameGetDatum("");
- if (constant_on_right)
- flag = (_SELEC_IS_CONSTANT_ || _SELEC_CONSTANT_RIGHT_);
- else
- flag = _SELEC_CONSTANT_RIGHT_;
-
- index_selectivity(lfirsti(index->relids),
+ index_selectivity(indexid,
index->classlist,
- lconsi(opno, NIL),
- getrelid(lfirsti(rel->relids),
- root->rtable),
- lconsi(attno, NIL),
- lconsi(value, NIL),
- lconsi(flag, NIL),
+ opnos,
+ relid,
+ attnos,
+ values,
+ flags,
1,
&npages,
&selec);
- subcost = cost_index((Oid) lfirsti(index->relids),
+ subcost = cost_index(indexid,
(int) npages,
(Cost) selec,
rel->pages,
if (first_run || subcost < *retCost)
{
- *retIndexid = lfirsti(index->relids);
+ *retIndexid = indexid;
*retCost = subcost;
*retSelec = selec;
first_run = false;
}
}
- return;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.62 1999/07/17 20:17:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.63 1999/07/24 23:21:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* create_indexscan_node
* Returns a indexscan node for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ *
+ * If an 'or' clause is to be used with this index, the indxqual field
+ * will contain a list of the 'or' clause arguments, e.g., the
+ * clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the
+ * indxqual will simply contain one conjunctive qualification: ((a)).
*/
static IndexScan *
create_indexscan_node(IndexPath *best_path,
List *tlist,
List *scan_clauses)
{
-
- /*
- * Extract the(first if conjunct, only if disjunct) clause from the
- * restrictinfo list.
- */
- Expr *index_clause = (Expr *) NULL;
- List *indxqual = NIL;
- List *qpqual = NIL;
- List *fixed_indxqual = NIL;
+ List *indxqual = best_path->indexqual;
+ List *qpqual;
+ List *fixed_indxqual;
List *ixid;
- IndexScan *scan_node = (IndexScan *) NULL;
- bool lossy = FALSE;
- HeapTuple indexTuple;
- Form_pg_index index;
-
- /*
- * If an 'or' clause is to be used with this index, the indxqual field
- * will contain a list of the 'or' clause arguments, e.g., the
- * clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the
- * indxqual will simply contain one conjunctive qualification: ((a)).
- */
- if (best_path->indexqual != NULL)
- /* added call to fix_opids, JMH 6/23/92 */
- index_clause = (Expr *)
- lfirst(fix_opids(get_actual_clauses(best_path->indexqual)));
-
- if (or_clause((Node *) index_clause))
- {
- List *temp = NIL;
-
- foreach(temp, index_clause->args)
- indxqual = lappend(indxqual, lcons(lfirst(temp), NIL));
- }
- else
- {
- indxqual = lcons(get_actual_clauses(best_path->indexqual),
- NIL);
- }
+ IndexScan *scan_node;
+ bool lossy = false;
/* check and see if any indices are lossy */
foreach(ixid, best_path->indexid)
{
+ HeapTuple indexTuple;
+ Form_pg_index index;
+
indexTuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(lfirsti(ixid)),
0, 0, 0);
elog(ERROR, "create_plan: index %u not found", lfirsti(ixid));
index = (Form_pg_index) GETSTRUCT(indexTuple);
if (index->indislossy)
- lossy = TRUE;
+ {
+ lossy = true;
+ break;
+ }
}
-
/*
- * The qpqual field contains all restrictions not automatically
+ * The qpqual list must contain all restrictions not automatically
* handled by the index. Note that for non-lossy indices, the
* predicates in the indxqual are handled by the index, while for
* lossy indices the indxqual predicates need to be double-checked
* after the index fetches the best-guess tuples.
+ *
+ * There should not be any clauses in scan_clauses that duplicate
+ * expressions checked by the index, but just in case, we will
+ * get rid of them via set_difference.
*/
- if (or_clause((Node *) index_clause))
+ if (length(indxqual) > 1)
{
+ /*
+ * Build an expression representation of the indexqual, expanding
+ * the implicit OR and AND semantics of the first- and second-level
+ * lists. XXX Is it really necessary to do a deep copy here?
+ */
+ List *orclauses = NIL;
+ List *orclause;
+ Expr *indxqual_expr;
+
+ foreach(orclause, indxqual)
+ {
+ orclauses = lappend(orclauses,
+ make_ands_explicit((List *) copyObject(lfirst(orclause))));
+ }
+ indxqual_expr = make_orclause(orclauses);
+
+ /* this set_difference is almost certainly a waste of time... */
qpqual = set_difference(scan_clauses,
- lcons(index_clause, NIL));
+ lcons(indxqual_expr, NIL));
if (lossy)
- qpqual = lappend(qpqual, (List *) copyObject(index_clause));
+ qpqual = lappend(qpqual, indxqual_expr);
}
- else
+ else if (indxqual != NIL)
{
+ /* Here, we can simply treat the first sublist as an independent
+ * set of qual expressions, since there is no top-level OR behavior.
+ */
qpqual = set_difference(scan_clauses, lfirst(indxqual));
if (lossy)
- qpqual = nconc(qpqual,
- (List *) copyObject(lfirst(indxqual)));
+ qpqual = nconc(qpqual, (List *) copyObject(lfirst(indxqual)));
}
+ else
+ qpqual = NIL;
+
+ /*
+ * Fix opids in the completed indxqual. We don't want to do this sooner
+ * since it would screw up the set_difference calcs above. Really,
+ * this ought to only happen at final exit from the planner...
+ */
+ indxqual = fix_opids(indxqual);
- fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual, (Path *) best_path);
+ /* The executor needs a copy with index attrs substituted for table ones */
+ fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual,
+ (Path *) best_path);
scan_node = make_indexscan(tlist,
qpqual,
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.34 1999/07/16 04:59:19 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.35 1999/07/24 23:21:12 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "optimizer/var.h"
#include "utils/lsyscache.h"
-extern int Quiet;
-static void add_restrict_and_join_to_rel(Query *root, List *clause);
+static void add_restrict_and_join_to_rel(Query *root, Node *clause);
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
Relids join_relids);
-static void add_vars_to_targetlist(Query *root, List *vars, Relids join_relids);
+static void add_vars_to_targetlist(Query *root, List *vars);
static MergeOrder *mergejoinop(Expr *clause);
static Oid hashjoinop(Expr *clause);
* relations appearing within clauses. Creates new relation entries if
* necessary, adding them to *query_relation_list*.
*
- * Returns nothing of interest.
+ * 'clauses': the list of clauses in the cnfify'd query qualification.
*/
void
add_restrict_and_join_to_rels(Query *root, List *clauses)
List *clause;
foreach(clause, clauses)
- add_restrict_and_join_to_rel(root, lfirst(clause));
- return;
+ add_restrict_and_join_to_rel(root, (Node*) lfirst(clause));
}
/*
* add_restrict_and_join_to_rel-
* Add clause information to either the 'RestrictInfo' or 'JoinInfo' field
- * of a relation entry(depending on whether or not the clause is a join)
+ * of a relation entry (depending on whether or not the clause is a join)
* by creating a new RestrictInfo node and setting appropriate fields
* within the nodes.
- *
- * Returns nothing of interest.
*/
static void
-add_restrict_and_join_to_rel(Query *root, List *clause)
+add_restrict_and_join_to_rel(Query *root, Node *clause)
{
+ RestrictInfo *restrictinfo = makeNode(RestrictInfo);
Relids relids;
List *vars;
- RestrictInfo *restrictinfo = makeNode(RestrictInfo);
-
- /*
- * Retrieve all relids and vars contained within the clause.
- */
- clause_get_relids_vars((Node *) clause, &relids, &vars);
restrictinfo->clause = (Expr *) clause;
- restrictinfo->notclause = contains_not((Node *) clause);
- restrictinfo->selectivity = 0;
restrictinfo->indexids = NIL;
restrictinfo->mergejoinorder = (MergeOrder *) NULL;
restrictinfo->hashjoinoperator = (Oid) 0;
+ /*
+ * The selectivity of the clause must be computed regardless of
+ * whether it's a restriction or a join clause
+ */
+ restrictinfo->selectivity = compute_clause_selec(root, clause);
+
+ /*
+ * Retrieve all relids and vars contained within the clause.
+ */
+ clause_get_relids_vars(clause, &relids, &vars);
+
if (length(relids) == 1)
{
-
/*
* There is only one relation participating in 'clause', so
- * 'clause' must be a restriction clause.
+ * 'clause' must be a restriction clause for that relation.
*/
RelOptInfo *rel = get_base_rel(root, lfirsti(relids));
- /*
- * The selectivity of the clause must be computed regardless of
- * whether it's a restriction or a join clause
- */
- if (is_funcclause((Node *) clause))
-
- /*
- * XXX If we have a func clause set selectivity to 1/3, really
- * need a true selectivity function.
- */
- restrictinfo->selectivity = (Cost) 0.3333333;
- else
- restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);
-
rel->restrictinfo = lcons(restrictinfo, rel->restrictinfo);
}
else
{
-
/*
* 'clause' is a join clause, since there is more than one atom in
- * the relid list.
+ * the relid list. Add it to the join lists of all the relevant
+ * relations. (If, perchance, 'clause' contains NO vars, then
+ * nothing will happen...)
*/
- if (is_funcclause((Node *) clause))
-
- /*
- * XXX If we have a func clause set selectivity to 1/3, really
- * need a true selectivity function.
- */
- restrictinfo->selectivity = (Cost) 0.3333333;
- else
- restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);
-
add_join_info_to_rels(root, restrictinfo, relids);
- /* we are going to be doing a join, so add var to targetlist */
- add_vars_to_targetlist(root, vars, relids);
+ /* we are going to be doing a join, so add vars to targetlists */
+ add_vars_to_targetlist(root, vars);
}
}
/*
* add_join_info_to_rels
* For every relation participating in a join clause, add 'restrictinfo' to
- * the appropriate joininfo node(creating a new one and adding it to the
+ * the appropriate joininfo node (creating a new one and adding it to the
* appropriate rel node if necessary).
*
* 'restrictinfo' describes the join clause
* 'join_relids' is the list of relations participating in the join clause
- *
*/
static void
add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
{
List *join_relid;
- /* For every relid, find the rel, and add the proper join entries */
+ /* For every relid, find the joininfo, and add the proper join entries */
foreach(join_relid, join_relids)
{
JoinInfo *joininfo;
unjoined_relids = lappendi(unjoined_relids, lfirsti(rel));
}
+ /*
+ * Find or make the joininfo node for this combination of rels
+ */
joininfo = find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
unjoined_relids);
+
+ /*
+ * And add the restrictinfo node to it. NOTE that each joininfo
+ * gets its own copy of the restrictinfo node! (Is this really
+ * necessary? Possibly ... later parts of the optimizer destructively
+ * modify restrict/join clauses...)
+ */
joininfo->jinfo_restrictinfo = lcons(copyObject((void *) restrictinfo),
- joininfo->jinfo_restrictinfo);
+ joininfo->jinfo_restrictinfo);
}
}
/*
* add_vars_to_targetlist
- * For each variable appearing in a clause,
- * (1) If a targetlist entry for the variable is not already present in
- * the appropriate relation's target list, add one.
- * (2) If a targetlist entry is already present, but the var is part of a
- * join clause, add the relids of the join relations to the JoinList
- * entry of the targetlist entry.
- *
- * 'vars' is the list of var nodes
- * 'join_relids' is the list of relids appearing in the join clause
- * (if this is a join clause)
- *
- * Returns nothing.
+ * For each variable appearing in a clause, add it to the relation's
+ * targetlist if not already present.
*/
static void
-add_vars_to_targetlist(Query *root, List *vars, Relids join_relids)
+add_vars_to_targetlist(Query *root, List *vars)
{
- Var *var;
- List *temp = NIL;
- RelOptInfo *rel = (RelOptInfo *) NULL;
- TargetEntry *tlistentry;
+ List *temp;
foreach(temp, vars)
{
- var = (Var *) lfirst(temp);
- rel = get_base_rel(root, var->varno);
- tlistentry = tlistentry_member(var, rel->targetlist);
- if (tlistentry == NULL)
- /* add a new entry */
+ Var *var = (Var *) lfirst(temp);
+ RelOptInfo *rel = get_base_rel(root, var->varno);
+
+ if (tlistentry_member(var, rel->targetlist) == NULL)
add_var_to_tlist(rel, var);
}
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.40 1999/07/16 04:59:23 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.41 1999/07/24 23:21:13 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
return expr;
}
+/*
+ * Sometimes (such as in the result of cnfify), we use lists of expression
+ * nodes with implicit AND semantics. This function converts back to an
+ * explicit-AND representation.
+ */
+Expr *
+make_ands_explicit(List *andclauses)
+{
+ if (andclauses == NIL)
+ return NULL;
+ else if (length(andclauses) == 1)
+ return (Expr *) lfirst(andclauses);
+ else
+ return make_andclause(andclauses);
+}
/*****************************************************************************
* CASE clause functions
return length(var_list);
}
-/*
- * contains_not
- *
- * Returns t iff the clause is a 'not' clause or if any of the
- * subclauses within an 'or' clause contain 'not's.
- *
- * NOTE that only the top-level AND/OR structure is searched for NOTs;
- * we are not interested in buried substructure.
- */
-bool
-contains_not(Node *clause)
-{
- if (single_node(clause))
- return false;
-
- if (not_clause(clause))
- return true;
-
- if (or_clause(clause) || and_clause(clause))
- {
- List *a;
-
- foreach(a, ((Expr *) clause)->args)
- {
- if (contains_not(lfirst(a)))
- return true;
- }
- }
-
- return false;
-}
-
/*
* is_joinable
*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.46 1999/07/16 04:59:25 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.47 1999/07/24 23:21:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* each clause gets an equal selectivity */
clausesel = pow(selec, 1.0 / (double) length(restriction_clauses));
- pathnode->indexqual = restriction_clauses;
+ pathnode->indexqual = lcons(get_actual_clauses(restriction_clauses),
+ NIL);
pathnode->path.path_cost = cost_index(lfirsti(index->relids),
(int) npages,
selec,
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.6 1999/07/16 04:59:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.7 1999/07/24 23:21:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "optimizer/restrictinfo.h"
/*
- * valid_or_clause
+ * restriction_is_or_clause
*
- * Returns t iff the restrictinfo node contains a 'normal' 'or' clause.
+ * Returns t iff the restrictinfo node contains an 'or' clause.
*
*/
bool
-valid_or_clause(RestrictInfo *restrictinfo)
+restriction_is_or_clause(RestrictInfo *restrictinfo)
{
if (restrictinfo != NULL &&
- !single_node((Node *) restrictinfo->clause) &&
- !restrictinfo->notclause &&
or_clause((Node *) restrictinfo->clause))
return true;
else
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: relation.h,v 1.34 1999/07/15 23:03:56 momjian Exp $
+ * $Id: relation.h,v 1.35 1999/07/24 23:21:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *loc_restrictinfo;
} Path;
+/*----------
+ * IndexPath represents an index scan. Although an indexscan can only read
+ * a single relation, it can scan it more than once, potentially using a
+ * different index during each scan. The result is the union (OR) of all the
+ * tuples matched during any scan. (The executor is smart enough not to return
+ * the same tuple more than once, even if it is matched in multiple scans.)
+ * 'indexid' is a list of index relation OIDs, one per scan to be performed.
+ * 'indexqual' is a list of index qualifications, also one per scan.
+ * Each entry in 'indexqual' is a sublist of qualification expressions with
+ * implicit AND semantics across the sublist items. Each one of the sublist
+ * items must be an operator expression of the form (var op something) or
+ * (something op var), where the var is a field the associated index keys on
+ * and the op is a member of the operator class of the index.
+ * NOTE that the semantics of the top-level list in 'indexqual' is OR
+ * combination, while the sublists are implicitly AND combinations!
+ *----------
+ */
typedef struct IndexPath
{
Path path;
} JoinKey;
/*
- * clause info
+ * Restriction clause info.
+ * We create one of these for each AND sub-clause of a restriction condition
+ * (WHERE clause). Since the restriction clauses are logically ANDed, we
+ * can use any one of them or any subset of them to filter out tuples,
+ * without having to evaluate the rest. The RestrictInfo node itself stores
+ * data used by the optimizer while choosing the best query plan.
*/
typedef struct RestrictInfo
{
NodeTag type;
- Expr *clause; /* should be an OP clause */
- Cost selectivity;
- bool notclause;
- List *indexids;
+ Expr *clause; /* the represented subclause of WHERE cond */
+ Cost selectivity; /* estimated selectivity */
+ List *indexids; /* subclause index IDs if clause is an OR */
/* mergejoin only */
MergeOrder *mergejoinorder;
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: clauses.h,v 1.21 1999/07/15 23:03:57 momjian Exp $
+ * $Id: clauses.h,v 1.22 1999/07/24 23:21:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern bool and_clause(Node *clause);
extern Expr *make_andclause(List *andclauses);
+extern Expr *make_ands_explicit(List *andclauses);
extern bool case_clause(Node *clause);
extern List *pull_constant_clauses(List *quals, List **constantQual);
extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
extern int NumRelids(Node *clause);
-extern bool contains_not(Node *clause);
extern bool is_joinable(Node *clause);
extern bool qual_clause_p(Node *clause);
extern void fix_opid(Node *clause);
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: cost.h,v 1.21 1999/07/15 15:21:20 momjian Exp $
+ * $Id: cost.h,v 1.22 1999/07/24 23:21:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Cost product_selec(List *restrictinfo_list);
extern void set_rest_relselec(Query *root, List *rel_list);
extern void set_rest_selec(Query *root, List *restrictinfo_list);
-extern Cost compute_clause_selec(Query *root,
- Node *clause, List *or_selectivities);
+extern Cost compute_clause_selec(Query *root, Node *clause);
#endif /* COST_H */
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: restrictinfo.h,v 1.5 1999/07/15 15:21:23 momjian Exp $
+ * $Id: restrictinfo.h,v 1.6 1999/07/24 23:21:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/relation.h"
-extern bool valid_or_clause(RestrictInfo *restrictinfo);
+extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
extern List *get_actual_clauses(List *restrictinfo_list);
extern void get_relattvals(List *restrictinfo_list, List **attnos,
List **values, List **flags);