Skip to content

Commit 1db9c80

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 495a9b1 commit 1db9c80

File tree

3 files changed

+97
-57
lines changed

3 files changed

+97
-57
lines changed

src/backend/partitioning/partprune.c

Lines changed: 81 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,7 +1355,6 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
13551355
List *eq_clauses = btree_clauses[BTEqualStrategyNumber];
13561356
List *le_clauses = btree_clauses[BTLessEqualStrategyNumber];
13571357
List *ge_clauses = btree_clauses[BTGreaterEqualStrategyNumber];
1358-
bool pk_has_clauses[PARTITION_MAX_KEYS];
13591358
int strat;
13601359

13611360
/*
@@ -1375,10 +1374,15 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
13751374
foreach(lc, btree_clauses[strat])
13761375
{
13771376
PartClauseInfo *pc = lfirst(lc);
1377+
ListCell *eq_start;
1378+
ListCell *le_start;
1379+
ListCell *ge_start;
13781380
ListCell *lc1;
13791381
List *prefix = NIL;
13801382
List *pc_steps;
13811383
bool prefix_valid = true;
1384+
bool pk_has_clauses;
1385+
int keyno;
13821386

13831387
/*
13841388
* If this is a clause for the first partition key,
@@ -1403,79 +1407,96 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
14031407
continue;
14041408
}
14051409

1406-
/* (Re-)initialize the pk_has_clauses array */
1407-
Assert(pc->keyno > 0);
1408-
for (i = 0; i < pc->keyno; i++)
1409-
pk_has_clauses[i] = false;
1410+
eq_start = list_head(eq_clauses);
1411+
le_start = list_head(le_clauses);
1412+
ge_start = list_head(ge_clauses);
14101413

14111414
/*
1412-
* Expressions from = clauses can always be in the
1413-
* prefix, provided they're from an earlier key.
1415+
* We arrange clauses into prefix in ascending order
1416+
* of their partition key numbers.
14141417
*/
1415-
foreach(lc1, eq_clauses)
1418+
for (keyno = 0; keyno < pc->keyno; keyno++)
14161419
{
1417-
PartClauseInfo *eqpc = lfirst(lc1);
1420+
pk_has_clauses = false;
14181421

1419-
if (eqpc->keyno == pc->keyno)
1420-
break;
1421-
if (eqpc->keyno < pc->keyno)
1422+
/*
1423+
* Expressions from = clauses can always be in the
1424+
* prefix, provided they're from an earlier key.
1425+
*/
1426+
for_each_cell(lc1, eq_start)
14221427
{
1423-
prefix = lappend(prefix, eqpc);
1424-
pk_has_clauses[eqpc->keyno] = true;
1425-
}
1426-
}
1428+
PartClauseInfo *eqpc = lfirst(lc1);
14271429

1428-
/*
1429-
* If we're generating steps for </<= strategy, we can
1430-
* add other <= clauses to the prefix, provided
1431-
* they're from an earlier key.
1432-
*/
1433-
if (strat == BTLessStrategyNumber ||
1434-
strat == BTLessEqualStrategyNumber)
1435-
{
1436-
foreach(lc1, le_clauses)
1437-
{
1438-
PartClauseInfo *lepc = lfirst(lc1);
1439-
1440-
if (lepc->keyno == pc->keyno)
1430+
if (eqpc->keyno == keyno)
1431+
{
1432+
prefix = lappend(prefix, eqpc);
1433+
pk_has_clauses = true;
1434+
}
1435+
else
1436+
{
1437+
Assert(eqpc->keyno > keyno);
14411438
break;
1442-
if (lepc->keyno < pc->keyno)
1439+
}
1440+
}
1441+
eq_start = lc1;
1442+
1443+
/*
1444+
* If we're generating steps for </<= strategy, we
1445+
* can add other <= clauses to the prefix,
1446+
* provided they're from an earlier key.
1447+
*/
1448+
if (strat == BTLessStrategyNumber ||
1449+
strat == BTLessEqualStrategyNumber)
1450+
{
1451+
for_each_cell(lc1, le_start)
14431452
{
1444-
prefix = lappend(prefix, lepc);
1445-
pk_has_clauses[lepc->keyno] = true;
1453+
PartClauseInfo *lepc = lfirst(lc1);
1454+
1455+
if (lepc->keyno == keyno)
1456+
{
1457+
prefix = lappend(prefix, lepc);
1458+
pk_has_clauses = true;
1459+
}
1460+
else
1461+
{
1462+
Assert(lepc->keyno > keyno);
1463+
break;
1464+
}
14461465
}
1466+
le_start = lc1;
14471467
}
1448-
}
14491468

