Skip to content

Commit febc9a6

Browse files
committed
Expand the 'special index operator' machinery to handle special cases
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.
1 parent 9d388e1 commit febc9a6

File tree

5 files changed

+192
-26
lines changed

5 files changed

+192
-26
lines changed

src/backend/optimizer/path/indxpath.c

Lines changed: 181 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.169 2005/03/02 04:10:53 tgl Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.170 2005/03/26 23:29:17 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -50,6 +50,9 @@
5050
#define is_indexable_operator(clause,opclass,indexkey_on_left) \
5151
(indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)
5252

53+
#define IsBooleanOpclass(opclass) \
54+
((opclass) == BOOL_BTREE_OPS_OID || (opclass) == BOOL_HASH_OPS_OID)
55+
5356

5457
static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index);
5558
static List *group_clauses_by_indexkey_for_join(Query *root,
@@ -72,8 +75,16 @@ static Path *make_innerjoin_index_path(Query *root,
7275
List *clausegroups);
7376
static bool match_index_to_operand(Node *operand, int indexcol,
7477
RelOptInfo *rel, IndexOptInfo *index);
78+
static bool match_boolean_index_clause(Node *clause,
79+
int indexcol,
80+
RelOptInfo *rel,
81+
IndexOptInfo *index);
7582
static bool match_special_index_operator(Expr *clause, Oid opclass,
7683
bool indexkey_on_left);
84+
static Expr *expand_boolean_index_clause(Node *clause,
85+
int indexcol,
86+
RelOptInfo *rel,
87+
IndexOptInfo *index);
7788
static List *expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass);
7889
static List *prefix_quals(Node *leftop, Oid opclass,
7990
Const *prefix, Pattern_Prefix_Status pstatus);
@@ -511,7 +522,7 @@ group_clauses_by_indexkey_for_or(RelOptInfo *rel,
511522
* match_clause_to_indexcol()
512523
* Determines whether a restriction clause matches a column of an index.
513524
*
514-
* To match, the clause:
525+
* To match a normal index, the clause:
515526
*
516527
* (1) must be in the form (indexkey op const) or (const op indexkey);
517528
* and
@@ -525,6 +536,9 @@ group_clauses_by_indexkey_for_or(RelOptInfo *rel,
525536
* We do not actually do the commuting here, but we check whether a
526537
* suitable commutator operator is available.
527538
*
539+
* For boolean indexes, it is also possible to match the clause directly
540+
* to the indexkey; or perhaps the clause is (NOT indexkey).
541+
*
528542
* 'rel' is the relation of interest.
529543
* 'index' is an index on 'rel'.
530544
* 'indexcol' is a column number of 'index' (counting from 0).
@@ -547,7 +561,15 @@ match_clause_to_indexcol(RelOptInfo *rel,
547561
Node *leftop,
548562
*rightop;
549563

550-
/* Clause must be a binary opclause. */
564+
/* First check for boolean-index cases. */
565+
if (IsBooleanOpclass(opclass))
566+
{
567+
if (match_boolean_index_clause((Node *) clause,
568+
indexcol, rel, index))
569+
return true;
570+
}
571+
572+
/* Else clause must be a binary opclause. */
551573
if (!is_opclause(clause))
552574
return false;
553575
leftop = get_leftop(clause);
@@ -606,6 +628,8 @@ match_clause_to_indexcol(RelOptInfo *rel,
606628
* operator for this column, or is a "special" operator as recognized
607629
* by match_special_index_operator().
608630
*
631+
* The boolean-index cases don't apply.
632+
*
609633
* As above, we must be able to commute the clause to put the indexkey
610634
* on the left.
611635
*
@@ -1662,7 +1686,7 @@ make_innerjoin_index_path(Query *root,
16621686
pathnode->path.pathkeys = NIL;
16631687

16641688
/* Convert clauses to indexquals the executor can handle */
1665-
indexquals = expand_indexqual_conditions(index, clausegroups);
1689+
indexquals = expand_indexqual_conditions(rel, index, clausegroups);
16661690

16671691
/* Flatten the clausegroups list to produce indexclauses list */
16681692
allclauses = flatten_clausegroups_list(clausegroups);
@@ -1868,21 +1892,78 @@ match_index_to_operand(Node *operand,
18681892
* from LIKE to indexscan limits rather harder than one might think ...
18691893
* but that's the basic idea.)
18701894
*
1871-
* Two routines are provided here, match_special_index_operator() and
1872-
* expand_indexqual_conditions(). match_special_index_operator() is
1873-
* just an auxiliary function for match_clause_to_indexcol(); after
1874-
* the latter fails to recognize a restriction opclause's operator
1875-
* as a member of an index's opclass, it asks match_special_index_operator()
1876-
* whether the clause should be considered an indexqual anyway.
1895+
* Another thing that we do with this machinery is to provide special
1896+
* smarts for "boolean" indexes (that is, indexes on boolean columns
1897+
* that support boolean equality). We can transform a plain reference
1898+
* to the indexkey into "indexkey = true", or "NOT indexkey" into
1899+
* "indexkey = false", so as to make the expression indexable using the
1900+
* regular index operators. (As of Postgres 8.1, we must do this here
1901+
* because constant simplification does the reverse transformation;
1902+
* without this code there'd be no way to use such an index at all.)
1903+
*
1904+
* Three routines are provided here:
1905+
*
1906+
* match_special_index_operator() is just an auxiliary function for
1907+
* match_clause_to_indexcol(); after the latter fails to recognize a
1908+
* restriction opclause's operator as a member of an index's opclass,
1909+
* it asks match_special_index_operator() whether the clause should be
1910+
* considered an indexqual anyway.
1911+
*
1912+
* match_boolean_index_clause() similarly detects clauses that can be
1913+
* converted into boolean equality operators.
1914+
*
18771915
* expand_indexqual_conditions() converts a list of lists of RestrictInfo
18781916
* nodes (with implicit AND semantics across list elements) into
18791917
* a list of clauses that the executor can actually handle. For operators
18801918
* that are members of the index's opclass this transformation is a no-op,
1881-
* but operators recognized by match_special_index_operator() must be
1882-
* converted into one or more "regular" indexqual conditions.
1919+
* but clauses recognized by match_special_index_operator() or
1920+
* match_boolean_index_clause() must be converted into one or more "regular"
1921+
* indexqual conditions.
18831922
*----------
18841923
*/
18851924

1925+
/*
1926+
* match_boolean_index_clause
1927+
* Recognize restriction clauses that can be matched to a boolean index.
1928+
*
1929+
* This should be called only when IsBooleanOpclass() recognizes the
1930+
* index's operator class. We check to see if the clause matches the
1931+
* index's key.
1932+
*/
1933+
static bool
1934+
match_boolean_index_clause(Node *clause,
1935+
int indexcol,
1936+
RelOptInfo *rel,
1937+
IndexOptInfo *index)
1938+
{
1939+
/* Direct match? */
1940+
if (match_index_to_operand(clause, indexcol, rel, index))
1941+
return true;
1942+
/* NOT clause? */
1943+
if (not_clause(clause))
1944+
{
1945+
if (match_index_to_operand((Node *) get_notclausearg((Expr *) clause),
1946+
indexcol, rel, index))
1947+
return true;
1948+
}
1949+
/*
1950+
* Since we only consider clauses at top level of WHERE, we can convert
1951+
* indexkey IS TRUE and indexkey IS FALSE to index searches as well.
1952+
* The different meaning for NULL isn't important.
1953+
*/
1954+
else if (clause && IsA(clause, BooleanTest))
1955+
{
1956+
BooleanTest *btest = (BooleanTest *) clause;
1957+
1958+
if (btest->booltesttype == IS_TRUE ||
1959+
btest->booltesttype == IS_FALSE)
1960+
if (match_index_to_operand((Node *) btest->arg,
1961+
indexcol, rel, index))
1962+
return true;
1963+
}
1964+
return false;
1965+
}
1966+
18861967
/*
18871968
* match_special_index_operator
18881969
* Recognize restriction clauses that can be used to generate
@@ -2042,9 +2123,9 @@ match_special_index_operator(Expr *clause, Oid opclass,
20422123
* expand_indexqual_conditions
20432124
* Given a list of sublists of RestrictInfo nodes, produce a flat list
20442125
* of index qual clauses. Standard qual clauses (those in the index's
2045-
* opclass) are passed through unchanged. "Special" index operators
2046-
* are expanded into clauses that the indexscan machinery will know
2047-
* what to do with.
2126+
* opclass) are passed through unchanged. Boolean clauses and "special"
2127+
* index operators are expanded into clauses that the indexscan machinery
2128+
* will know what to do with.
20482129
*
20492130
* The input list is ordered by index key, and so the output list is too.
20502131
* (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,
20542135
* someday --- tgl 7/00)
20552136
*/
20562137
List *
2057-
expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
2138+
expand_indexqual_conditions(RelOptInfo *rel, IndexOptInfo *index, List *clausegroups)
20582139
{
20592140
List *resultquals = NIL;
20602141
ListCell *clausegroup_item;
2142+
int indexcol = 0;
20612143
Oid *classes = index->classlist;
20622144

20632145
if (clausegroups == NIL)
@@ -2073,12 +2155,32 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
20732155
{
20742156
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
20752157

2158+
/* First check for boolean cases */
2159+
if (IsBooleanOpclass(curClass))
2160+
{
2161+
Expr *boolqual;
2162+
2163+
boolqual = expand_boolean_index_clause((Node *) rinfo->clause,
2164+
indexcol,
2165+
rel,
2166+
index);
2167+
if (boolqual)
2168+
{
2169+
resultquals = lappend(resultquals,
2170+
make_restrictinfo(boolqual,
2171+
true, true));
2172+
continue;
2173+
}
2174+
}
2175+
20762176
resultquals = list_concat(resultquals,
20772177
expand_indexqual_condition(rinfo,
2078-
curClass));
2178+
curClass));
20792179
}
20802180

20812181
clausegroup_item = lnext(clausegroup_item);
2182+
2183+
indexcol++;
20822184
classes++;
20832185
} while (clausegroup_item != NULL && !DoneMatchingIndexKeys(classes));
20842186

@@ -2087,16 +2189,77 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
20872189
return resultquals;
20882190
}
20892191

2192+
/*
2193+
* expand_boolean_index_clause
2194+
* Convert a clause recognized by match_boolean_index_clause into
2195+
* a boolean equality operator clause.
2196+
*
2197+
* Returns NULL if the clause isn't a boolean index qual.
2198+
*/
2199+
static Expr *
2200+
expand_boolean_index_clause(Node *clause,
2201+
int indexcol,
2202+
RelOptInfo *rel,
2203+
IndexOptInfo *index)
2204+
{
2205+
/* Direct match? */
2206+
if (match_index_to_operand(clause, indexcol, rel, index))
2207+
{
2208+
/* convert to indexkey = TRUE */
2209+
return make_opclause(BooleanEqualOperator, BOOLOID, false,
2210+
(Expr *) clause,
2211+
(Expr *) makeBoolConst(true, false));
2212+
}
2213+
/* NOT clause? */
2214+
if (not_clause(clause))
2215+
{
2216+
Node *arg = (Node *) get_notclausearg((Expr *) clause);
2217+
2218+
/* It must have matched the indexkey */
2219+
Assert(match_index_to_operand(arg, indexcol, rel, index));
2220+
/* convert to indexkey = FALSE */
2221+
return make_opclause(BooleanEqualOperator, BOOLOID, false,
2222+
(Expr *) arg,
2223+
(Expr *) makeBoolConst(false, false));
2224+
}
2225+
if (clause && IsA(clause, BooleanTest))
2226+
{
2227+
BooleanTest *btest = (BooleanTest *) clause;
2228+
Node *arg = (Node *) btest->arg;
2229+
2230+
/* It must have matched the indexkey */
2231+
Assert(match_index_to_operand(arg, indexcol, rel, index));
2232+
if (btest->booltesttype == IS_TRUE)
2233+
{
2234+
/* convert to indexkey = TRUE */
2235+
return make_opclause(BooleanEqualOperator, BOOLOID, false,
2236+
(Expr *) arg,
2237+
(Expr *) makeBoolConst(true, false));
2238+
}
2239+
if (btest->booltesttype == IS_FALSE)
2240+
{
2241+
/* convert to indexkey = FALSE */
2242+
return make_opclause(BooleanEqualOperator, BOOLOID, false,
2243+
(Expr *) arg,
2244+
(Expr *) makeBoolConst(false, false));
2245+
}
2246+
/* Oops */
2247+
Assert(false);
2248+
}
2249+
2250+
return NULL;
2251+
}
2252+
20902253
/*
20912254
* expand_indexqual_condition --- expand a single indexqual condition
2255+
* (other than a boolean-qual case)
20922256
*
20932257
* The input is a single RestrictInfo, the output a list of RestrictInfos
20942258
*/
20952259
static List *
20962260
expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass)
20972261
{
20982262
Expr *clause = rinfo->clause;
2099-
21002263
/* we know these will succeed */
21012264
Node *leftop = get_leftop(clause);
21022265
Node *rightop = get_rightop(clause);

src/backend/optimizer/path/orindxpath.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.65 2005/03/01 01:40:05 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.66 2005/03/26 23:29:17 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -398,7 +398,7 @@ best_or_subclause_index(Query *root,
398398
continue;
399399

400400
/* Convert clauses to indexquals the executor can handle */
401-
indexquals = expand_indexqual_conditions(index, indexclauses);
401+
indexquals = expand_indexqual_conditions(rel, index, indexclauses);
402402

403403
cost_index(&subclause_path, root, rel, index, indexquals, false);
404404

src/backend/optimizer/util/pathnode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.112 2005/03/10 23:21:22 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.113 2005/03/26 23:29:18 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -444,7 +444,7 @@ create_index_path(Query *root,
444444
pathnode->path.pathkeys = pathkeys;
445445

446446
/* Convert clauses to indexquals the executor can handle */
447-
indexquals = expand_indexqual_conditions(index, restriction_clauses);
447+
indexquals = expand_indexqual_conditions(rel, index, restriction_clauses);
448448

449449
/* Flatten the clause-groups list to produce indexclauses list */
450450
restriction_clauses = flatten_clausegroups_list(restriction_clauses);

src/include/catalog/pg_opclass.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
2828
* Portions Copyright (c) 1994, Regents of the University of California
2929
*
30-
* $PostgreSQL: pgsql/src/include/catalog/pg_opclass.h,v 1.62 2004/12/31 22:03:24 pgsql Exp $
30+
* $PostgreSQL: pgsql/src/include/catalog/pg_opclass.h,v 1.63 2005/03/26 23:29:19 tgl Exp $
3131
*
3232
* NOTES
3333
* 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 ));
9292
#define ARRAY_BTREE_OPS_OID 397
9393
DATA(insert OID = 423 ( 403 bit_ops PGNSP PGUID 1560 t 0 ));
9494
DATA(insert OID = 424 ( 403 bool_ops PGNSP PGUID 16 t 0 ));
95+
#define BOOL_BTREE_OPS_OID 424
9596
DATA(insert OID = 425 ( 402 box_ops PGNSP PGUID 603 t 0 ));
9697
DATA(insert OID = 426 ( 403 bpchar_ops PGNSP PGUID 1042 t 0 ));
9798
#define BPCHAR_BTREE_OPS_OID 426
@@ -159,6 +160,7 @@ DATA(insert OID = 2098 ( 403 name_pattern_ops PGNSP PGUID 19 f 0 ));
159160
#define NAME_PATTERN_BTREE_OPS_OID 2098
160161
DATA(insert OID = 2099 ( 403 money_ops PGNSP PGUID 790 t 0 ));
161162
DATA(insert OID = 2222 ( 405 bool_ops PGNSP PGUID 16 t 0 ));
163+
#define BOOL_HASH_OPS_OID 2222
162164
DATA(insert OID = 2223 ( 405 bytea_ops PGNSP PGUID 17 t 0 ));
163165
DATA(insert OID = 2224 ( 405 int2vector_ops PGNSP PGUID 22 t 0 ));
164166
DATA(insert OID = 2225 ( 405 xid_ops PGNSP PGUID 28 t 0 ));

src/include/optimizer/paths.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.78 2005/01/23 02:21:36 tgl Exp $
11+
* $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.79 2005/03/26 23:29:20 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -41,8 +41,9 @@ extern Path *best_inner_indexscan(Query *root, RelOptInfo *rel,
4141
extern List *group_clauses_by_indexkey_for_or(RelOptInfo *rel,
4242
IndexOptInfo *index,
4343
Expr *orsubclause);
44-
extern List *expand_indexqual_conditions(IndexOptInfo *index,
45-
List *clausegroups);
44+
extern List *expand_indexqual_conditions(RelOptInfo *rel,
45+
IndexOptInfo *index,
46+
List *clausegroups);
4647
extern void check_partial_indexes(Query *root, RelOptInfo *rel);
4748
extern bool pred_test(List *predicate_list, List *restrictinfo_list);
4849
extern List *flatten_clausegroups_list(List *clausegroups);

0 commit comments

Comments
 (0)