*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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,
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);
* 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
* 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).
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);
* 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.
*
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);
* 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
* 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
* 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)
{
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));
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
*/
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);