1450-
/*
1451-
* If we're generating steps for >/>= strategy, we can
1452-
* add other >= clauses to the prefix, provided
1453-
* they're from an earlier key.
1454-
*/
1455-
if (strat == BTGreaterStrategyNumber ||
1456-
strat == BTGreaterEqualStrategyNumber)
1457-
{
1458-
foreach(lc1, ge_clauses)
1469+
/*
1470+
* If we're generating steps for >/>= strategy, we
1471+
* can add other >= clauses to the prefix,
1472+
* provided they're from an earlier key.
1473+
*/
1474+
if (strat == BTGreaterStrategyNumber ||
1475+
strat == BTGreaterEqualStrategyNumber)
14591476
{
1460-
PartClauseInfo *gepc = lfirst(lc1);
1461-
1462-
if (gepc->keyno == pc->keyno)
1463-
break;
1464-
if (gepc->keyno < pc->keyno)
1477+
for_each_cell(lc1, ge_start)
14651478
{
1466-
prefix = lappend(prefix, gepc);
1467-
pk_has_clauses[gepc->keyno] = true;
1479+
PartClauseInfo *gepc = lfirst(lc1);
1480+
1481+
if (gepc->keyno == keyno)
1482+
{
1483+
prefix = lappend(prefix, gepc);
1484+
pk_has_clauses = true;
1485+
}
1486+
else
1487+
{
1488+
Assert(gepc->keyno > keyno);
1489+
break;
1490+
}
14681491
}
1492+
ge_start = lc1;
14691493
}
1470-
}
14711494

1472-
/*
1473-
* Check whether every earlier partition key has at
1474-
* least one clause.
1475-
*/
1476-
for (i = 0; i < pc->keyno; i++)
1477-
{
1478-
if (!pk_has_clauses[i])
1495+
/*
1496+
* If this key has no clauses, prefix is not valid
1497+
* anymore.
1498+
*/
1499+
if (!pk_has_clauses)
14791500
{
14801501
prefix_valid = false;
14811502
break;
@@ -2234,6 +2255,9 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
22342255
* non-NULL, but they must ensure that prefix contains at least one clause
22352256
* for each of the partition keys other than those specified in step_nullkeys
22362257
* and step_lastkeyno.
2258+
*
2259+
* For both cases, callers must also ensure that clauses in prefix are sorted
2260+
* in ascending order of their partition key numbers.
22372261
*/
22382262
static List *
22392263
get_steps_using_prefix(GeneratePruningStepsContext *context,

src/test/regress/expected/partition_prune.out

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3747,6 +3747,17 @@ explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b
37473747
Filter: ((a >= 1) AND (b >= 1) AND (b >= 2) AND (c >= 2) AND (d >= 0))
37483748
(3 rows)
37493749

3750+
-- Test that get_steps_using_prefix() handles a prefix that contains multiple
3751+
-- clauses for the partition key b (ie, b >= 1 and b = 2) (This also tests
3752+
-- that the caller arranges clauses in that prefix in the required order)
3753+
explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b = 2 and c = 2 and d >= 0;
3754+
QUERY PLAN
3755+
------------------------------------------------------------------------------
3756+
Append
3757+
-> Seq Scan on rp_prefix_test3_p2
3758+
Filter: ((a >= 1) AND (b >= 1) AND (d >= 0) AND (b = 2) AND (c = 2))
3759+
(3 rows)
3760+
37503761
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);
37513762
create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0);
37523763
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
@@ -1051,6 +1051,11 @@ create table rp_prefix_test3_p2 partition of rp_prefix_test3 for values from (2,
10511051
-- clauses for the partition key b (ie, b >= 1 and b >= 2)
10521052
explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b >= 2 and c >= 2 and d >= 0;
10531053

1054+
-- Test that get_steps_using_prefix() handles a prefix that contains multiple
1055+
-- clauses for the partition key b (ie, b >= 1 and b = 2) (This also tests
1056+
-- that the caller arranges clauses in that prefix in the required order)
1057+
explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b = 2 and c = 2 and d >= 0;
1058+
10541059
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);
10551060
create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0);
10561061
create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 2, remainder 1);

0 commit comments

Comments
 (0)