summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util')
-rw-r--r--src/backend/optimizer/util/clauses.c199
-rw-r--r--src/backend/optimizer/util/pathnode.c6
-rw-r--r--src/backend/optimizer/util/plancat.c151
-rw-r--r--src/backend/optimizer/util/relnode.c135
4 files changed, 215 insertions, 276 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8bd6ef6f68b..e0cc97e3a1d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.84 2001/03/27 17:12:34 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.85 2001/05/20 20:28:19 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -46,7 +46,6 @@ static bool pull_subplans_walker(Node *node, List **listptr);
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
Query *context);
static bool contain_noncachable_functions_walker(Node *node, void *context);
-static int is_single_func(Node *node);
static Node *eval_const_expressions_mutator(Node *node, void *context);
static Expr *simplify_op_or_func(Expr *expr, List *args);
@@ -797,202 +796,6 @@ NumRelids(Node *clause)
return result;
}
-/*
- * get_relattval
- * Extract information from a restriction or join clause for
- * selectivity estimation. The inputs are an expression
- * and a relation number (which can be 0 if we don't care which
- * relation is used; that'd normally be the case for restriction
- * clauses, where the caller already knows that only one relation
- * is referenced in the clause). The routine checks that the
- * expression is of the form (var op something) or (something op var)
- * where the var is an attribute of the specified relation, or
- * a function of a var of the specified relation. If so, it
- * returns the following info:
- * the found relation number (same as targetrelid unless that is 0)
- * the found var number (or InvalidAttrNumber if a function)
- * if the "something" is a constant, the value of the constant
- * flags indicating whether a constant was found, and on which side.
- * Default values are returned if the expression is too complicated,
- * specifically 0 for the relid and attno, 0 for the constant value.
- *
- * Note that negative attno values are *not* invalid, but represent
- * system attributes such as OID. It's sufficient to check for relid=0
- * to determine whether the routine succeeded.
- */
-void
-get_relattval(Node *clause,
- int targetrelid,
- int *relid,
- AttrNumber *attno,
- Datum *constval,
- int *flag)
-{
- Var *left,
- *right,
- *other;
- int funcvarno;
-
- /* Careful; the passed clause might not be a binary operator at all */
-
- if (!is_opclause(clause))
- goto default_results;
-
- left = get_leftop((Expr *) clause);
- right = get_rightop((Expr *) clause);
-
- if (!right)
- goto default_results;
-
- /* Ignore any binary-compatible relabeling */
-
- if (IsA(left, RelabelType))
- left = (Var *) ((RelabelType *) left)->arg;
- if (IsA(right, RelabelType))
- right = (Var *) ((RelabelType *) right)->arg;
-
- /* First look for the var or func */
-
- if (IsA(left, Var) &&
- (targetrelid == 0 || targetrelid == left->varno))
- {
- *relid = left->varno;
- *attno = left->varattno;
- *flag = SEL_RIGHT;
- }
- else if (IsA(right, Var) &&
- (targetrelid == 0 || targetrelid == right->varno))
- {
- *relid = right->varno;
- *attno = right->varattno;
- *flag = 0;
- }
- else if ((funcvarno = is_single_func((Node *) left)) != 0 &&
- (targetrelid == 0 || targetrelid == funcvarno))
- {
- *relid = funcvarno;
- *attno = InvalidAttrNumber;
- *flag = SEL_RIGHT;
- }
- else if ((funcvarno = is_single_func((Node *) right)) != 0 &&
- (targetrelid == 0 || targetrelid == funcvarno))
- {
- *relid = funcvarno;
- *attno = InvalidAttrNumber;
- *flag = 0;
- }
- else
- {
- /* Duh, it's too complicated for me... */
-default_results:
- *relid = 0;
- *attno = 0;
- *constval = 0;
- *flag = 0;
- return;
- }
-
- /* OK, we identified the var or func; now look at the other side */
-
- other = (*flag == 0) ? left : right;
-
- if (IsA(other, Const) &&
- !((Const *) other)->constisnull)
- {
- *constval = ((Const *) other)->constvalue;
- *flag |= SEL_CONSTANT;
- }
- else
- *constval = 0;
-}
-
-/*
- * is_single_func
- * If the given expression is a function of a single relation,
- * return the relation number; else return 0
- */
-static int
-is_single_func(Node *node)
-{
- if (is_funcclause(node))
- {
- List *varnos = pull_varnos(node);
-
- if (length(varnos) == 1)
- {
- int funcvarno = lfirsti(varnos);
-
- freeList(varnos);
- return funcvarno;
- }
- freeList(varnos);
- }
- return 0;
-}
-
-/*
- * get_rels_atts
- *
- * Returns the info
- * ( relid1 attno1 relid2 attno2 )
- * for a joinclause.
- *
- * If the clause is not of the form (var op var) or if any of the vars
- * refer to nested attributes, then zeroes are returned.
- */
-void
-get_rels_atts(Node *clause,
- int *relid1,
- AttrNumber *attno1,
- int *relid2,
- AttrNumber *attno2)
-{
- /* set default values */
- *relid1 = 0;
- *attno1 = 0;
- *relid2 = 0;
- *attno2 = 0;
-
- if (is_opclause(clause))
- {
- Var *left = get_leftop((Expr *) clause);
- Var *right = get_rightop((Expr *) clause);
-
- if (left && right)
- {
- int funcvarno;
-
- /* Ignore any binary-compatible relabeling */
- if (IsA(left, RelabelType))
- left = (Var *) ((RelabelType *) left)->arg;
- if (IsA(right, RelabelType))
- right = (Var *) ((RelabelType *) right)->arg;
-
- if (IsA(left, Var))
- {
- *relid1 = left->varno;
- *attno1 = left->varattno;
- }
- else if ((funcvarno = is_single_func((Node *) left)) != 0)
- {
- *relid1 = funcvarno;
- *attno1 = InvalidAttrNumber;
- }
-
- if (IsA(right, Var))
- {
- *relid2 = right->varno;
- *attno2 = right->varattno;
- }
- else if ((funcvarno = is_single_func((Node *) right)) != 0)
- {
- *relid2 = funcvarno;
- *attno2 = InvalidAttrNumber;
- }
- }
- }
-}
-
/*--------------------
* CommuteClause: commute a binary operator clause
*
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 407c132b4f7..801b328d817 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.72 2001/05/07 00:43:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.73 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -346,9 +346,9 @@ create_index_path(Query *root,
/*
* We are making a pathnode for a single-scan indexscan; therefore,
- * both indexid and indexqual should be single-element lists.
+ * both indexinfo and indexqual should be single-element lists.
*/
- pathnode->indexid = makeListi1(index->indexoid);
+ pathnode->indexinfo = makeList1(index);
pathnode->indexqual = makeList1(indexquals);
pathnode->indexscandir = indexscandir;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index ee3523553e8..749390a4d2d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.65 2001/05/07 00:43:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.66 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,9 +23,12 @@
#include "catalog/pg_amop.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_index.h"
+#include "optimizer/clauses.h"
#include "optimizer/plancat.h"
+#include "parser/parsetree.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
#include "catalog/catalog.h"
@@ -33,7 +36,7 @@
/*
- * relation_info -
+ * get_relation_info -
* Retrieves catalog information for a given relation.
* Given the Oid of the relation, return the following info:
* whether the relation has secondary indices
@@ -41,8 +44,8 @@
* number of tuples
*/
void
-relation_info(Oid relationObjectId,
- bool *hasindex, long *pages, double *tuples)
+get_relation_info(Oid relationObjectId,
+ bool *hasindex, long *pages, double *tuples)
{
HeapTuple relationTuple;
Form_pg_class relation;
@@ -51,16 +54,19 @@ relation_info(Oid relationObjectId,
ObjectIdGetDatum(relationObjectId),
0, 0, 0);
if (!HeapTupleIsValid(relationTuple))
- elog(ERROR, "relation_info: Relation %u not found",
+ elog(ERROR, "get_relation_info: Relation %u not found",
relationObjectId);
relation = (Form_pg_class) GETSTRUCT(relationTuple);
- if (IsIgnoringSystemIndexes() && IsSystemRelationName(NameStr(relation->relname)))
+ if (IsIgnoringSystemIndexes() &&
+ IsSystemRelationName(NameStr(relation->relname)))
*hasindex = false;
else
- *hasindex = (relation->relhasindex) ? true : false;
+ *hasindex = relation->relhasindex;
+
*pages = relation->relpages;
*tuples = relation->reltuples;
+
ReleaseSysCache(relationTuple);
}
@@ -110,8 +116,8 @@ find_secondary_indexes(Oid relationObjectId)
info = makeNode(IndexOptInfo);
/*
- * Need to make these arrays large enough to be sure there is a
- * terminating 0 at the end of each one.
+ * Need to make these arrays large enough to be sure there is
+ * room for a terminating 0 at the end of each one.
*/
info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS + 1));
@@ -131,14 +137,26 @@ find_secondary_indexes(Oid relationObjectId)
}
else
info->indpred = NIL;
+ info->unique = index->indisunique;
info->lossy = index->indislossy;
for (i = 0; i < INDEX_MAX_KEYS; i++)
- info->indexkeys[i] = index->indkey[i];
- info->indexkeys[INDEX_MAX_KEYS] = 0;
- for (i = 0; i < INDEX_MAX_KEYS; i++)
+ {
+ if (index->indclass[i] == (Oid) 0)
+ break;
info->classlist[i] = index->indclass[i];
- info->classlist[INDEX_MAX_KEYS] = (Oid) 0;
+ }
+ info->classlist[i] = (Oid) 0;
+ info->ncolumns = i;
+
+ for (i = 0; i < INDEX_MAX_KEYS; i++)
+ {
+ if (index->indkey[i] == 0)
+ break;
+ info->indexkeys[i] = index->indkey[i];
+ }
+ info->indexkeys[i] = 0;
+ info->nkeys = i;
/* Extract info from the relation descriptor for the index */
indexRelation = index_open(index->indexrelid);
@@ -156,7 +174,7 @@ find_secondary_indexes(Oid relationObjectId)
MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1));
if (amorderstrategy != 0)
{
- for (i = 0; i < INDEX_MAX_KEYS && index->indclass[i]; i++)
+ for (i = 0; i < info->ncolumns; i++)
{
HeapTuple amopTuple;
Form_pg_amop amop;
@@ -193,30 +211,34 @@ find_secondary_indexes(Oid relationObjectId)
/*
* restriction_selectivity
*
- * Returns the selectivity of a specified operator.
+ * Returns the selectivity of a specified restriction operator clause.
* This code executes registered procedures stored in the
* operator relation, by calling the function manager.
*
- * XXX The assumption in the selectivity procedures is that if the
- * relation OIDs or attribute numbers are 0, then the clause
- * isn't of the form (op var const).
+ * varRelid is either 0 or a rangetable index. See clause_selectivity()
+ * for details about its meaning.
*/
Selectivity
-restriction_selectivity(Oid functionObjectId,
- Oid operatorObjectId,
- Oid relationObjectId,
- AttrNumber attributeNumber,
- Datum constValue,
- int constFlag)
+restriction_selectivity(Query *root,
+ Oid operator,
+ List *args,
+ int varRelid)
{
+ RegProcedure oprrest = get_oprrest(operator);
float8 result;
- result = DatumGetFloat8(OidFunctionCall5(functionObjectId,
- ObjectIdGetDatum(operatorObjectId),
- ObjectIdGetDatum(relationObjectId),
- Int16GetDatum(attributeNumber),
- constValue,
- Int32GetDatum(constFlag)));
+ /*
+ * if the oprrest procedure is missing for whatever reason,
+ * use a selectivity of 0.5
+ */
+ if (!oprrest)
+ return (Selectivity) 0.5;
+
+ result = DatumGetFloat8(OidFunctionCall4(oprrest,
+ PointerGetDatum(root),
+ ObjectIdGetDatum(operator),
+ PointerGetDatum(args),
+ Int32GetDatum(varRelid)));
if (result < 0.0 || result > 1.0)
elog(ERROR, "restriction_selectivity: bad value %f", result);
@@ -227,29 +249,29 @@ restriction_selectivity(Oid functionObjectId,
/*
* join_selectivity
*
- * Returns the selectivity of an operator, given the join clause
- * information.
- *
- * XXX The assumption in the selectivity procedures is that if the
- * relation OIDs or attribute numbers are 0, then the clause
- * isn't of the form (op var var).
+ * Returns the selectivity of a specified join operator clause.
+ * This code executes registered procedures stored in the
+ * operator relation, by calling the function manager.
*/
Selectivity
-join_selectivity(Oid functionObjectId,
- Oid operatorObjectId,
- Oid relationObjectId1,
- AttrNumber attributeNumber1,
- Oid relationObjectId2,
- AttrNumber attributeNumber2)
+join_selectivity(Query *root,
+ Oid operator,
+ List *args)
{
+ RegProcedure oprjoin = get_oprjoin(operator);
float8 result;
- result = DatumGetFloat8(OidFunctionCall5(functionObjectId,
- ObjectIdGetDatum(operatorObjectId),
- ObjectIdGetDatum(relationObjectId1),
- Int16GetDatum(attributeNumber1),
- ObjectIdGetDatum(relationObjectId2),
- Int16GetDatum(attributeNumber2)));
+ /*
+ * if the oprjoin procedure is missing for whatever reason,
+ * use a selectivity of 0.5
+ */
+ if (!oprjoin)
+ return (Selectivity) 0.5;
+
+ result = DatumGetFloat8(OidFunctionCall3(oprjoin,
+ PointerGetDatum(root),
+ ObjectIdGetDatum(operator),
+ PointerGetDatum(args)));
if (result < 0.0 || result > 1.0)
elog(ERROR, "join_selectivity: bad value %f", result);
@@ -330,3 +352,36 @@ has_subclass(Oid relationId)
ReleaseSysCache(tuple);
return result;
}
+
+/*
+ * has_unique_index
+ *
+ * Detect whether there is a unique index on the specified attribute
+ * of the specified relation, thus allowing us to conclude that all
+ * the (non-null) values of the attribute are distinct.
+ */
+bool
+has_unique_index(RelOptInfo *rel, AttrNumber attno)
+{
+ List *ilist;
+
+ foreach(ilist, rel->indexlist)
+ {
+ IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
+
+ /*
+ * Note: ignore functional, partial, or lossy indexes, since they
+ * don't allow us to conclude that all attr values are distinct.
+ * Also, a multicolumn unique index doesn't allow us to conclude
+ * that just the specified attr is unique.
+ */
+ if (index->unique &&
+ index->nkeys == 1 &&
+ index->indexkeys[0] == attno &&
+ index->indproc == InvalidOid &&
+ index->indpred == NIL &&
+ !index->lossy)
+ return true;
+ }
+ return false;
+}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index b4764ab6f8c..86d923116a4 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.32 2001/02/16 00:03:08 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.33 2001/05/20 20:28:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,6 +22,7 @@
#include "parser/parsetree.h"
+static RelOptInfo *make_base_rel(Query *root, int relid);
static List *new_join_tlist(List *tlist, int first_resdomno);
static List *build_joinrel_restrictlist(RelOptInfo *joinrel,
RelOptInfo *outer_rel,
@@ -36,28 +37,35 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
/*
- * get_base_rel
+ * build_base_rel
* Returns relation entry corresponding to 'relid', creating a new one
* if necessary. This is for base relations.
*/
RelOptInfo *
-get_base_rel(Query *root, int relid)
+build_base_rel(Query *root, int relid)
{
- List *baserels;
+ List *rels;
RelOptInfo *rel;
- foreach(baserels, root->base_rel_list)
+ /* Already made? */
+ foreach(rels, root->base_rel_list)
{
- rel = (RelOptInfo *) lfirst(baserels);
+ rel = (RelOptInfo *) lfirst(rels);
- /*
- * We know length(rel->relids) == 1 for all members of
- * base_rel_list
- */
+ /* length(rel->relids) == 1 for all members of base_rel_list */
if (lfirsti(rel->relids) == relid)
return rel;
}
+ /* It should not exist as an "other" rel */
+ foreach(rels, root->other_rel_list)
+ {
+ rel = (RelOptInfo *) lfirst(rels);
+
+ if (lfirsti(rel->relids) == relid)
+ elog(ERROR, "build_base_rel: rel already exists as 'other' rel");
+ }
+
/* No existing RelOptInfo for this base rel, so make a new one */
rel = make_base_rel(root, relid);
@@ -68,16 +76,52 @@ get_base_rel(Query *root, int relid)
}
/*
+ * build_other_rel
+ * Returns relation entry corresponding to 'relid', creating a new one
+ * if necessary. This is for 'other' relations, which are just like
+ * base relations except that they live in a different list.
+ */
+RelOptInfo *
+build_other_rel(Query *root, int relid)
+{
+ List *rels;
+ RelOptInfo *rel;
+
+ /* Already made? */
+ foreach(rels, root->other_rel_list)
+ {
+ rel = (RelOptInfo *) lfirst(rels);
+
+ /* length(rel->relids) == 1 for all members of other_rel_list */
+ if (lfirsti(rel->relids) == relid)
+ return rel;
+ }
+
+ /* It should not exist as a base rel */
+ foreach(rels, root->base_rel_list)
+ {
+ rel = (RelOptInfo *) lfirst(rels);
+
+ if (lfirsti(rel->relids) == relid)
+ elog(ERROR, "build_other_rel: rel already exists as base rel");
+ }
+
+ /* No existing RelOptInfo for this other rel, so make a new one */
+ rel = make_base_rel(root, relid);
+
+ /* and add it to the list */
+ root->other_rel_list = lcons(rel, root->other_rel_list);
+
+ return rel;
+}
+
+/*
* make_base_rel
* Construct a base-relation RelOptInfo for the specified rangetable index.
*
- * This is split out of get_base_rel so that inheritance-tree processing can
- * construct baserel nodes for child tables. We need a RelOptInfo so we can
- * plan a suitable access path for each child table, but we do NOT want to
- * enter the child nodes into base_rel_list. In most contexts, get_base_rel
- * should be called instead.
+ * Common code for build_base_rel and build_other_rel.
*/
-RelOptInfo *
+static RelOptInfo *
make_base_rel(Query *root, int relid)
{
RelOptInfo *rel = makeNode(RelOptInfo);
@@ -92,7 +136,7 @@ make_base_rel(Query *root, int relid)
rel->cheapest_total_path = NULL;
rel->pruneable = true;
rel->issubquery = false;
- rel->indexed = false;
+ rel->indexlist = NIL;
rel->pages = 0;
rel->tuples = 0;
rel->subplan = NULL;
@@ -108,8 +152,12 @@ make_base_rel(Query *root, int relid)
if (relationObjectId != InvalidOid)
{
/* Plain relation --- retrieve statistics from the system catalogs */
- relation_info(relationObjectId,
- &rel->indexed, &rel->pages, &rel->tuples);
+ bool indexed;
+
+ get_relation_info(relationObjectId,
+ &indexed, &rel->pages, &rel->tuples);
+ if (indexed)
+ rel->indexlist = find_secondary_indexes(relationObjectId);
}
else
{
@@ -121,12 +169,45 @@ make_base_rel(Query *root, int relid)
}
/*
+ * find_base_rel
+ * Find a base or other relation entry, which must already exist
+ * (since we'd have no idea which list to add it to).
+ */
+RelOptInfo *
+find_base_rel(Query *root, int relid)
+{
+ List *rels;
+ RelOptInfo *rel;
+
+ foreach(rels, root->base_rel_list)
+ {
+ rel = (RelOptInfo *) lfirst(rels);
+
+ /* length(rel->relids) == 1 for all members of base_rel_list */
+ if (lfirsti(rel->relids) == relid)
+ return rel;
+ }
+
+ foreach(rels, root->other_rel_list)
+ {
+ rel = (RelOptInfo *) lfirst(rels);
+
+ if (lfirsti(rel->relids) == relid)
+ return rel;
+ }
+
+ elog(ERROR, "find_base_rel: no relation entry for relid %d", relid);
+
+ return NULL; /* keep compiler quiet */
+}
+
+/*
* find_join_rel
* Returns relation entry corresponding to 'relids' (a list of RT indexes),
* or NULL if none exists. This is for join relations.
*
* Note: there is probably no good reason for this to be called from
- * anywhere except get_join_rel, but keep it as a separate routine
+ * anywhere except build_join_rel, but keep it as a separate routine
* just in case.
*/
static RelOptInfo *
@@ -146,7 +227,7 @@ find_join_rel(Query *root, Relids relids)
}
/*
- * get_join_rel
+ * build_join_rel
* Returns relation entry corresponding to the union of two given rels,
* creating a new relation entry if none already exists.
*
@@ -161,11 +242,11 @@ find_join_rel(Query *root, Relids relids)
* duplicated calculation of the restrictlist...
*/
RelOptInfo *
-get_join_rel(Query *root,
- RelOptInfo *outer_rel,
- RelOptInfo *inner_rel,
- JoinType jointype,
- List **restrictlist_ptr)
+build_join_rel(Query *root,
+ RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel,
+ JoinType jointype,
+ List **restrictlist_ptr)
{
List *joinrelids;
RelOptInfo *joinrel;
@@ -212,7 +293,7 @@ get_join_rel(Query *root,
joinrel->cheapest_total_path = NULL;
joinrel->pruneable = true;
joinrel->issubquery = false;
- joinrel->indexed = false;
+ joinrel->indexlist = NIL;
joinrel->pages = 0;
joinrel->tuples = 0;
joinrel->subplan = NULL;