Skip to content

Commit 07c36c1

Browse files
committed
Support partition pruning on boolcol IS [NOT] UNKNOWN
While working on 4c2369a, I noticed we went out of our way not to support clauses on boolean partitioned tables in the form of "IS UNKNOWN" and "IS NOT UNKNOWN". It's almost as much code to disallow this as it is to allow it, so let's allow it. Discussion: https://postgr.es/m/CAApHDvobKtcN6+xOuOfcutfp6T7jP=JPA9y3=MAEqnuKdDsQrw@mail.gmail.com
1 parent eca2c1e commit 07c36c1

File tree

3 files changed

+93
-41
lines changed

3 files changed

+93
-41
lines changed

src/backend/partitioning/partprune.c

+40-31
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily,
200200
Expr *clause,
201201
Expr *partkey,
202202
Expr **outconst,
203-
bool *noteq);
203+
bool *notclause);
204204
static void partkey_datum_from_expr(PartitionPruneContext *context,
205205
Expr *expr, int stateidx,
206206
Datum *value, bool *isnull);
@@ -1798,13 +1798,14 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
17981798
Oid partopfamily = part_scheme->partopfamily[partkeyidx],
17991799
partcoll = part_scheme->partcollation[partkeyidx];
18001800
Expr *expr;
1801-
bool noteq;
1801+
bool notclause;
18021802

18031803
/*
18041804
* Recognize specially shaped clauses that match a Boolean partition key.
18051805
*/
18061806
boolmatchstatus = match_boolean_partition_clause(partopfamily, clause,
1807-
partkey, &expr, &noteq);
1807+
partkey, &expr,
1808+
&notclause);
18081809

