Skip to content

Commit 042c909

Browse files
committedNov 8, 2022
Produce more-optimal plans for bitmap scans on boolean columns.
The planner simplifies boolean comparisons such as "x = true" and "x = false" down to "x" and "NOT x" respectively, to have a canonical form to ease comparisons. However, if we want to use an index on x, the index AM APIs require us to reconstitute the comparison-operator form of the indexqual. While that works, in bitmap indexscans the canonical form of the qual was emitted as a "filter" condition although it really only needs to be a "recheck" condition, because create_bitmap_scan_plan didn't recognize the equivalence of that form with the generated indexqual. booleq() is pretty cheap so that likely doesn't make very much difference, but it's unsightly so let's clean it up. To fix, add a case to predicate_implied_by() to recognize the equivalence of such clauses. This is a relatively low-cost place to add a check, and perhaps it will have additional use cases in future. Richard Guo and Tom Lane, per discussion of bug #17618 from Sindy Senorita. Discussion: https://postgr.es/m/[email protected]
1 parent 05a7be9 commit 042c909

File tree

3 files changed

+60
-2
lines changed

3 files changed

+60
-2
lines changed
 

‎contrib/btree_gin/expected/bool.out

+13-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i=true ORDER BY i;
9292
Sort
9393
Sort Key: i
9494
-> Bitmap Heap Scan on test_bool
95-
Filter: i
95+
Recheck Cond: i
9696
-> Bitmap Index Scan on idx_bool
9797
Index Cond: (i = true)
9898
(6 rows)
@@ -119,3 +119,15 @@ EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i>true ORDER BY i;
119119
Index Cond: (i > true)
120120
(6 rows)
121121

122+
-- probably sufficient to check just this one:
123+
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i=false ORDER BY i;
124+
QUERY PLAN
125+
-------------------------------------------
126+
Sort
127+
Sort Key: i
128+
-> Bitmap Heap Scan on test_bool
129+
Recheck Cond: (NOT i)
130+
-> Bitmap Index Scan on idx_bool
131+
Index Cond: (i = false)
132+
(6 rows)
133+

‎contrib/btree_gin/sql/bool.sql

+2
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i<=true ORDER BY i;
2525
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i=true ORDER BY i;
2626
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i>=true ORDER BY i;
2727
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i>true ORDER BY i;
28+
-- probably sufficient to check just this one:
29+
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i=false ORDER BY i;

‎src/backend/optimizer/util/predtest.c

+45-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
#include "postgres.h"
1717

18+
#include "catalog/pg_operator.h"
1819
#include "catalog/pg_proc.h"
1920
#include "catalog/pg_type.h"
2021
#include "executor/executor.h"
@@ -1093,14 +1094,20 @@ arrayexpr_cleanup_fn(PredIterInfo info)
10931094
*
10941095
* We return true if able to prove the implication, false if not.
10951096
*
1096-
* We have three strategies for determining whether one simple clause
1097+
* We have several strategies for determining whether one simple clause
10971098
* implies another:
10981099
*
10991100
* A simple and general way is to see if they are equal(); this works for any
11001101
* kind of expression, and for either implication definition. (Actually,
11011102
* there is an implied assumption that the functions in the expression are
11021103
* immutable --- but this was checked for the predicate by the caller.)
11031104
*
1105+
* Another way that always works is that for boolean x, "x = TRUE" is
1106+
* equivalent to "x", likewise "x = FALSE" is equivalent to "NOT x".
1107+
* These can be worth checking because, while we preferentially simplify
1108+
* boolean comparisons down to "x" and "NOT x", the other form has to be
1109+
* dealt with anyway in the context of index conditions.
1110+
*
11041111
* If the predicate is of the form "foo IS NOT NULL", and we are considering
11051112
* strong implication, we can conclude that the predicate is implied if the
11061113
* clause is strict for "foo", i.e., it must yield false or NULL when "foo"
@@ -1124,6 +1131,43 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause,
11241131
if (equal((Node *) predicate, clause))
11251132
return true;
11261133

1134+
/* Next see if clause is boolean equality to a constant */
1135+
if (is_opclause(clause) &&
1136+
((OpExpr *) clause)->opno == BooleanEqualOperator)
1137+
{
1138+
OpExpr *op = (OpExpr *) clause;
1139+
Node *rightop;
1140+
1141+
Assert(list_length(op->args) == 2);
1142+
rightop = lsecond(op->args);
1143+
/* We might never see a null Const here, but better check anyway */
1144+
if (rightop && IsA(rightop, Const) &&
1145+
!((Const *) rightop)->constisnull)
1146+
{
1147+
Node *leftop = linitial(op->args);
1148+
1149+
if (DatumGetBool(((Const *) rightop)->constvalue))
1150+
{
1151+
/* X = true implies X */
1152+
if (equal(predicate, leftop))
1153+
return true;
1154+
}
1155+
else
1156+
{
1157+
/* X = false implies NOT X */
1158+
if (is_notclause(predicate) &&
1159+
equal(get_notclausearg(predicate), leftop))
1160+
return true;
1161+
}
1162+
}
1163+
}
1164+
1165+
/*
1166+
* We could likewise check whether the predicate is boolean equality to a
1167+
* constant; but there are no known use-cases for that at the moment,
1168+
* assuming that the predicate has been through constant-folding.
1169+
*/
1170+
11271171
/* Next try the IS NOT NULL case */
11281172
if (!weak &&
11291173
predicate && IsA(predicate, NullTest))

0 commit comments

Comments
 (0)
Please sign in to comment.