Skip to content

Commit 4f26932

Browse files
author
Etsuro Fujita
committed
Fix yet another issue with step generation in partition pruning.
Commit 1383874 fixed some issues with step generation in partition pruning, but there was yet another one: get_steps_using_prefix() assumes that clauses in the passed-in prefix list are sorted in ascending order of their partition key numbers, but the caller failed to ensure this for range partitioning, which led to an assertion failure in debug builds. Adjust the caller function to arrange the clauses in the prefix list in the required order for range partitioning. Back-patch to v11, like the previous commit. Patch by me, reviewed by Amit Langote. Discussion: https://postgr.es/m/CAPmGK16jkXiFG0YqMbU66wte-oJTfW6D1HaNvQf%3D%2B5o9%3Dm55wQ%40mail.gmail.com
1 parent a2e0cf4 commit 4f26932

File tree

3 files changed

+96
-57
lines changed

3 files changed

+96
-57
lines changed

src/backend/partitioning/partprune.c

Lines changed: 81 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,7 +1375,6 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
13751375
List *eq_clauses = btree_clauses[BTEqualStrategyNumber];
13761376
List *le_clauses = btree_clauses[BTLessEqualStrategyNumber];
13771377
List *ge_clauses = btree_clauses[BTGreaterEqualStrategyNumber];
1378-
bool pk_has_clauses[PARTITION_MAX_KEYS];
13791378
int strat;
13801379

13811380
/*
@@ -1395,10 +1394,15 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
13951394
foreach(lc, btree_clauses[strat])
13961395
{
13971396
PartClauseInfo *pc = lfirst(lc);
1397+
ListCell *eq_start;
1398+
ListCell *le_start;
1399+
ListCell *ge_start;
13981400
ListCell *lc1;
13991401
List *prefix = NIL;
14001402
List *pc_steps;
14011403
bool prefix_valid = true;
1404+
bool pk_has_clauses;
1405+
int keyno;
14021406

14031407
/*
14041408
* If this is a clause for the first partition key,
@@ -1423,79 +1427,96 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
14231427
continue;
14241428
}
14251429

1426-
/* (Re-)initialize the pk_has_clauses array */
1427-
Assert(pc->keyno > 0);
1428-
for (i = 0; i < pc->keyno; i++)
1429-
pk_has_clauses[i] = false;
1430+
eq_start = list_head(eq_clauses);
1431+
le_start = list_head(le_clauses);
1432+
ge_start = list_head(ge_clauses);
14301433

14311434
/*
1432-
* Expressions from = clauses can always be in the
1433-
* prefix, provided they're from an earlier key.
1435+
* We arrange clauses into prefix in ascending order
1436+
* of their partition key numbers.
14341437
*/
1435-
foreach(lc1, eq_clauses)
1438+
for (keyno = 0; keyno < pc->keyno; keyno++)
14361439
{
1437-
PartClauseInfo *eqpc = lfirst(lc1);
1440+
pk_has_clauses = false;
14381441

1439-
if (eqpc->keyno == pc->keyno)
1440-
break;
1441-
if (eqpc->keyno < pc->keyno)
1442+
/*
1443+
* Expressions from = clauses can always be in the
1444+
* prefix, provided they're from an earlier key.
1445+
*/
1446+
for_each_cell(lc1, eq_start)
14421447
{
1443-
prefix = lappend(prefix, eqpc);
1444-
pk_has_clauses[eqpc->keyno] = true;
1445-
}
1446-
}
1448+
PartClauseInfo *eqpc = lfirst(lc1);
14471449

1448-
/*
1449-
* If we're generating steps for </<= strategy, we can
1450-
* add other <= clauses to the prefix, provided
1451-
* they're from an earlier key.
1452-
*/
1453-
if (strat == BTLessStrategyNumber ||
1454-
strat == BTLessEqualStrategyNumber)
1455-
{
1456-
foreach(lc1, le_clauses)
1457-
{
1458-
PartClauseInfo *lepc = lfirst(lc1);
1459-
1460-
if (lepc->keyno == pc->keyno)
1450+
if (eqpc->keyno == keyno)
1451+
{
1452+
prefix = lappend(prefix, eqpc);
1453+
pk_has_clauses = true;
1454+
}
1455+
else
1456+
{
1457+
Assert(eqpc->keyno > keyno);
14611458
break;
1462-
if (lepc->keyno < pc->keyno)
1459+
}
1460+
}
1461+
eq_start = lc1;
1462+
1463+
/*
1464+
* If we're generating steps for </<= strategy, we
1465+
* can add other <= clauses to the prefix,
1466+
* provided they're from an earlier key.
1467+
*/
1468+
if (strat == BTLessStrategyNumber ||
1469+
strat == BTLessEqualStrategyNumber)
1470+
{
1471+
for_each_cell(lc1, le_start)
14631472
{
1464-
prefix = lappend(prefix, lepc);
1465-
pk_has_clauses[lepc->keyno] = true;
1473+
PartClauseInfo *lepc = lfirst(lc1);
1474+
1475+
if (lepc->keyno == keyno)
1476+
{
1477+
prefix = lappend(prefix, lepc);
1478+
pk_has_clauses = true;
1479+
}
1480+
else
1481+
{
1482+
Assert(lepc->keyno > keyno);
1483+
break;
1484+
}
14661485
}
1486+
le_start = lc1;
14671487
}
1468-
}
14691488