18091810
if (boolmatchstatus == PARTCLAUSE_MATCH_CLAUSE)
18101811
{
@@ -1818,7 +1819,7 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
18181819
* punt it off to gen_partprune_steps_internal() to generate pruning
18191820
* steps.
18201821
*/
1821-
if (noteq)
1822+
if (notclause)
18221823
{
18231824
List *new_clauses;
18241825
List *or_clause;
@@ -1836,9 +1837,8 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
18361837
else
18371838
{
18381839
/*
1839-
* We only expect match_boolean_partition_clause to match for
1840-
* IS_NOT_TRUE and IS_NOT_FALSE. IS_NOT_UNKNOWN is not
1841-
* supported.
1840+
* We only expect match_boolean_partition_clause to return
1841+
* PARTCLAUSE_MATCH_CLAUSE for IS_NOT_TRUE and IS_NOT_FALSE.
18421842
*/
18431843
Assert(false);
18441844
}
@@ -1876,6 +1876,15 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
18761876

18771877
return PARTCLAUSE_MATCH_CLAUSE;
18781878
}
1879+
else if (boolmatchstatus == PARTCLAUSE_MATCH_NULLNESS)
1880+
{
1881+
/*
1882+
* Handle IS UNKNOWN and IS NOT UNKNOWN. These just logically
1883+
* translate to IS NULL and IS NOT NULL.
1884+
*/
1885+
*clause_is_not_null = notclause;
1886+
return PARTCLAUSE_MATCH_NULLNESS;
1887+
}
18791888
else if (IsA(clause, OpExpr) &&
18801889
list_length(((OpExpr *) clause)->args) == 2)
18811890
{
@@ -3652,22 +3661,23 @@ perform_pruning_combine_step(PartitionPruneContext *context,
36523661
* match_boolean_partition_clause
36533662
*
36543663
* If we're able to match the clause to the partition key as specially-shaped
3655-
* boolean clause, set *outconst to a Const containing a true or false value,
3656-
* set *noteq according to if the clause was in the "not" form, i.e. "is not
3657-
* true" or "is not false", and return PARTCLAUSE_MATCH_CLAUSE. Returns
3658-
* PARTCLAUSE_UNSUPPORTED if the clause is not a boolean clause or if the
3659-
* boolean clause is unsuitable for partition pruning. Returns
3660-
* PARTCLAUSE_NOMATCH if it's a bool quals but just does not match this
3661-
* partition key. *outconst is set to NULL in the latter two cases.
3664+
* boolean clause, set *outconst to a Const containing a true, false or NULL
3665+
* value, set *notclause according to if the clause was in the "not" form,
3666+
* i.e. "IS NOT TRUE", "IS NOT FALSE" or "IS NOT UNKNOWN" and return
3667+
* PARTCLAUSE_MATCH_CLAUSE for "IS [NOT] (TRUE|FALSE)" clauses and
3668+
* PARTCLAUSE_MATCH_NULLNESS for "IS [NOT] UNKNOWN" clauses. Otherwise,
3669+
* return PARTCLAUSE_UNSUPPORTED if the clause cannot be used for partition
3670+
* pruning, and PARTCLAUSE_NOMATCH for supported clauses that do not match this
3671+
* 'partkey'.
36623672
*/
36633673
static PartClauseMatchStatus
36643674
match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
3665-
Expr **outconst, bool *noteq)
3675+
Expr **outconst, bool *notclause)
36663676
{
36673677
Expr *leftop;
36683678

36693679
*outconst = NULL;
3670-
*noteq = false;
3680+
*notclause = false;
36713681

36723682
/*
36733683
* Partitioning currently can only use built-in AMs, so checking for
@@ -3680,11 +3690,6 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
36803690
{
36813691
BooleanTest *btest = (BooleanTest *) clause;
36823692

3683-
/* Only IS [NOT] TRUE/FALSE are any good to us */
3684-
if (btest->booltesttype == IS_UNKNOWN ||
3685-
btest->booltesttype == IS_NOT_UNKNOWN)
3686-
return PARTCLAUSE_UNSUPPORTED;
3687-
36883693
leftop = btest->arg;
36893694
if (IsA(leftop, RelabelType))
36903695
leftop = ((RelabelType *) leftop)->arg;
@@ -3694,23 +3699,28 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
36943699
switch (btest->booltesttype)
36953700
{
36963701
case IS_NOT_TRUE:
3697-
*noteq = true;
3702+
*notclause = true;
36983703
/* fall through */
36993704
case IS_TRUE:
37003705
*outconst = (Expr *) makeBoolConst(true, false);
3701-
break;
3706+
return PARTCLAUSE_MATCH_CLAUSE;
37023707
case IS_NOT_FALSE:
3703-
*noteq = true;
3708+
*notclause = true;
37043709
/* fall through */
37053710
case IS_FALSE:
37063711
*outconst = (Expr *) makeBoolConst(false, false);
3707-
break;
3712+
return PARTCLAUSE_MATCH_CLAUSE;
3713+
case IS_NOT_UNKNOWN:
3714+
*notclause = true;
3715+
/* fall through */
3716+
case IS_UNKNOWN:
3717+
return PARTCLAUSE_MATCH_NULLNESS;
37083718
default:
37093719
return PARTCLAUSE_UNSUPPORTED;
37103720
}
37113721
}
3712-
if (*outconst)
3713-
return PARTCLAUSE_MATCH_CLAUSE;
3722+
/* does not match partition key */
3723+
return PARTCLAUSE_NOMATCH;
37143724
}
37153725
else
37163726
{
@@ -3726,12 +3736,11 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
37263736
*outconst = (Expr *) makeBoolConst(!is_not_clause, false);
37273737
else if (equal(negate_clause((Node *) leftop), partkey))
37283738
*outconst = (Expr *) makeBoolConst(is_not_clause, false);
3739+
else
3740+
return PARTCLAUSE_NOMATCH;
37293741

3730-
if (*outconst)
3731-
return PARTCLAUSE_MATCH_CLAUSE;
3742+
return PARTCLAUSE_MATCH_CLAUSE;
37323743
}
3733-
3734-
return PARTCLAUSE_NOMATCH;
37353744
}
37363745

