Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit cfde234

Browse files
committed
Fix RANGE partition pruning with multiple boolean partition keys
match_clause_to_partition_key incorrectly would return PARTCLAUSE_UNSUPPORTED if a bool qual could not be matched to the current partition key. This was a problem, as it causes the calling function to discard the qual and not try to match it to any other partition key. If there was another partition key which did match this qual, then the qual would not be checked again and we could fail to prune some partitions. The worst this could do was to cause partitions not to be pruned when they could have been, so there was no danger of incorrect query results here. Fix this by changing match_boolean_partition_clause to have it return a PartClauseMatchStatus rather than a boolean value. This allows it to communicate if the qual is unsupported or if it just does not match this particular partition key, previously these two cases were treated the same. Now, if match_clause_to_partition_key is unable to match the qual to any other qual type then we can simply return the value from the match_boolean_partition_clause call so that the calling function properly treats the qual as either unmatched or unsupported. Reported-by: Rares Salcudean Reviewed-by: Amit Langote Backpatch-through: 11 where partition pruning was introduced Discussion: https://postgr.es/m/CAHp_FN2xwEznH6oyS0hNTuUUZKp5PvegcVv=Co6nBXJ+mC7Y5w@mail.gmail.com
1 parent 0cea6eb commit cfde234

File tree

3 files changed

+61
-15
lines changed

3 files changed

+61
-15
lines changed

src/backend/partitioning/partprune.c

+37-13
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,10 @@ static PruneStepResult *perform_pruning_base_step(PartitionPruneContext *context
194194
static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *context,
195195
PartitionPruneStepCombine *cstep,
196196
PruneStepResult **step_results);
197-
static bool match_boolean_partition_clause(Oid partopfamily, Expr *clause,
198-
Expr *partkey, Expr **outconst);
197+
static PartClauseMatchStatus match_boolean_partition_clause(Oid partopfamily,
198+
Expr *clause,
199+
Expr *partkey,
200+
Expr **outconst);
199201
static void partkey_datum_from_expr(PartitionPruneContext *context,
200202
Expr *expr, int stateidx,
201203
Datum *value, bool *isnull);
@@ -1623,6 +1625,7 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
16231625
bool *clause_is_not_null, PartClauseInfo **pc,
16241626
List **clause_steps)
16251627
{
1628+
PartClauseMatchStatus boolmatchstatus;
16261629
PartitionScheme part_scheme = context->rel->part_scheme;
16271630
Oid partopfamily = part_scheme->partopfamily[partkeyidx],
16281631
partcoll = part_scheme->partcollation[partkeyidx];
@@ -1631,7 +1634,10 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
16311634
/*
16321635
* Recognize specially shaped clauses that match a Boolean partition key.
16331636
*/
1634-
if (match_boolean_partition_clause(partopfamily, clause, partkey, &expr))
1637+
boolmatchstatus = match_boolean_partition_clause(partopfamily, clause,
1638+
partkey, &expr);
1639+
1640+
if (boolmatchstatus == PARTCLAUSE_MATCH_CLAUSE)
16351641
{
16361642
PartClauseInfo *partclause;
16371643

@@ -2147,7 +2153,21 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
21472153
return PARTCLAUSE_MATCH_NULLNESS;
21482154
}
21492155

2150-
return PARTCLAUSE_UNSUPPORTED;
2156+
/*
2157+
* If we get here then the return value depends on the result of the
2158+
* match_boolean_partition_clause call above. If the call returned
2159+
* PARTCLAUSE_UNSUPPORTED then we're either not dealing with a bool qual
2160+
* or the bool qual is not suitable for pruning. Since the qual didn't
2161+
* match up to any of the other qual types supported here, then trying to
2162+
* match it against any other partition key is a waste of time, so just
2163+
* return PARTCLAUSE_UNSUPPORTED. If the qual just couldn't be matched to
2164+
* this partition key, then it may match another, so return
2165+
* PARTCLAUSE_NOMATCH. The only other value that
2166+
* match_boolean_partition_clause can return is PARTCLAUSE_MATCH_CLAUSE,
2167+
* and since that value was already dealt with above, then we can just
2168+
* return boolmatchstatus.
2169+
*/
2170+
return boolmatchstatus;
21512171
}
21522172