1470-
/*
1471-
* If we're generating steps for >/>= strategy, we can
1472-
* add other >= clauses to the prefix, provided
1473-
* they're from an earlier key.
1474-
*/
1475-
if (strat == BTGreaterStrategyNumber ||
1476-
strat == BTGreaterEqualStrategyNumber)
1477-
{
1478-
foreach(lc1, ge_clauses)
1489+
/*
1490+
* If we're generating steps for >/>= strategy, we
1491+
* can add other >= clauses to the prefix,
1492+
* provided they're from an earlier key.
1493+
*/
1494+
if (strat == BTGreaterStrategyNumber ||
1495+
strat == BTGreaterEqualStrategyNumber)
14791496
{
1480-
PartClauseInfo *gepc = lfirst(lc1);
1481-
1482-
if (gepc->keyno == pc->keyno)
1483-
break;
1484-
if (gepc->keyno < pc->keyno)
1497+
for_each_cell(lc1, ge_start)
14851498
{
1486-
prefix = lappend(prefix, gepc);
1487-
pk_has_clauses[gepc->keyno] = true;
1499+
PartClauseInfo *gepc = lfirst(lc1);
1500+
1501+
if (gepc->keyno == keyno)
1502+
{
1503+
prefix = lappend(prefix, gepc);
1504+
pk_has_clauses = true;
1505+
}
1506+
else
1507+
{
1508+
Assert(gepc->keyno > keyno);
1509+
break;
1510+
}
14881511
}
1512+
ge_start = lc1;
14891513
}
1490-
}
14911514

1492-
/*
1493-
* Check whether every earlier partition key has at
1494-
* least one clause.
1495-
*/
1496-
for (i = 0; i < pc->keyno; i++)
1497-
{
1498-
if (!pk_has_clauses[i])
1515+
/*
1516+
* If this key has no clauses, prefix is not valid
1517+
* anymore.
1518+
*/
1519+
if (!pk_has_clauses)
14991520
{
15001521
prefix_valid = false;
15011522
break;
@@ -2254,6 +2275,9 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
22542275
* non-NULL, but they must ensure that prefix contains at least one clause
22552276
* for each of the partition keys other than those specified in step_nullkeys
22562277
* and step_lastkeyno.
2278+
*
2279+
* For both cases, callers must also ensure that clauses in prefix are sorted
2280+
* in ascending order of their partition key numbers.
22572281
*/
22582282
static List *
22592283
get_steps_using_prefix(GeneratePruningStepsContext *context,

src/test/regress/expected/partition_prune.out

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3963,6 +3963,16 @@ explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b
39633963
Filter: ((a >= 1) AND (b >= 1) AND (b >= 2) AND (c >= 2) AND (d >= 0))
39643964
(2 rows)
39653965

3966+
-- Test that get_steps_using_prefix() handles a prefix that contains multiple
3967+
-- clauses for the partition key b (ie, b >= 1 and b = 2) (This also tests
3968+
-- that the caller arranges clauses in that prefix in the required order)
3969+
explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b = 2 and c = 2 and d >= 0;
3970+
QUERY PLAN
3971+
------------------------------------------------------------------------
3972+
Seq Scan on rp_prefix_test3_p2
3973+
Filter: ((a >= 1) AND (b >= 1) AND (d >= 0) AND (b = 2) AND (c = 2))
3974+
(2 rows)
3975+
39663976
create table hp_prefix_test (a int, b int, c int, d int) partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops);
39673977
create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0);
39683978
create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 2, remainder 1);

src/test/regress/sql/partition_prune.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,11 @@ create table rp_prefix_test3_p2 partition of rp_prefix_test3 for values from (2,
11411141
-- clauses for the partition key b (ie, b >= 1 and b >= 2)
11421142
explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b >= 2 and c >= 2 and d >= 0;
11431143

1144+
-- Test that get_steps_using_prefix() handles a prefix that contains multiple
1145+
-- clauses for the partition key b (ie, b >= 1 and b = 2) (This also tests
1146+
-- that the caller arranges clauses in that prefix in the required order)
1147+
explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b = 2 and c = 2 and d >= 0;
1148+
11441149
create table hp_prefix_test (a int, b int, c int, d int) partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops);
11451150
create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0);
11461151
create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 2, remainder 1);

0 commit comments

Comments
 (0)