diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index f86206304c..9f5ab60337 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.169 2005/03/02 04:10:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.170 2005/03/26 23:29:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -50,6 +50,9 @@ #define is_indexable_operator(clause,opclass,indexkey_on_left) \ (indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid) +#define IsBooleanOpclass(opclass) \ + ((opclass) == BOOL_BTREE_OPS_OID || (opclass) == BOOL_HASH_OPS_OID) + static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index); static List *group_clauses_by_indexkey_for_join(Query *root, @@ -72,8 +75,16 @@ static Path *make_innerjoin_index_path(Query *root, List *clausegroups); static bool match_index_to_operand(Node *operand, int indexcol, RelOptInfo *rel, IndexOptInfo *index); +static bool match_boolean_index_clause(Node *clause, + int indexcol, + RelOptInfo *rel, + IndexOptInfo *index); static bool match_special_index_operator(Expr *clause, Oid opclass, bool indexkey_on_left); +static Expr *expand_boolean_index_clause(Node *clause, + int indexcol, + RelOptInfo *rel, + IndexOptInfo *index); static List *expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass); static List *prefix_quals(Node *leftop, Oid opclass, Const *prefix, Pattern_Prefix_Status pstatus); @@ -511,7 +522,7 @@ group_clauses_by_indexkey_for_or(RelOptInfo *rel, * match_clause_to_indexcol() * Determines whether a restriction clause matches a column of an index. * - * To match, the clause: + * To match a normal index, the clause: * * (1) must be in the form (indexkey op const) or (const op indexkey); * and @@ -525,6 +536,9 @@ group_clauses_by_indexkey_for_or(RelOptInfo *rel, * We do not actually do the commuting here, but we check whether a * suitable commutator operator is available. * + * For boolean indexes, it is also possible to match the clause directly + * to the indexkey; or perhaps the clause is (NOT indexkey). + * * 'rel' is the relation of interest. * 'index' is an index on 'rel'. * 'indexcol' is a column number of 'index' (counting from 0). @@ -547,7 +561,15 @@ match_clause_to_indexcol(RelOptInfo *rel, Node *leftop, *rightop; - /* Clause must be a binary opclause. */ + /* First check for boolean-index cases. */ + if (IsBooleanOpclass(opclass)) + { + if (match_boolean_index_clause((Node *) clause, + indexcol, rel, index)) + return true; + } + + /* Else clause must be a binary opclause. */ if (!is_opclause(clause)) return false; leftop = get_leftop(clause); @@ -606,6 +628,8 @@ match_clause_to_indexcol(RelOptInfo *rel, * operator for this column, or is a "special" operator as recognized * by match_special_index_operator(). * + * The boolean-index cases don't apply. + * * As above, we must be able to commute the clause to put the indexkey * on the left. * @@ -1662,7 +1686,7 @@ make_innerjoin_index_path(Query *root, pathnode->path.pathkeys = NIL; /* Convert clauses to indexquals the executor can handle */ - indexquals = expand_indexqual_conditions(index, clausegroups); + indexquals = expand_indexqual_conditions(rel, index, clausegroups); /* Flatten the clausegroups list to produce indexclauses list */ allclauses = flatten_clausegroups_list(clausegroups); @@ -1868,21 +1892,78 @@ match_index_to_operand(Node *operand, * from LIKE to indexscan limits rather harder than one might think ... * but that's the basic idea.) * - * Two routines are provided here, match_special_index_operator() and - * expand_indexqual_conditions(). match_special_index_operator() is - * just an auxiliary function for match_clause_to_indexcol(); after - * the latter fails to recognize a restriction opclause's operator - * as a member of an index's opclass, it asks match_special_index_operator() - * whether the clause should be considered an indexqual anyway. + * Another thing that we do with this machinery is to provide special + * smarts for "boolean" indexes (that is, indexes on boolean columns + * that support boolean equality). We can transform a plain reference + * to the indexkey into "indexkey = true", or "NOT indexkey" into + * "indexkey = false", so as to make the expression indexable using the + * regular index operators. (As of Postgres 8.1, we must do this here + * because constant simplification does the reverse transformation; + * without this code there'd be no way to use such an index at all.) + * + * Three routines are provided here: + * + * match_special_index_operator() is just an auxiliary function for + * match_clause_to_indexcol(); after the latter fails to recognize a + * restriction opclause's operator as a member of an index's opclass, + * it asks match_special_index_operator() whether the clause should be + * considered an indexqual anyway. + * + * match_boolean_index_clause() similarly detects clauses that can be + * converted into boolean equality operators. + * * expand_indexqual_conditions() converts a list of lists of RestrictInfo * nodes (with implicit AND semantics across list elements) into * a list of clauses that the executor can actually handle. For operators * that are members of the index's opclass this transformation is a no-op, - * but operators recognized by match_special_index_operator() must be - * converted into one or more "regular" indexqual conditions. + * but clauses recognized by match_special_index_operator() or + * match_boolean_index_clause() must be converted into one or more "regular" + * indexqual conditions. *---------- */ +/* + * match_boolean_index_clause + * Recognize restriction clauses that can be matched to a boolean index. + * + * This should be called only when IsBooleanOpclass() recognizes the + * index's operator class. We check to see if the clause matches the + * index's key. + */ +static bool +match_boolean_index_clause(Node *clause, + int indexcol, + RelOptInfo *rel, + IndexOptInfo *index) +{ + /* Direct match? */ + if (match_index_to_operand(clause, indexcol, rel, index)) + return true; + /* NOT clause? */ + if (not_clause(clause)) + { + if (match_index_to_operand((Node *) get_notclausearg((Expr *) clause), + indexcol, rel, index)) + return true; + } + /* + * Since we only consider clauses at top level of WHERE, we can convert + * indexkey IS TRUE and indexkey IS FALSE to index searches as well. + * The different meaning for NULL isn't important. + */ + else if (clause && IsA(clause, BooleanTest)) + { + BooleanTest *btest = (BooleanTest *) clause; + + if (btest->booltesttype == IS_TRUE || + btest->booltesttype == IS_FALSE) + if (match_index_to_operand((Node *) btest->arg, + indexcol, rel, index)) + return true; + } + return false; +} + /* * match_special_index_operator * Recognize restriction clauses that can be used to generate @@ -2042,9 +2123,9 @@ match_special_index_operator(Expr *clause, Oid opclass, * expand_indexqual_conditions * Given a list of sublists of RestrictInfo nodes, produce a flat list * of index qual clauses. Standard qual clauses (those in the index's - * opclass) are passed through unchanged. "Special" index operators - * are expanded into clauses that the indexscan machinery will know - * what to do with. + * opclass) are passed through unchanged. Boolean clauses and "special" + * index operators are expanded into clauses that the indexscan machinery + * will know what to do with. * * The input list is ordered by index key, and so the output list is too. * (The latter is not depended on by any part of the planner, so far as I can @@ -2054,10 +2135,11 @@ match_special_index_operator(Expr *clause, Oid opclass, * someday --- tgl 7/00) */ List * -expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) +expand_indexqual_conditions(RelOptInfo *rel, IndexOptInfo *index, List *clausegroups) { List *resultquals = NIL; ListCell *clausegroup_item; + int indexcol = 0; Oid *classes = index->classlist; if (clausegroups == NIL) @@ -2073,12 +2155,32 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); + /* First check for boolean cases */ + if (IsBooleanOpclass(curClass)) + { + Expr *boolqual; + + boolqual = expand_boolean_index_clause((Node *) rinfo->clause, + indexcol, + rel, + index); + if (boolqual) + { + resultquals = lappend(resultquals, + make_restrictinfo(boolqual, + true, true)); + continue; + } + } + resultquals = list_concat(resultquals, expand_indexqual_condition(rinfo, - curClass)); + curClass)); } clausegroup_item = lnext(clausegroup_item); + + indexcol++; classes++; } while (clausegroup_item != NULL && !DoneMatchingIndexKeys(classes)); @@ -2087,8 +2189,70 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) return resultquals; } +/* + * expand_boolean_index_clause + * Convert a clause recognized by match_boolean_index_clause into + * a boolean equality operator clause. + * + * Returns NULL if the clause isn't a boolean index qual. + */ +static Expr * +expand_boolean_index_clause(Node *clause, + int indexcol, + RelOptInfo *rel, + IndexOptInfo *index) +{ + /* Direct match? */ + if (match_index_to_operand(clause, indexcol, rel, index)) + { + /* convert to indexkey = TRUE */ + return make_opclause(BooleanEqualOperator, BOOLOID, false, + (Expr *) clause, + (Expr *) makeBoolConst(true, false)); + } + /* NOT clause? */ + if (not_clause(clause)) + { + Node *arg = (Node *) get_notclausearg((Expr *) clause); + + /* It must have matched the indexkey */ + Assert(match_index_to_operand(arg, indexcol, rel, index)); + /* convert to indexkey = FALSE */ + return make_opclause(BooleanEqualOperator, BOOLOID, false, + (Expr *) arg, + (Expr *) makeBoolConst(false, false)); + } + if (clause && IsA(clause, BooleanTest)) + { + BooleanTest *btest = (BooleanTest *) clause; + Node *arg = (Node *) btest->arg; + + /* It must have matched the indexkey */ + Assert(match_index_to_operand(arg, indexcol, rel, index)); + if (btest->booltesttype == IS_TRUE) + { + /* convert to indexkey = TRUE */ + return make_opclause(BooleanEqualOperator, BOOLOID, false, + (Expr *) arg, + (Expr *) makeBoolConst(true, false)); + } + if (btest->booltesttype == IS_FALSE) + { + /* convert to indexkey = FALSE */ + return make_opclause(BooleanEqualOperator, BOOLOID, false, + (Expr *) arg, + (Expr *) makeBoolConst(false, false)); + } + /* Oops */ + Assert(false); + } + + return NULL; +} + /* * expand_indexqual_condition --- expand a single indexqual condition + * (other than a boolean-qual case) * * The input is a single RestrictInfo, the output a list of RestrictInfos */ @@ -2096,7 +2260,6 @@ static List * expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass) { Expr *clause = rinfo->clause; - /* we know these will succeed */ Node *leftop = get_leftop(clause); Node *rightop = get_rightop(clause); diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c index ffd08c738c..1cda19c8fa 100644 --- a/src/backend/optimizer/path/orindxpath.c +++ b/src/backend/optimizer/path/orindxpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.65 2005/03/01 01:40:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.66 2005/03/26 23:29:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -398,7 +398,7 @@ best_or_subclause_index(Query *root, continue; /* Convert clauses to indexquals the executor can handle */ - indexquals = expand_indexqual_conditions(index, indexclauses); + indexquals = expand_indexqual_conditions(rel, index, indexclauses); cost_index(&subclause_path, root, rel, index, indexquals, false); diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index f20c95299f..53b2197edd 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.112 2005/03/10 23:21:22 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.113 2005/03/26 23:29:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -444,7 +444,7 @@ create_index_path(Query *root, pathnode->path.pathkeys = pathkeys; /* Convert clauses to indexquals the executor can handle */ - indexquals = expand_indexqual_conditions(index, restriction_clauses); + indexquals = expand_indexqual_conditions(rel, index, restriction_clauses); /* Flatten the clause-groups list to produce indexclauses list */ restriction_clauses = flatten_clausegroups_list(restriction_clauses); diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index b6213f2f3a..3e4c175916 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -27,7 +27,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_opclass.h,v 1.62 2004/12/31 22:03:24 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_opclass.h,v 1.63 2005/03/26 23:29:19 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -92,6 +92,7 @@ DATA(insert OID = 397 ( 403 array_ops PGNSP PGUID 2277 t 0 )); #define ARRAY_BTREE_OPS_OID 397 DATA(insert OID = 423 ( 403 bit_ops PGNSP PGUID 1560 t 0 )); DATA(insert OID = 424 ( 403 bool_ops PGNSP PGUID 16 t 0 )); +#define BOOL_BTREE_OPS_OID 424 DATA(insert OID = 425 ( 402 box_ops PGNSP PGUID 603 t 0 )); DATA(insert OID = 426 ( 403 bpchar_ops PGNSP PGUID 1042 t 0 )); #define BPCHAR_BTREE_OPS_OID 426 @@ -159,6 +160,7 @@ DATA(insert OID = 2098 ( 403 name_pattern_ops PGNSP PGUID 19 f 0 )); #define NAME_PATTERN_BTREE_OPS_OID 2098 DATA(insert OID = 2099 ( 403 money_ops PGNSP PGUID 790 t 0 )); DATA(insert OID = 2222 ( 405 bool_ops PGNSP PGUID 16 t 0 )); +#define BOOL_HASH_OPS_OID 2222 DATA(insert OID = 2223 ( 405 bytea_ops PGNSP PGUID 17 t 0 )); DATA(insert OID = 2224 ( 405 int2vector_ops PGNSP PGUID 22 t 0 )); DATA(insert OID = 2225 ( 405 xid_ops PGNSP PGUID 28 t 0 )); diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index db1a9f4aff..d159e1ecf4 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.78 2005/01/23 02:21:36 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.79 2005/03/26 23:29:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,8 +41,9 @@ extern Path *best_inner_indexscan(Query *root, RelOptInfo *rel, extern List *group_clauses_by_indexkey_for_or(RelOptInfo *rel, IndexOptInfo *index, Expr *orsubclause); -extern List *expand_indexqual_conditions(IndexOptInfo *index, - List *clausegroups); +extern List *expand_indexqual_conditions(RelOptInfo *rel, + IndexOptInfo *index, + List *clausegroups); extern void check_partial_indexes(Query *root, RelOptInfo *rel); extern bool pred_test(List *predicate_list, List *restrictinfo_list); extern List *flatten_clausegroups_list(List *clausegroups);