diff options
Diffstat (limited to 'src/backend/optimizer/path/indxpath.c')
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 681 |
1 files changed, 15 insertions, 666 deletions
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 1b488d191e8..9c1874d5907 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.182 2005/06/09 04:18:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.183 2005/06/10 22:25:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,30 +17,22 @@ #include <math.h> -#include "access/nbtree.h" -#include "catalog/pg_amop.h" -#include "catalog/pg_namespace.h" +#include "access/skey.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" -#include "catalog/pg_proc.h" #include "catalog/pg_type.h" -#include "executor/executor.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/predtest.h" #include "optimizer/restrictinfo.h" -#include "optimizer/var.h" -#include "parser/parse_expr.h" -#include "rewrite/rewriteManip.h" #include "utils/builtins.h" -#include "utils/catcache.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/pg_locale.h" #include "utils/selfuncs.h" -#include "utils/syscache.h" /* @@ -68,8 +60,6 @@ static bool match_clause_to_indexcol(IndexOptInfo *index, Relids outer_relids); static Oid indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left); -static bool pred_test_recurse(Node *clause, Node *predicate); -static bool pred_test_simple_clause(Expr *predicate, Node *clause); static Relids indexable_outerrelids(RelOptInfo *rel); static bool matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel, Relids outer_relids); @@ -266,8 +256,8 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, all_clauses = list_concat(list_copy(clauses), outer_clauses); - if (!pred_test(index->indpred, all_clauses) || - pred_test(index->indpred, outer_clauses)) + if (!predicate_implied_by(index->indpred, all_clauses) || + predicate_implied_by(index->indpred, outer_clauses)) continue; } @@ -497,9 +487,9 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths) * as can happen if there are multiple possibly usable indexes. For * this we look only at plain IndexPath inputs, not at sub-OR clauses. * And we consider an index redundant if all its index conditions were - * already used by earlier indexes. (We could use pred_test() to have - * a more intelligent, but much more expensive, check --- but in most - * cases simple pointer equality should suffice, since after all the + * already used by earlier indexes. (We could use predicate_implied_by + * to have a more intelligent, but much more expensive, check --- but in + * most cases simple pointer equality should suffice, since after all the * index conditions are all coming from the same RestrictInfo lists.) * * XXX is there any risk of throwing away a useful partial index here @@ -867,40 +857,6 @@ check_partial_indexes(PlannerInfo *root, RelOptInfo *rel) List *restrictinfo_list = rel->baserestrictinfo; ListCell *ilist; - foreach(ilist, rel->indexlist) - { - IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); - - /* - * If this is a partial index, we can only use it if it passes the - * predicate test. - */ - if (index->indpred == NIL) - continue; /* ignore non-partial indexes */ - - index->predOK = pred_test(index->indpred, restrictinfo_list); - } -} - -/* - * pred_test - * Does the "predicate inclusion test" for partial indexes. - * - * Recursively checks whether the clauses in restrictinfo_list imply - * that the given predicate is true. - * - * The top-level List structure of each list corresponds to an AND list. - * We assume that eval_const_expressions() has been applied and so there - * are no un-flattened ANDs or ORs (e.g., no AND immediately within an AND, - * including AND just below the top-level List structure). - * If this is not true we might fail to prove an implication that is - * valid, but no worse consequences will ensue. - */ -bool -pred_test(List *predicate_list, List *restrictinfo_list) -{ - ListCell *item; - /* * Note: if Postgres tried to optimize queries by forming equivalence * classes over equi-joined attributes (i.e., if it recognized that a @@ -908,631 +864,24 @@ pred_test(List *predicate_list, List *restrictinfo_list) * an index on c.d), then we could use that equivalence class info * here with joininfo lists to do more complete tests for the usability * of a partial index. For now, the test only uses restriction - * clauses (those in restrictinfo_list). --Nels, Dec '92 + * clauses (those in baserestrictinfo). --Nels, Dec '92 * * XXX as of 7.1, equivalence class info *is* available. Consider * improving this code as foreseen by Nels. */ - if (predicate_list == NIL) - return true; /* no predicate: the index is usable */ - if (restrictinfo_list == NIL) - return false; /* no restriction clauses: the test must - * fail */ - - /* - * In all cases where the predicate is an AND-clause, pred_test_recurse() - * will prefer to iterate over the predicate's components. So we can - * just do that to start with here, and eliminate the need for - * pred_test_recurse() to handle a bare List on the predicate side. - * - * Logic is: restriction must imply each of the AND'ed predicate items. - */ - foreach(item, predicate_list) - { - if (!pred_test_recurse((Node *) restrictinfo_list, lfirst(item))) - return false; - } - return true; -} - - -/*---------- - * pred_test_recurse - * Does the "predicate inclusion test" for non-NULL restriction and - * predicate clauses. - * - * The logic followed here is ("=>" means "implies"): - * atom A => atom B iff: pred_test_simple_clause says so - * atom A => AND-expr B iff: A => each of B's components - * atom A => OR-expr B iff: A => any of B's components - * AND-expr A => atom B iff: any of A's components => B - * AND-expr A => AND-expr B iff: A => each of B's components - * AND-expr A => OR-expr B iff: A => any of B's components, - * *or* any of A's components => B - * OR-expr A => atom B iff: each of A's components => B - * OR-expr A => AND-expr B iff: A => each of B's components - * OR-expr A => OR-expr B iff: each of A's components => any of B's - * - * An "atom" is anything other than an AND or OR node. Notice that we don't - * have any special logic to handle NOT nodes; these should have been pushed - * down or eliminated where feasible by prepqual.c. - * - * We can't recursively expand either side first, but have to interleave - * the expansions per the above rules, to be sure we handle all of these - * examples: - * (x OR y) => (x OR y OR z) - * (x AND y AND z) => (x AND y) - * (x AND y) => ((x AND y) OR z) - * ((x OR y) AND z) => (x OR y) - * This is still not an exhaustive test, but it handles most normal cases - * under the assumption that both inputs have been AND/OR flattened. - * - * A bare List node on the restriction side is interpreted as an AND clause, - * in order to handle the top-level restriction List properly. However we - * need not consider a List on the predicate side since pred_test() already - * expanded it. - * - * We have to be prepared to handle RestrictInfo nodes in the restrictinfo - * tree, though not in the predicate tree. - *---------- - */ -static bool -pred_test_recurse(Node *clause, Node *predicate) -{ - ListCell *item; - - Assert(clause != NULL); - /* skip through RestrictInfo */ - if (IsA(clause, RestrictInfo)) - { - clause = (Node *) ((RestrictInfo *) clause)->clause; - Assert(clause != NULL); - Assert(!IsA(clause, RestrictInfo)); - } - Assert(predicate != NULL); - - /* - * Since a restriction List clause is handled the same as an AND clause, - * we can avoid duplicate code like this: - */ - if (and_clause(clause)) - clause = (Node *) ((BoolExpr *) clause)->args; - - if (IsA(clause, List)) - { - if (and_clause(predicate)) - { - /* AND-clause => AND-clause if A implies each of B's items */ - foreach(item, ((BoolExpr *) predicate)->args) - { - if (!pred_test_recurse(clause, lfirst(item))) - return false; - } - return true; - } - else if (or_clause(predicate)) - { - /* AND-clause => OR-clause if A implies any of B's items */ - /* Needed to handle (x AND y) => ((x AND y) OR z) */ - foreach(item, ((BoolExpr *) predicate)->args) - { - if (pred_test_recurse(clause, lfirst(item))) - return true; - } - /* Also check if any of A's items implies B */ - /* Needed to handle ((x OR y) AND z) => (x OR y) */ - foreach(item, (List *) clause) - { - if (pred_test_recurse(lfirst(item), predicate)) - return true; - } - return false; - } - else - { - /* AND-clause => atom if any of A's items implies B */ - foreach(item, (List *) clause) - { - if (pred_test_recurse(lfirst(item), predicate)) - return true; - } - return false; - } - } - else if (or_clause(clause)) - { - if (or_clause(predicate)) - { - /* - * OR-clause => OR-clause if each of A's items implies any of - * B's items. Messy but can't do it any more simply. - */ - foreach(item, ((BoolExpr *) clause)->args) - { - Node *citem = lfirst(item); - ListCell *item2; - - foreach(item2, ((BoolExpr *) predicate)->args) - { - if (pred_test_recurse(citem, lfirst(item2))) - break; - } - if (item2 == NULL) - return false; /* doesn't imply any of B's */ - } - return true; - } - else - { - /* OR-clause => AND-clause if each of A's items implies B */ - /* OR-clause => atom if each of A's items implies B */ - foreach(item, ((BoolExpr *) clause)->args) - { - if (!pred_test_recurse(lfirst(item), predicate)) - return false; - } - return true; - } - } - else - { - if (and_clause(predicate)) - { - /* atom => AND-clause if A implies each of B's items */ - foreach(item, ((BoolExpr *) predicate)->args) - { - if (!pred_test_recurse(clause, lfirst(item))) - return false; - } - return true; - } - else if (or_clause(predicate)) - { - /* atom => OR-clause if A implies any of B's items */ - foreach(item, ((BoolExpr *) predicate)->args) - { - if (pred_test_recurse(clause, lfirst(item))) - return true; - } - return false; - } - else - { - /* atom => atom is the base case */ - return pred_test_simple_clause((Expr *) predicate, clause); - } - } -} - - -/* - * Define an "operator implication table" for btree operators ("strategies"). - * - * The strategy numbers defined by btree indexes (see access/skey.h) are: - * (1) < (2) <= (3) = (4) >= (5) > - * and in addition we use (6) to represent <>. <> is not a btree-indexable - * operator, but we assume here that if the equality operator of a btree - * opclass has a negator operator, the negator behaves as <> for the opclass. - * - * The interpretation of: - * - * test_op = BT_implic_table[given_op-1][target_op-1] - * - * where test_op, given_op and target_op are strategy numbers (from 1 to 6) - * of btree operators, is as follows: - * - * If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you - * want to determine whether "ATTR target_op CONST2" must also be true, then - * you can use "CONST2 test_op CONST1" as a test. If this test returns true, - * then the target expression must be true; if the test returns false, then - * the target expression may be false. - * - * An entry where test_op == 0 means the implication cannot be determined, - * i.e., this test should always be considered false. - */ - -#define BTLT BTLessStrategyNumber -#define BTLE BTLessEqualStrategyNumber -#define BTEQ BTEqualStrategyNumber -#define BTGE BTGreaterEqualStrategyNumber -#define BTGT BTGreaterStrategyNumber -#define BTNE 6 - -static const StrategyNumber - BT_implic_table[6][6] = { -/* - * The target operator: - * - * LT LE EQ GE GT NE - */ - {BTGE, BTGE, 0, 0, 0, BTGE}, /* LT */ - {BTGT, BTGE, 0, 0, 0, BTGT}, /* LE */ - {BTGT, BTGE, BTEQ, BTLE, BTLT, BTNE}, /* EQ */ - {0, 0, 0, BTLE, BTLT, BTLT}, /* GE */ - {0, 0, 0, BTLE, BTLE, BTLE}, /* GT */ - {0, 0, 0, 0, 0, BTEQ} /* NE */ -}; - - -/*---------- - * pred_test_simple_clause - * Does the "predicate inclusion test" for a "simple clause" predicate - * and a "simple clause" restriction. - * - * We have three strategies for determining whether one simple clause - * implies another: - * - * A simple and general way is to see if they are equal(); this works for any - * kind of expression. (Actually, there is an implied assumption that the - * functions in the expression are immutable, ie dependent only on their input - * arguments --- but this was checked for the predicate by CheckPredicate().) - * - * When the predicate is of the form "foo IS NOT NULL", we can conclude that - * the predicate is implied if the clause is a strict operator or function - * that has "foo" as an input. In this case the clause must yield NULL when - * "foo" is NULL, which we can take as equivalent to FALSE because we know - * we are within an AND/OR subtree of a WHERE clause. (Again, "foo" is - * already known immutable, so the clause will certainly always fail.) - * - * Our other way works only for binary boolean opclauses of the form - * "foo op constant", where "foo" is the same in both clauses. The operators - * and constants can be different but the operators must be in the same btree - * operator class. We use the above operator implication table to be able to - * derive implications between nonidentical clauses. (Note: "foo" is known - * immutable, and constants are surely immutable, but we have to check that - * the operators are too. As of 8.0 it's possible for opclasses to contain - * operators that are merely stable, and we dare not make deductions with - * these.) - * - * Eventually, rtree operators could also be handled by defining an - * appropriate "RT_implic_table" array. - *---------- - */ -static bool -pred_test_simple_clause(Expr *predicate, Node *clause) -{ - Node *leftop, - *rightop; - Node *pred_var, - *clause_var; - Const *pred_const, - *clause_const; - bool pred_var_on_left, - clause_var_on_left, - pred_op_negated; - Oid pred_op, - clause_op, - pred_op_negator, - clause_op_negator, - test_op = InvalidOid; - Oid opclass_id; - bool found = false; - StrategyNumber pred_strategy, - clause_strategy, - test_strategy; - Oid clause_subtype; - Expr *test_expr; - ExprState *test_exprstate; - Datum test_result; - bool isNull; - CatCList *catlist; - int i; - EState *estate; - MemoryContext oldcontext; - - /* First try the equal() test */ - if (equal((Node *) predicate, clause)) - return true; - - /* Next try the IS NOT NULL case */ - if (predicate && IsA(predicate, NullTest) && - ((NullTest *) predicate)->nulltesttype == IS_NOT_NULL) - { - Expr *nonnullarg = ((NullTest *) predicate)->arg; - - if (is_opclause(clause) && - list_member(((OpExpr *) clause)->args, nonnullarg) && - op_strict(((OpExpr *) clause)->opno)) - return true; - if (is_funcclause(clause) && - list_member(((FuncExpr *) clause)->args, nonnullarg) && - func_strict(((FuncExpr *) clause)->funcid)) - return true; - return false; /* we can't succeed below... */ - } - - /* - * Can't do anything more unless they are both binary opclauses with a - * Const on one side, and identical subexpressions on the other sides. - * Note we don't have to think about binary relabeling of the Const - * node, since that would have been folded right into the Const. - * - * If either Const is null, we also fail right away; this assumes that - * the test operator will always be strict. - */ - if (!is_opclause(predicate)) - return false; - leftop = get_leftop(predicate); - rightop = get_rightop(predicate); - if (rightop == NULL) - return false; /* not a binary opclause */ - if (IsA(rightop, Const)) - { - pred_var = leftop; - pred_const = (Const *) rightop; - pred_var_on_left = true; - } - else if (IsA(leftop, Const)) - { - pred_var = rightop; - pred_const = (Const *) leftop; - pred_var_on_left = false; - } - else - return false; /* no Const to be found */ - if (pred_const->constisnull) - return false; - - if (!is_opclause(clause)) - return false; - leftop = get_leftop((Expr *) clause); - rightop = get_rightop((Expr *) clause); - if (rightop == NULL) - return false; /* not a binary opclause */ - if (IsA(rightop, Const)) - { - clause_var = leftop; - clause_const = (Const *) rightop; - clause_var_on_left = true; - } - else if (IsA(leftop, Const)) - { - clause_var = rightop; - clause_const = (Const *) leftop; - clause_var_on_left = false; - } - else - return false; /* no Const to be found */ - if (clause_const->constisnull) - return false; - - /* - * Check for matching subexpressions on the non-Const sides. We used - * to only allow a simple Var, but it's about as easy to allow any - * expression. Remember we already know that the pred expression does - * not contain any non-immutable functions, so identical expressions - * should yield identical results. - */ - if (!equal(pred_var, clause_var)) - return false; - - /* - * Okay, get the operators in the two clauses we're comparing. Commute - * them if needed so that we can assume the variables are on the left. - */ - pred_op = ((OpExpr *) predicate)->opno; - if (!pred_var_on_left) - { - pred_op = get_commutator(pred_op); - if (!OidIsValid(pred_op)) - return false; - } - - clause_op = ((OpExpr *) clause)->opno; - if (!clause_var_on_left) - { - clause_op = get_commutator(clause_op); - if (!OidIsValid(clause_op)) - return false; - } - - /* - * Try to find a btree opclass containing the needed operators. - * - * We must find a btree opclass that contains both operators, else the - * implication can't be determined. Also, the pred_op has to be of - * default subtype (implying left and right input datatypes are the - * same); otherwise it's unsafe to put the pred_const on the left side - * of the test. Also, the opclass must contain a suitable test - * operator matching the clause_const's type (which we take to mean - * that it has the same subtype as the original clause_operator). - * - * If there are multiple matching opclasses, assume we can use any one to - * determine the logical relationship of the two operators and the - * correct corresponding test operator. This should work for any - * logically consistent opclasses. - */ - catlist = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(pred_op), - 0, 0, 0); - - /* - * If we couldn't find any opclass containing the pred_op, perhaps it - * is a <> operator. See if it has a negator that is in an opclass. - */ - pred_op_negated = false; - if (catlist->n_members == 0) - { - pred_op_negator = get_negator(pred_op); - if (OidIsValid(pred_op_negator)) - { - pred_op_negated = true; - ReleaseSysCacheList(catlist); - catlist = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(pred_op_negator), - 0, 0, 0); - } - } - - /* Also may need the clause_op's negator */ - clause_op_negator = get_negator(clause_op); - - /* Now search the opclasses */ - for (i = 0; i < catlist->n_members; i++) - { - HeapTuple pred_tuple = &catlist->members[i]->tuple; - Form_pg_amop pred_form = (Form_pg_amop) GETSTRUCT(pred_tuple); - HeapTuple clause_tuple; - - opclass_id = pred_form->amopclaid; - - /* must be btree */ - if (!opclass_is_btree(opclass_id)) - continue; - /* predicate operator must be default within this opclass */ - if (pred_form->amopsubtype != InvalidOid) - continue; - - /* Get the predicate operator's btree strategy number */ - pred_strategy = (StrategyNumber) pred_form->amopstrategy; - Assert(pred_strategy >= 1 && pred_strategy <= 5); - - if (pred_op_negated) - { - /* Only consider negators that are = */ - if (pred_strategy != BTEqualStrategyNumber) - continue; - pred_strategy = BTNE; - } - - /* - * From the same opclass, find a strategy number for the - * clause_op, if possible - */ - clause_tuple = SearchSysCache(AMOPOPID, - ObjectIdGetDatum(clause_op), - ObjectIdGetDatum(opclass_id), - 0, 0); - if (HeapTupleIsValid(clause_tuple)) - { - Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple); - - /* Get the restriction clause operator's strategy/subtype */ - clause_strategy = (StrategyNumber) clause_form->amopstrategy; - Assert(clause_strategy >= 1 && clause_strategy <= 5); - clause_subtype = clause_form->amopsubtype; - ReleaseSysCache(clause_tuple); - } - else if (OidIsValid(clause_op_negator)) - { - clause_tuple = SearchSysCache(AMOPOPID, - ObjectIdGetDatum(clause_op_negator), - ObjectIdGetDatum(opclass_id), - 0, 0); - if (HeapTupleIsValid(clause_tuple)) - { - Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple); - - /* Get the restriction clause operator's strategy/subtype */ - clause_strategy = (StrategyNumber) clause_form->amopstrategy; - Assert(clause_strategy >= 1 && clause_strategy <= 5); - clause_subtype = clause_form->amopsubtype; - ReleaseSysCache(clause_tuple); - - /* Only consider negators that are = */ - if (clause_strategy != BTEqualStrategyNumber) - continue; - clause_strategy = BTNE; - } - else - continue; - } - else - continue; - - /* - * Look up the "test" strategy number in the implication table - */ - test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1]; - if (test_strategy == 0) - { - /* Can't determine implication using this interpretation */ - continue; - } - - /* - * See if opclass has an operator for the test strategy and the - * clause datatype. - */ - if (test_strategy == BTNE) - { - test_op = get_opclass_member(opclass_id, clause_subtype, - BTEqualStrategyNumber); - if (OidIsValid(test_op)) - test_op = get_negator(test_op); - } - else - { - test_op = get_opclass_member(opclass_id, clause_subtype, - test_strategy); - } - if (OidIsValid(test_op)) - { - /* - * Last check: test_op must be immutable. - * - * Note that we require only the test_op to be immutable, not the - * original clause_op. (pred_op must be immutable, else it - * would not be allowed in an index predicate.) Essentially - * we are assuming that the opclass is consistent even if it - * contains operators that are merely stable. - */ - if (op_volatile(test_op) == PROVOLATILE_IMMUTABLE) - { - found = true; - break; - } - } - } - - ReleaseSysCacheList(catlist); - - if (!found) + foreach(ilist, rel->indexlist) { - /* couldn't find a btree opclass to interpret the operators */ - return false; - } - - /* - * Evaluate the test. For this we need an EState. - */ - estate = CreateExecutorState(); - - /* We can use the estate's working context to avoid memory leaks. */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* Build expression tree */ - test_expr = make_opclause(test_op, - BOOLOID, - false, - (Expr *) pred_const, - (Expr *) clause_const); - - /* Prepare it for execution */ - test_exprstate = ExecPrepareExpr(test_expr, estate); - - /* And execute it. */ - test_result = ExecEvalExprSwitchContext(test_exprstate, - GetPerTupleExprContext(estate), - &isNull, NULL); - - /* Get back to outer memory context */ - MemoryContextSwitchTo(oldcontext); + IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); - /* Release all the junk we just created */ - FreeExecutorState(estate); + if (index->indpred == NIL) + continue; /* ignore non-partial indexes */ - if (isNull) - { - /* Treat a null result as false ... but it's a tad fishy ... */ - elog(DEBUG2, "null predicate test result"); - return false; + index->predOK = predicate_implied_by(index->indpred, + restrictinfo_list); } - return DatumGetBool(test_result); } - /**************************************************************************** * ---- ROUTINES TO CHECK JOIN CLAUSES ---- ****************************************************************************/ |