37373746
/*

src/test/regress/expected/partition_prune.out

+46-10
Original file line numberDiff line numberDiff line change
@@ -1093,16 +1093,11 @@ explain (costs off) select * from boolpart where a is not true and a is not fals
10931093
(2 rows)
10941094

10951095
explain (costs off) select * from boolpart where a is unknown;
1096-
QUERY PLAN
1097-
-----------------------------------------------
1098-
Append
1099-
-> Seq Scan on boolpart_f boolpart_1
1100-
Filter: (a IS UNKNOWN)
1101-
-> Seq Scan on boolpart_t boolpart_2
1102-
Filter: (a IS UNKNOWN)
1103-
-> Seq Scan on boolpart_default boolpart_3
1104-
Filter: (a IS UNKNOWN)
1105-
(7 rows)
1096+
QUERY PLAN
1097+
---------------------------------------
1098+
Seq Scan on boolpart_default boolpart
1099+
Filter: (a IS UNKNOWN)
1100+
(2 rows)
11061101

11071102
explain (costs off) select * from boolpart where a is not unknown;
11081103
QUERY PLAN
@@ -1200,6 +1195,18 @@ explain (costs off) select * from boolpart where a is not false;
12001195
Filter: (a IS NOT FALSE)
12011196
(5 rows)
12021197

1198+
explain (costs off) select * from boolpart where a is not unknown;
1199+
QUERY PLAN
1200+
-----------------------------------------------
1201+
Append
1202+
-> Seq Scan on boolpart_f boolpart_1
1203+
Filter: (a IS NOT UNKNOWN)
1204+
-> Seq Scan on boolpart_t boolpart_2
1205+
Filter: (a IS NOT UNKNOWN)
1206+
-> Seq Scan on boolpart_default boolpart_3
1207+
Filter: (a IS NOT UNKNOWN)
1208+
(7 rows)
1209+
12031210
select * from boolpart where a is not true;
12041211
a
12051212
---
@@ -1220,6 +1227,35 @@ select * from boolpart where a is not false;
12201227

12211228
(2 rows)
12221229

1230+
select * from boolpart where a is not unknown;
1231+
a
1232+
---
1233+
f
1234+
t
1235+
(2 rows)
1236+
1237+
-- check that all partitions are pruned when faced with conflicting clauses
1238+
explain (costs off) select * from boolpart where a is not unknown and a is unknown;
1239+
QUERY PLAN
1240+
--------------------------
1241+
Result
1242+
One-Time Filter: false
1243+
(2 rows)
1244+
1245+
explain (costs off) select * from boolpart where a is false and a is unknown;
1246+
QUERY PLAN
1247+
--------------------------
1248+
Result
1249+
One-Time Filter: false
1250+
(2 rows)
1251+
1252+
explain (costs off) select * from boolpart where a is true and a is unknown;
1253+
QUERY PLAN
1254+
--------------------------
1255+
Result
1256+
One-Time Filter: false
1257+
(2 rows)
1258+
12231259
-- inverse boolean partitioning - a seemingly unlikely design, but we've got
12241260
-- code for it, so we'd better test it.
12251261
create table iboolpart (a bool) partition by list ((not a));

src/test/regress/sql/partition_prune.sql

+7
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,17 @@ insert into boolpart values(null);
186186
explain (costs off) select * from boolpart where a is not true;
187187
explain (costs off) select * from boolpart where a is not true and a is not false;
188188
explain (costs off) select * from boolpart where a is not false;
189+
explain (costs off) select * from boolpart where a is not unknown;
189190

190191
select * from boolpart where a is not true;
191192
select * from boolpart where a is not true and a is not false;
192193
select * from boolpart where a is not false;
194+
select * from boolpart where a is not unknown;
195+
196+
-- check that all partitions are pruned when faced with conflicting clauses
197+
explain (costs off) select * from boolpart where a is not unknown and a is unknown;
198+
explain (costs off) select * from boolpart where a is false and a is unknown;
199+
explain (costs off) select * from boolpart where a is true and a is unknown;
193200

194201
-- inverse boolean partitioning - a seemingly unlikely design, but we've got
195202
-- code for it, so we'd better test it.

0 commit comments

Comments
 (0)