From 49ed4dd77910f5993e5c4100bf0ca48f893a898e Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 25 Jul 1999 23:07:26 +0000 Subject: [PATCH] Further work on planning of indexscans. Cleaned up interfaces to index_selectivity so that it can be handed an indexqual clause list rather than a bunch of assorted derivative data. --- src/backend/optimizer/path/clausesel.c | 88 +++++----- src/backend/optimizer/path/indxpath.c | 39 ++--- src/backend/optimizer/path/orindxpath.c | 59 +++---- src/backend/optimizer/util/clauses.c | 202 ++++++++++++---------- src/backend/optimizer/util/pathnode.c | 65 +++---- src/backend/optimizer/util/plancat.c | 185 +++++++++----------- src/backend/optimizer/util/restrictinfo.c | 136 +-------------- src/include/optimizer/clauses.h | 7 +- src/include/optimizer/internal.h | 16 +- src/include/optimizer/plancat.h | 12 +- src/include/optimizer/restrictinfo.h | 7 +- 11 files changed, 319 insertions(+), 497 deletions(-) diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 00d3780a352..23998a54bff 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.24 1999/07/24 23:21:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.25 1999/07/25 23:07:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -142,8 +142,8 @@ compute_clause_selec(Query *root, Node *clause) BooleanEqualOperator, relid, ((Var *) clause)->varoattno, - "t", - _SELEC_CONSTANT_RIGHT_); + Int8GetDatum(true), + SEL_CONSTANT | SEL_RIGHT); } else if (IsA(clause, Param)) { @@ -215,14 +215,6 @@ compute_clause_selec(Query *root, Node *clause) */ 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); /* * if the oprrest procedure is missing for whatever reason, use a @@ -230,22 +222,33 @@ compute_clause_selec(Query *root, Node *clause) */ 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); + { + int relidx; + AttrNumber attno; + Datum constval; + int flag; + + get_relattval(clause, 0, &relidx, &attno, &constval, &flag); + if (relidx <= 0 || attno <= 0) + { + /* + * attno can be Invalid if the clause had a function in it, + * i.e. WHERE myFunc(f) = 10 + * + * XXX should be FIXED to use function selectivity + */ + s1 = (Cost) (0.5); + } + else + s1 = (Cost) restriction_selectivity(oprrest, + opno, + getrelid(relidx, + root->rtable), + attno, + constval, + flag); + } } else { @@ -256,14 +259,6 @@ compute_clause_selec(Query *root, Node *clause) */ Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno; RegProcedure oprjoin = get_oprjoin(opno); - int relid1, - relid2; - AttrNumber attno1, - attno2; - - 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 @@ -272,12 +267,25 @@ compute_clause_selec(Query *root, Node *clause) if (!oprjoin) s1 = (Cost) (0.5); else - s1 = (Cost) join_selectivity(oprjoin, - opno, - relid1, - attno1, - relid2, - attno2); + { + int relid1, + relid2; + AttrNumber attno1, + attno2; + + get_rels_atts(clause, &relid1, &attno1, &relid2, &attno2); + if (relid1 > 0 && relid2 > 0 && attno1 > 0 && attno2 > 0) + s1 = (Cost) join_selectivity(oprjoin, + opno, + getrelid(relid1, + root->rtable), + attno1, + getrelid(relid2, + root->rtable), + attno2); + else /* XXX more code for function selectivity? */ + s1 = (Cost) (0.5); + } } } diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 62996ee7d4c..2f1002c78f5 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.64 1999/07/25 17:53:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.65 1999/07/25 23:07:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1216,25 +1216,20 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index, { List *clausegroup = lfirst(i); IndexPath *pathnode = makeNode(IndexPath); - Cost temp_selec; - float temp_pages; - List *attnos, - *values, - *flags; + List *indexquals; + float npages; + float selec; - get_joinvars(lfirsti(rel->relids), clausegroup, - &attnos, &values, &flags); - index_selectivity(lfirsti(index->relids), - index->classlist, - get_opnos(clausegroup), - getrelid(lfirsti(rel->relids), - root->rtable), - attnos, - values, - flags, - length(clausegroup), - &temp_pages, - &temp_selec); + indexquals = get_actual_clauses(clausegroup); + + index_selectivity(root, + lfirsti(rel->relids), + lfirsti(index->relids), + indexquals, + &npages, + &selec); + + /* XXX this code ought to be merged with create_index_path */ pathnode->path.pathtype = T_IndexScan; pathnode->path.parent = rel; @@ -1249,14 +1244,14 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index, */ pathnode->indexid = index->relids; pathnode->indexkeys = index->indexkeys; - pathnode->indexqual = lcons(get_actual_clauses(clausegroup), NIL); + pathnode->indexqual = lcons(indexquals, NIL); /* joinid saves the rels needed on the outer side of the join */ pathnode->path.joinid = lfirst(outerrelids_list); pathnode->path.path_cost = cost_index((Oid) lfirsti(index->relids), - (int) temp_pages, - temp_selec, + (int) npages, + selec, rel->pages, rel->tuples, index->pages, diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c index 4a511372a53..39fa69cdef8 100644 --- a/src/backend/optimizer/path/orindxpath.c +++ b/src/backend/optimizer/path/orindxpath.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.29 1999/07/24 23:21:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.30 1999/07/25 23:07:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -121,11 +121,14 @@ create_or_index_paths(Query *root, pathnode->indexqual = NIL; foreach(orclause, clausenode->clause->args) { - List *sublist; - if (and_clause(lfirst(orclause))) - sublist = ((Expr *) lfirst(orclause))->args; + Expr *subclause = (Expr *) lfirst(orclause); + List *sublist; + + if (and_clause((Node *) subclause)) + sublist = subclause->args; else - sublist = lcons(lfirst(orclause), NIL); + sublist = lcons(subclause, NIL); + /* expansion call... */ pathnode->indexqual = lappend(pathnode->indexqual, sublist); } @@ -224,18 +227,8 @@ best_or_subclause_index(Query *root, Cost *retCost, /* return value */ Cost *retSelec) /* return value */ { - 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 *indexquals; List *ilist; /* if we don't match anything, return zeros */ @@ -243,37 +236,25 @@ best_or_subclause_index(Query *root, *retCost = (Cost) 0.0; *retSelec = (Cost) 0.0; - if (constant_on_right) /* XXX looks pretty bogus ... tgl */ - value = ((Const *) get_rightop(subclause))->constvalue; + /* convert 'or' subclause to an indexqual list */ + if (and_clause((Node *) subclause)) + indexquals = subclause->args; 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); + indexquals = lcons(subclause, NIL); + /* expansion call... */ foreach(ilist, indices) { RelOptInfo *index = (RelOptInfo *) lfirst(ilist); Oid indexid = (Oid) lfirsti(index->relids); Cost subcost; - float npages, - selec; + float npages; + float selec; - index_selectivity(indexid, - index->classlist, - opnos, - relid, - attnos, - values, - flags, - 1, + index_selectivity(root, + lfirsti(rel->relids), + indexid, + indexquals, &npages, &selec); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 5c9e4175ade..442ebad1e7b 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.41 1999/07/24 23:21:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.42 1999/07/25 23:07:25 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -29,6 +29,7 @@ static bool fix_opid_walker(Node *node, void *context); +static int is_single_func(Node *node); Expr * @@ -533,36 +534,36 @@ fix_opids(List *clauses) /* * get_relattval - * For a non-join clause, returns a list consisting of the - * relid, - * attno, - * value of the CONST node (if any), and a - * flag indicating whether the value appears on the left or right - * of the operator and whether the value varied. - * - * OLD OBSOLETE COMMENT FOLLOWS: - * If 'clause' is not of the format (op var node) or (op node var), - * or if the var refers to a nested attribute, then -1's are returned for - * everything but the value a blank string "" (pointer to \0) is - * returned for the value if it is unknown or null. - * END OF OLD OBSOLETE COMMENT. - * NEW COMMENT: - * when defining rules one of the attributes of the operator can - * be a Param node (which is supposed to be treated as a constant). - * However as there is no value specified for a parameter until run time - * this routine used to return "" as value, which caused 'compute_selec' - * to bomb (because it was expecting a lisp integer and got back a lisp - * string). Now the code returns a plain old good "lispInteger(0)". + * 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 -1 for the relid and attno, 0 for the constant value. + * Note that InvalidAttrNumber is *not* -1, but 0. */ void get_relattval(Node *clause, + int targetrelid, int *relid, AttrNumber *attno, Datum *constval, int *flag) { Var *left, - *right; + *right, + *other; + int funcvarno; /* Careful; the passed clause might not be a binary operator at all */ @@ -575,71 +576,96 @@ get_relattval(Node *clause, if (!right) goto default_results; - if (IsA(left, Var) &&IsA(right, Const)) + /* First look for the var or func */ + + if (IsA(left, Var) && + (targetrelid == 0 || targetrelid == left->varno)) { *relid = left->varno; *attno = left->varattno; - *constval = ((Const *) right)->constvalue; - *flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_); + *flag = SEL_RIGHT; } - else if (IsA(left, Var) &&IsA(right, Param)) - { - *relid = left->varno; - *attno = left->varattno; - *constval = 0; - *flag = (_SELEC_NOT_CONSTANT_); - } - else if (is_funcclause((Node *) left) && IsA(right, Const)) - { - List *vars = pull_var_clause((Node *) left); - - *relid = ((Var *) lfirst(vars))->varno; - *attno = InvalidAttrNumber; - *constval = ((Const *) right)->constvalue; - *flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_); - } - else if (IsA(right, Var) &&IsA(left, Const)) + else if (IsA(right, Var) && + (targetrelid == 0 || targetrelid == right->varno)) { *relid = right->varno; *attno = right->varattno; - *constval = ((Const *) left)->constvalue; - *flag = (_SELEC_IS_CONSTANT_); + *flag = 0; } - else if (IsA(right, Var) &&IsA(left, Param)) + else if ((funcvarno = is_single_func((Node *) left)) != 0 && + (targetrelid == 0 || targetrelid == funcvarno)) { - *relid = right->varno; - *attno = right->varattno; - *constval = 0; - *flag = (_SELEC_NOT_CONSTANT_); - } - else if (is_funcclause((Node *) right) && IsA(left, Const)) - { - List *vars = pull_var_clause((Node *) right); - - *relid = ((Var *) lfirst(vars))->varno; + *relid = funcvarno; *attno = InvalidAttrNumber; - *constval = ((Const *) left)->constvalue; - *flag = (_SELEC_IS_CONSTANT_); + *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 = _SELEC_VALUE_UNKNOWN_; - *attno = _SELEC_VALUE_UNKNOWN_; + *relid = -1; + *attno = -1; + *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)) + { + *constval = ((Const *) other)->constvalue; + *flag |= SEL_CONSTANT; + } + else + { *constval = 0; - *flag = (_SELEC_NOT_CONSTANT_); } } /* - * get_relsatts + * 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 *vars = pull_var_clause(node); + + if (vars != NIL) + { + int funcvarno = ((Var *) lfirst(vars))->varno; + /* need to check that all args of func are same relation */ + while ((vars = lnext(vars)) != NIL) + { + if (((Var *) lfirst(vars))->varno != funcvarno) + return 0; + } + return funcvarno; + } + } + return 0; +} + +/* + * get_rels_atts * - * Returns a list + * Returns the info * ( relid1 attno1 relid2 attno2 ) * for a joinclause. * - * If the clause is not of the form (op var var) or if any of the vars + * If the clause is not of the form (var op var) or if any of the vars * refer to nested attributes, then -1's are returned. * */ @@ -650,6 +676,12 @@ get_rels_atts(Node *clause, int *relid2, AttrNumber *attno2) { + /* set default values */ + *relid1 = -1; + *attno1 = -1; + *relid2 = -1; + *attno2 = -1; + if (is_opclause(clause)) { Var *left = get_leftop((Expr *) clause); @@ -657,47 +689,31 @@ get_rels_atts(Node *clause, if (left && right) { - bool var_left = IsA(left, Var); - bool var_right = IsA(right, Var); - bool varexpr_left = (bool) ((IsA(left, Func) ||IsA(left, Oper)) && - contain_var_clause((Node *) left)); - bool varexpr_right = (bool) ((IsA(right, Func) ||IsA(right, Oper)) && - contain_var_clause((Node *) right)); + int funcvarno; - if (var_left && var_right) + if (IsA(left, Var)) { - *relid1 = left->varno; - *attno1 = left->varoattno; - *relid2 = right->varno; - *attno2 = right->varoattno; - return; + *attno1 = left->varattno; } - if (var_left && varexpr_right) + else if ((funcvarno = is_single_func((Node *) left)) != 0) { - - *relid1 = left->varno; - *attno1 = left->varoattno; - *relid2 = _SELEC_VALUE_UNKNOWN_; - *attno2 = _SELEC_VALUE_UNKNOWN_; - return; + *relid1 = funcvarno; + *attno1 = InvalidAttrNumber; } - if (varexpr_left && var_right) - { - *relid1 = _SELEC_VALUE_UNKNOWN_; - *attno1 = _SELEC_VALUE_UNKNOWN_; + if (IsA(right, Var)) + { *relid2 = right->varno; - *attno2 = right->varoattno; - return; + *attno2 = right->varattno; + } + else if ((funcvarno = is_single_func((Node *) right)) != 0) + { + *relid2 = funcvarno; + *attno2 = InvalidAttrNumber; } } } - - *relid1 = _SELEC_VALUE_UNKNOWN_; - *attno1 = _SELEC_VALUE_UNKNOWN_; - *relid2 = _SELEC_VALUE_UNKNOWN_; - *attno2 = _SELEC_VALUE_UNKNOWN_; } /*-------------------- diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 52689d96a4d..658ee57baaa 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.47 1999/07/24 23:21:14 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.48 1999/07/25 23:07:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -334,7 +334,12 @@ create_index_path(Query *root, pathnode->path.pathorder = makeNode(PathOrder); pathnode->path.pathorder->ordtype = SORTOP_ORDER; 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 (unless indexqual is empty). + */ pathnode->indexid = index->relids; pathnode->indexkeys = index->indexkeys; pathnode->indexqual = NIL; @@ -344,7 +349,7 @@ create_index_path(Query *root, * JMH, 7/7/92 */ pathnode->path.loc_restrictinfo = set_difference((List *) copyObject((Node *) rel->restrictinfo), - (List *) restriction_clauses); + restriction_clauses); /* * The index must have an ordering for the path to have (ordering) @@ -385,49 +390,28 @@ create_index_path(Query *root, index->pages, index->tuples, false); -#ifdef NOT_USED - /* add in expensive functions cost! -- JMH, 7/7/92 */ - if (XfuncMode != XFUNC_OFF) - { - pathnode->path_cost = (pathnode->path_cost + - xfunc_get_path_cost((Path *) pathnode)); - } -#endif } else { - /* * Compute scan cost for the case when 'index' is used with a * restriction clause. */ - List *attnos; - List *values; - List *flags; + List *indexquals; float npages; float selec; Cost clausesel; - get_relattvals(restriction_clauses, - &attnos, - &values, - &flags); - index_selectivity(lfirsti(index->relids), - index->classlist, - get_opnos(restriction_clauses), - getrelid(lfirsti(rel->relids), - root->rtable), - attnos, - values, - flags, - length(restriction_clauses), + indexquals = get_actual_clauses(restriction_clauses); + + index_selectivity(root, + lfirsti(rel->relids), + lfirsti(index->relids), + indexquals, &npages, &selec); - /* each clause gets an equal selectivity */ - clausesel = pow(selec, 1.0 / (double) length(restriction_clauses)); - pathnode->indexqual = lcons(get_actual_clauses(restriction_clauses), - NIL); + pathnode->indexqual = lcons(indexquals, NIL); pathnode->path.path_cost = cost_index(lfirsti(index->relids), (int) npages, selec, @@ -437,21 +421,24 @@ create_index_path(Query *root, index->tuples, false); -#ifdef NOT_USED - /* add in expensive functions cost! -- JMH, 7/7/92 */ - if (XfuncMode != XFUNC_OFF) - pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode); -#endif - /* * Set selectivities of clauses used with index to the selectivity * of this index, subdividing the selectivity equally over each of * the clauses. + * XXX Can this divide the selectivities in a better way? + * XXX In fact, why the heck are we doing this at all? We already + * set the cost for the indexpath. */ - - /* XXX Can this divide the selectivities in a better way? */ + clausesel = pow(selec, 1.0 / (double) length(restriction_clauses)); set_clause_selectivities(restriction_clauses, clausesel); } + +#ifdef NOT_USED + /* add in expensive functions cost! -- JMH, 7/7/92 */ + if (XfuncMode != XFUNC_OFF) + pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode); +#endif + return pathnode; } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index deb1d021165..a428e027f61 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.35 1999/07/17 20:17:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.36 1999/07/25 23:07:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,16 +20,20 @@ #include "catalog/catname.h" #include "catalog/pg_amop.h" #include "catalog/pg_inherits.h" +#include "optimizer/clauses.h" #include "optimizer/internal.h" #include "optimizer/plancat.h" #include "parser/parsetree.h" #include "utils/syscache.h" -static void IndexSelectivity(Oid indexrelid, Oid indrelid, int32 nIndexKeys, - Oid *AccessMethodOperatorClasses, Oid *operatorObjectIds, - int32 *varAttributeNumbers, char **constValues, int32 *constFlags, - float *idxPages, float *idxSelec); +static void IndexSelectivity(Oid indexrelid, Oid baserelid, int nIndexKeys, + Oid *operatorObjectIds, + AttrNumber *varAttributeNumbers, + Datum *constValues, + int *constFlags, + float *idxPages, + float *idxSelec); /* @@ -212,85 +216,75 @@ index_info(Query *root, bool first, int relid, IdxInfoRetval *info) /* * index_selectivity + * Estimate the selectivity of an index scan with the given index quals. * - * Call util/plancat.c:IndexSelectivity with the indicated arguments. - * - * 'indid' is the index OID - * 'classes' is a list of index key classes - * 'opnos' is a list of index key operator OIDs - * 'relid' is the OID of the relation indexed - * 'attnos' is a list of the relation attnos which the index keys over - * 'values' is a list of the values of the clause's constants - * 'flags' is a list of fixnums which describe the constants - * 'nkeys' is the number of index keys - * - * Returns two floats: index pages and index selectivity in 'idxPages' and - * 'idxSelec'. + * NOTE: an indexscan plan node can actually represent several passes, + * but here we consider the cost of just one pass. * + * 'root' is the query root + * 'relid' is the RT index of the relation being scanned + * 'indexid' is the OID of the index to be used + * 'indexquals' is the list of qual condition exprs (implicit AND semantics) + * '*idxPages' receives an estimate of the number of index pages touched + * '*idxSelec' receives an estimate of selectivity of the scan */ void -index_selectivity(Oid indid, - Oid *classes, - List *opnos, - Oid relid, - List *attnos, - List *values, - List *flags, - int32 nkeys, +index_selectivity(Query *root, + int relid, + Oid indexid, + List *indexquals, float *idxPages, float *idxSelec) { + int nclauses = length(indexquals); Oid *opno_array; - int *attno_array, - *flag_array; - char **value_array; - int i = 0; - List *xopno, - *xattno, - *value, - *flag; + AttrNumber *attno_array; + Datum *value_array; + int *flag_array; + List *q; + int i; - if (length(opnos) != nkeys || length(attnos) != nkeys || - length(values) != nkeys || length(flags) != nkeys) + if (nclauses <= 0) { - *idxPages = 0.0; *idxSelec = 1.0; return; } - - opno_array = (Oid *) palloc(nkeys * sizeof(Oid)); - attno_array = (int *) palloc(nkeys * sizeof(int32)); - value_array = (char **) palloc(nkeys * sizeof(char *)); - flag_array = (int *) palloc(nkeys * sizeof(int32)); + opno_array = (Oid *) palloc(nclauses * sizeof(Oid)); + attno_array = (AttrNumber *) palloc(nclauses * sizeof(AttrNumber)); + value_array = (Datum *) palloc(nclauses * sizeof(Datum)); + flag_array = (int *) palloc(nclauses * sizeof(int)); i = 0; - foreach(xopno, opnos) - opno_array[i++] = lfirsti(xopno); + foreach(q, indexquals) + { + Node *expr = (Node *) lfirst(q); + int dummyrelid; - i = 0; - foreach(xattno, attnos) - attno_array[i++] = lfirsti(xattno); + if (is_opclause(expr)) + opno_array[i] = ((Oper *) ((Expr *) expr)->oper)->opno; + else + opno_array[i] = InvalidOid; - i = 0; - foreach(value, values) - value_array[i++] = (char *) lfirst(value); + get_relattval(expr, relid, &dummyrelid, &attno_array[i], + &value_array[i], &flag_array[i]); + i++; + } - i = 0; - foreach(flag, flags) - flag_array[i++] = lfirsti(flag); - - IndexSelectivity(indid, - relid, - nkeys, - classes, /* not used */ + IndexSelectivity(indexid, + getrelid(relid, root->rtable), + nclauses, opno_array, attno_array, value_array, flag_array, idxPages, idxSelec); - return; + + pfree(opno_array); + pfree(attno_array); + pfree(value_array); + pfree(flag_array); } /* @@ -312,8 +306,8 @@ restriction_selectivity(Oid functionObjectId, Oid operatorObjectId, Oid relationObjectId, AttrNumber attributeNumber, - char *constValue, - int32 constFlag) + Datum constValue, + int constFlag) { float64 result; @@ -456,35 +450,29 @@ VersionGetParents(Oid verrelid) *****************************************************************************/ /* - * IdexSelectivity + * IndexSelectivity * - * Retrieves the 'amopnpages' and 'amopselect' parameters for each + * Calls the 'amopnpages' and 'amopselect' functions for each * AM operator when a given index (specified by 'indexrelid') is used. - * These two parameters are returned by copying them to into an array of - * floats. + * The total number of pages and product of the selectivities are returned. * * Assumption: the attribute numbers and operator ObjectIds are in order * WRT to each other (otherwise, you have no way of knowing which * AM operator class or attribute number corresponds to which operator. * + * 'nIndexKeys' is the number of qual clauses in use * 'varAttributeNumbers' contains attribute numbers for variables * 'constValues' contains the constant values * 'constFlags' describes how to treat the constants in each clause - * 'nIndexKeys' describes how many keys the index actually has - * - * Returns 'selectivityInfo' filled with the sum of all pages touched - * and the product of each clause's selectivity. - * */ static void IndexSelectivity(Oid indexrelid, - Oid indrelid, - int32 nIndexKeys, - Oid *AccessMethodOperatorClasses, /* XXX not used? */ + Oid baserelid, + int nIndexKeys, Oid *operatorObjectIds, - int32 *varAttributeNumbers, - char **constValues, - int32 *constFlags, + AttrNumber *varAttributeNumbers, + Datum *constValues, + int *constFlags, float *idxPages, float *idxSelec) { @@ -493,6 +481,7 @@ IndexSelectivity(Oid indexrelid, HeapTuple indexTuple, amopTuple, indRel; + Form_pg_class indexrelation; Form_pg_index index; Form_pg_amop amop; Oid indclass; @@ -510,7 +499,8 @@ IndexSelectivity(Oid indexrelid, if (!HeapTupleIsValid(indRel)) elog(ERROR, "IndexSelectivity: index %u not found", indexrelid); - relam = ((Form_pg_class) GETSTRUCT(indRel))->relam; + indexrelation = (Form_pg_class) GETSTRUCT(indRel); + relam = indexrelation->relam; indexTuple = SearchSysCacheTuple(INDEXRELID, ObjectIdGetDatum(indexrelid), @@ -530,9 +520,8 @@ IndexSelectivity(Oid indexrelid, npages = 0.0; select = 1.0; - for (n = 0; n < nIndexKeys; ++n) + for (n = 0; n < nIndexKeys; n++) { - /* * Find the AM class for this key. * @@ -567,49 +556,43 @@ IndexSelectivity(Oid indexrelid, amopTuple = SearchSysCacheTuple(AMOPOPID, ObjectIdGetDatum(indclass), - ObjectIdGetDatum(operatorObjectIds[n]), + ObjectIdGetDatum(operatorObjectIds[n]), ObjectIdGetDatum(relam), 0); if (!HeapTupleIsValid(amopTuple)) - elog(ERROR, "IndexSelectivity: no amop %u %u", - indclass, operatorObjectIds[n]); + elog(ERROR, "IndexSelectivity: no amop %u %u %u", + indclass, operatorObjectIds[n], relam); amop = (Form_pg_amop) GETSTRUCT(amopTuple); if (!nphack) { amopnpages = (float64) fmgr(amop->amopnpages, (char *) operatorObjectIds[n], - (char *) indrelid, - (char *) varAttributeNumbers[n], + (char *) baserelid, + (char *) (int) varAttributeNumbers[n], (char *) constValues[n], (char *) constFlags[n], (char *) nIndexKeys, (char *) indexrelid); -#ifdef NOT_USED -/* - * So cool guys! Npages for x > 10 and x < 20 is twice as - * npages for x > 10! - vadim 04/09/97 - */ - npages += PointerIsValid(amopnpages) ? *amopnpages : 0.0; - if ((i = npages) < npages) /* ceil(npages)? */ - npages += 1.0; -#endif - npages += PointerIsValid(amopnpages) ? *amopnpages : 0.0; + if (PointerIsValid(amopnpages)) + npages += *amopnpages; } amopselect = (float64) fmgr(amop->amopselect, (char *) operatorObjectIds[n], - (char *) indrelid, - (char *) varAttributeNumbers[n], + (char *) baserelid, + (char *) (int) varAttributeNumbers[n], (char *) constValues[n], (char *) constFlags[n], (char *) nIndexKeys, (char *) indexrelid); - if (nphack && varAttributeNumbers[n] == index->indkey[0]) - fattr_select *= PointerIsValid(amopselect) ? *amopselect : 1.0; - - select *= PointerIsValid(amopselect) ? *amopselect : 1.0; + if (PointerIsValid(amopselect)) + { + select *= *amopselect; + if (nphack && varAttributeNumbers[n] == index->indkey[0]) + fattr_select *= *amopselect; + } } /* @@ -618,7 +601,7 @@ IndexSelectivity(Oid indexrelid, */ if (nphack) { - npages = fattr_select * ((Form_pg_class) GETSTRUCT(indRel))->relpages; + npages = fattr_select * indexrelation->relpages; *idxPages = ceil((double) npages); } else diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index 4dfdf66fcbc..a77656e974b 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.7 1999/07/24 23:21:14 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.8 1999/07/25 23:07:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,144 +44,14 @@ restriction_is_or_clause(RestrictInfo *restrictinfo) List * get_actual_clauses(List *restrictinfo_list) { - List *temp = NIL; List *result = NIL; - RestrictInfo *clause = (RestrictInfo *) NULL; - - foreach(temp, restrictinfo_list) - { - clause = (RestrictInfo *) lfirst(temp); - result = lappend(result, clause->clause); - } - return result; -} - -/* - * XXX NOTE: - * The following routines must return their contents in the same order - * (e.g., the first clause's info should be first, and so on) or else - * get_index_sel() won't work. - * - */ - -/* - * get_relattvals - * For each member of a list of restrictinfo nodes to be used with an - * index, create a vectori-long specifying: - * the attnos, - * the values of the clause constants, and - * flags indicating the type and location of the constant within - * each clause. - * Each clause is of the form (op var some_type_of_constant), thus the - * flag indicating whether the constant is on the left or right should - * always be *SELEC-CONSTANT-RIGHT*. - * - * 'restrictinfo_list' is a list of restrictinfo nodes - * - * Returns a list of vectori-longs. - * - */ -void -get_relattvals(List *restrictinfo_list, - List **attnos, - List **values, - List **flags) -{ - List *result1 = NIL; - List *result2 = NIL; - List *result3 = NIL; - RestrictInfo *temp = (RestrictInfo *) NULL; - List *i = NIL; - - foreach(i, restrictinfo_list) - { - int dummy; - AttrNumber attno; - Datum constval; - int flag; - - temp = (RestrictInfo *) lfirst(i); - get_relattval((Node *) temp->clause, &dummy, &attno, &constval, &flag); - result1 = lappendi(result1, (int) attno); - result2 = lappendi(result2, constval); - result3 = lappendi(result3, flag); - } - - *attnos = result1; - *values = result2; - *flags = result3; - return; -} - -/* - * get_joinvars - * Given a list of join restrictinfo nodes to be used with the index - * of an inner join relation, return three lists consisting of: - * the attributes corresponding to the inner join relation - * the value of the inner var clause (always "") - * whether the attribute appears on the left or right side of - * the operator. - * - * 'relid' is the inner join relation - * 'restrictinfo_list' is a list of qualification clauses to be used with - * 'rel' - * - */ -void -get_joinvars(Oid relid, - List *restrictinfo_list, - List **attnos, - List **values, - List **flags) -{ - List *result1 = NIL; - List *result2 = NIL; - List *result3 = NIL; List *temp; foreach(temp, restrictinfo_list) { - RestrictInfo *restrictinfo = lfirst(temp); - Expr *clause = restrictinfo->clause; + RestrictInfo *clause = (RestrictInfo *) lfirst(temp); - if (IsA(get_leftop(clause), Var) && - (relid == (get_leftop(clause))->varno)) - { - result1 = lappendi(result1, (int4) (get_leftop(clause))->varattno); - result2 = lappend(result2, ""); - result3 = lappendi(result3, _SELEC_CONSTANT_RIGHT_); - } - else - { - result1 = lappendi(result1, (int4) (get_rightop(clause))->varattno); - result2 = lappend(result2, ""); - result3 = lappendi(result3, _SELEC_CONSTANT_LEFT_); - } - } - *attnos = result1; - *values = result2; - *flags = result3; - return; -} - -/* - * get_opnos - * Create and return a list containing the clause operators of each member - * of a list of restrictinfo nodes to be used with an index. - * - */ -List * -get_opnos(List *restrictinfo_list) -{ - RestrictInfo *temp = (RestrictInfo *) NULL; - List *result = NIL; - List *i = NIL; - - foreach(i, restrictinfo_list) - { - temp = (RestrictInfo *) lfirst(i); - result = lappendi(result, - (((Oper *) temp->clause->oper)->opno)); + result = lappend(result, clause->clause); } return result; } diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index e362b7c64d9..748c0d46e9f 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.22 1999/07/24 23:21:05 tgl Exp $ + * $Id: clauses.h,v 1.23 1999/07/25 23:07:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,8 +45,9 @@ extern bool is_joinable(Node *clause); extern bool qual_clause_p(Node *clause); extern void fix_opid(Node *clause); extern List *fix_opids(List *clauses); -extern void get_relattval(Node *clause, int *relid, - AttrNumber *attno, Datum *constval, int *flag); +extern void get_relattval(Node *clause, int targetrelid, + int *relid, AttrNumber *attno, + Datum *constval, int *flag); extern void get_rels_atts(Node *clause, int *relid1, AttrNumber *attno1, int *relid2, AttrNumber *attno2); extern void CommuteClause(Node *clause); diff --git a/src/include/optimizer/internal.h b/src/include/optimizer/internal.h index faa573bbe76..5cf04f70ef3 100644 --- a/src/include/optimizer/internal.h +++ b/src/include/optimizer/internal.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: internal.h,v 1.22 1999/07/15 15:21:21 momjian Exp $ + * $Id: internal.h,v 1.23 1999/07/25 23:07:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -63,20 +63,6 @@ /* used to be -1 */ #define _NONAME_RELATION_ID_ InvalidOid -/* Identifier for invalid relation OIDs and attribute numbers for use by - * selectivity functions - */ -#define _SELEC_VALUE_UNKNOWN_ (-1) - -/* Flag indicating that a clause constant is really a parameter (or other - * non-constant?), a non-parameter, or a constant on the right side - * of the clause. - */ -#define _SELEC_NOT_CONSTANT_ 0 -#define _SELEC_IS_CONSTANT_ 1 -#define _SELEC_CONSTANT_LEFT_ 0 -#define _SELEC_CONSTANT_RIGHT_ 2 - /* #define deactivate_joininfo(joininfo) joininfo->inactive=true*/ /*#define joininfo_inactive(joininfo) joininfo->inactive */ diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h index 90c5e3e099f..9a12ecb1d8c 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: plancat.h,v 1.12 1999/07/15 23:03:58 momjian Exp $ + * $Id: plancat.h,v 1.13 1999/07/25 23:07:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -46,12 +46,12 @@ extern Cost restriction_selectivity(Oid functionObjectId, Oid operatorObjectId, Oid relationObjectId, AttrNumber attributeNumber, - char *constValue, - int32 constFlag); + Datum constValue, + int constFlag); -extern void index_selectivity(Oid indid, Oid *classes, List *opnos, - Oid relid, List *attnos, List *values, List *flags, - int32 nkeys, float *idxPages, float *idxSelec); +extern void index_selectivity(Query *root, int relid, Oid indexid, + List *indexquals, + float *idxPages, float *idxSelec); extern Cost join_selectivity(Oid functionObjectId, Oid operatorObjectId, Oid relationObjectId1, AttrNumber attributeNumber1, diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h index 7412ca46fa0..e6b5e2d89b1 100644 --- a/src/include/optimizer/restrictinfo.h +++ b/src/include/optimizer/restrictinfo.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: restrictinfo.h,v 1.6 1999/07/24 23:21:05 tgl Exp $ + * $Id: restrictinfo.h,v 1.7 1999/07/25 23:07:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,10 +17,5 @@ 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); -extern void get_joinvars(Oid relid, List *restrictinfo_list, - List **attnos, List **values, List **flags); -extern List *get_opnos(List *restrictinfo_list); #endif /* RESTRICTINFO_H */