]> granicus.if.org Git - postgresql/commitdiff
Expand the 'special index operator' machinery to handle special cases
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 26 Mar 2005 23:29:20 +0000 (23:29 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 26 Mar 2005 23:29:20 +0000 (23:29 +0000)
for boolean indexes.  Previously we would only use such an index with
WHERE clauses like 'indexkey = true' or 'indexkey = false'.  The new
code transforms the cases 'indexkey', 'NOT indexkey', 'indexkey IS TRUE',
and 'indexkey IS FALSE' into one of these.  While this is only marginally
useful in itself, I intend soon to change constant-expression simplification
so that 'foo = true' and 'foo = false' are reduced to just 'foo' and
'NOT foo' ... which would lose the ability to use boolean indexes for
such queries at all, if the indexscan machinery couldn't make the
reverse transformation.

src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/orindxpath.c
src/backend/optimizer/util/pathnode.c
src/include/catalog/pg_opclass.h
src/include/optimizer/paths.h

index f86206304c16410242e694924565ea5c4bba4568..9f5ab60337e43be5fc716edeac3b1ff5273b7190 100644 (file)
@@ -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);
index ffd08c738cb7486ace9af3cfab6875bdb8d6b041..1cda19c8fa70139eac2fe41f7f6979c0770bbfb5 100644 (file)
@@ -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);
 
index f20c95299f34d8a8f46aacb7f6e25f4ac9cd5ca1..53b2197edd40b2b22b866ddcfe888b92f2ead0d9 100644 (file)
@@ -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);
index b6213f2f3a86b9e5f0959a8e952b53eaefae1f7a..3e4c17591620504de2fda95f9efeaae19c0e4846 100644 (file)
@@ -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 ));
index db1a9f4affcf14b9c7aa523257ebbe631da24587..d159e1ecf4e86aa6f7957f932f8beeaaf689c9ca 100644 (file)
@@ -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);