21532173
/*
@@ -3395,11 +3415,15 @@ perform_pruning_combine_step(PartitionPruneContext *context,
33953415
/*
33963416
* match_boolean_partition_clause
33973417
*
3398-
* Sets *outconst to a Const containing true or false value and returns true if
3399-
* we're able to match the clause to the partition key as specially-shaped
3400-
* Boolean clause. Returns false otherwise with *outconst set to NULL.
3418+
* If we're able to match the clause to the partition key as specially-shaped
3419+
* boolean clause, set *outconst to a Const containing a true or false value
3420+
* and return PARTCLAUSE_MATCH_CLAUSE. Returns PARTCLAUSE_UNSUPPORTED if the
3421+
* clause is not a boolean clause or if the boolean clause is unsuitable for
3422+
* partition pruning. Returns PARTCLAUSE_NOMATCH if it's a bool quals but
3423+
* just does not match this partition key. *outconst is set to NULL in the
3424+
* latter two cases.
34013425
*/
3402-
static bool
3426+
static PartClauseMatchStatus
34033427
match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
34043428
Expr **outconst)
34053429
{
@@ -3408,7 +3432,7 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
34083432
*outconst = NULL;
34093433

34103434
if (!IsBooleanOpfamily(partopfamily))
3411-
return false;
3435+
return PARTCLAUSE_UNSUPPORTED;
34123436

34133437
if (IsA(clause, BooleanTest))
34143438
{
@@ -3417,7 +3441,7 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
34173441
/* Only IS [NOT] TRUE/FALSE are any good to us */
34183442
if (btest->booltesttype == IS_UNKNOWN ||
34193443
btest->booltesttype == IS_NOT_UNKNOWN)
3420-
return false;
3444+
return PARTCLAUSE_UNSUPPORTED;
34213445

34223446
leftop = btest->arg;
34233447
if (IsA(leftop, RelabelType))
@@ -3430,7 +3454,7 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
34303454
: (Expr *) makeBoolConst(false, false);
34313455

34323456
if (*outconst)
3433-
return true;
3457+
return PARTCLAUSE_MATCH_CLAUSE;
34343458
}
34353459
else
34363460
{
@@ -3450,10 +3474,10 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
34503474
*outconst = (Expr *) makeBoolConst(false, false);
34513475

34523476
if (*outconst)
3453-
return true;
3477+
return PARTCLAUSE_MATCH_CLAUSE;
34543478
}
34553479

3456-
return false;
3480+
return PARTCLAUSE_NOMATCH;
34573481
}
34583482

34593483
/*

src/test/regress/expected/partition_prune.out

+14-1
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,19 @@ explain (costs off) select * from boolpart where a is not unknown;
10861086
Filter: (a IS NOT UNKNOWN)
10871087
(7 rows)
10881088

1089+
create table boolrangep (a bool, b bool, c int) partition by range (a,b,c);
1090+
create table boolrangep_tf partition of boolrangep for values from ('true', 'false', 0) to ('true', 'false', 100);
1091+
create table boolrangep_ft partition of boolrangep for values from ('false', 'true', 0) to ('false', 'true', 100);
1092+
create table boolrangep_ff1 partition of boolrangep for values from ('false', 'false', 0) to ('false', 'false', 50);
1093+
create table boolrangep_ff2 partition of boolrangep for values from ('false', 'false', 50) to ('false', 'false', 100);
1094+
-- try a more complex case that's been known to trip up pruning in the past
1095+
explain (costs off) select * from boolrangep where not a and not b and c = 25;
1096+
QUERY PLAN
1097+
----------------------------------------------
1098+
Seq Scan on boolrangep_ff1
1099+
Filter: ((NOT a) AND (NOT b) AND (c = 25))
1100+
(2 rows)
1101+
10891102
-- test scalar-to-array operators
10901103
create table coercepart (a varchar) partition by list (a);
10911104
create table coercepart_ab partition of coercepart for values in ('ab');
@@ -1420,7 +1433,7 @@ explain (costs off) select * from rparted_by_int2 where a > 100000000000000;
14201433
Filter: (a > '100000000000000'::bigint)
14211434
(2 rows)
14221435

1423-
drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2;
1436+
drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, boolrangep, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2;
14241437
--
14251438
-- Test Partition pruning for HASH partitioning
14261439
--

src/test/regress/sql/partition_prune.sql

+10-1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@ explain (costs off) select * from boolpart where a is not true and a is not fals
159159
explain (costs off) select * from boolpart where a is unknown;
160160
explain (costs off) select * from boolpart where a is not unknown;
161161

162+
create table boolrangep (a bool, b bool, c int) partition by range (a,b,c);
163+
create table boolrangep_tf partition of boolrangep for values from ('true', 'false', 0) to ('true', 'false', 100);
164+
create table boolrangep_ft partition of boolrangep for values from ('false', 'true', 0) to ('false', 'true', 100);
165+
create table boolrangep_ff1 partition of boolrangep for values from ('false', 'false', 0) to ('false', 'false', 50);
166+
create table boolrangep_ff2 partition of boolrangep for values from ('false', 'false', 50) to ('false', 'false', 100);
167+
168+
-- try a more complex case that's been known to trip up pruning in the past
169+
explain (costs off) select * from boolrangep where not a and not b and c = 25;
170+
162171
-- test scalar-to-array operators
163172
create table coercepart (a varchar) partition by list (a);
164173
create table coercepart_ab partition of coercepart for values in ('ab');
@@ -264,7 +273,7 @@ create table rparted_by_int2_maxvalue partition of rparted_by_int2 for values fr
264273
-- all partitions but rparted_by_int2_maxvalue pruned
265274
explain (costs off) select * from rparted_by_int2 where a > 100000000000000;
266275

267-
drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2;
276+
drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, boolrangep, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2;
268277

269278
--
270279
-- Test Partition pruning for HASH partitioning

0 commit comments

Comments
 (0)