diff options
Diffstat (limited to 'src/backend/optimizer/util')
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 199 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 6 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 151 | ||||
-rw-r--r-- | src/backend/optimizer/util/relnode.c | 135 |
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; |