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

Commit d9e46df

Browse files
committed
Fix runtime partition pruning for HASH partitioned tables
This could only affect HASH partitioned tables with at least 2 partition key columns. If partition pruning was delayed until execution and the query contained an IS NULL qual on one of the partitioned keys, and some subsequent partitioned key was being compared to a non-Const, then this could result in a crash due to the incorrect keyno being used to calculate the stateidx for the expression evaluation code. Here we fix this by properly skipping partitioned keys which have a nullkey set. Effectively, this must be the same as what's going on inside perform_pruning_base_step(). Sergei Glukhov also provided a patch, but that's not what's being used here. Reported-by: Sergei Glukhov Reviewed-by: tender wang, Sergei Glukhov Discussion: https://postgr.es/m/d05b26fa-af54-27e1-f693-6c31590802fa@postgrespro.ru Backpatch-through: 11, where runtime partition pruning was added.
1 parent dab5538 commit d9e46df

File tree

3 files changed

+69
-27
lines changed

3 files changed

+69
-27
lines changed

src/backend/executor/execPartition.c

+29-24
Original file line numberDiff line numberDiff line change
@@ -2108,7 +2108,7 @@ InitPartitionPruneContext(PartitionPruneContext *context,
21082108
foreach(lc, pruning_steps)
21092109
{
21102110
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
2111-
ListCell *lc2;
2111+
ListCell *lc2 = list_head(step->exprs);
21122112
int keyno;
21132113

21142114
/* not needed for other step kinds */
@@ -2117,34 +2117,39 @@ InitPartitionPruneContext(PartitionPruneContext *context,
21172117

21182118
Assert(list_length(step->exprs) <= partnatts);
21192119

2120-
keyno = 0;
2121-
foreach(lc2, step->exprs)
2120+
for (keyno = 0; keyno < partnatts; keyno++)
21222121
{
2123-
Expr *expr = (Expr *) lfirst(lc2);
2122+
if (bms_is_member(keyno, step->nullkeys))
2123+
continue;
21242124

2125-
/* not needed for Consts */
2126-
if (!IsA(expr, Const))
2125+
if (lc2 != NULL)
21272126
{
2128-
int stateidx = PruneCxtStateIdx(partnatts,
2129-
step->step.step_id,
2130-
keyno);
2127+
Expr *expr = lfirst(lc2);
21312128

2132-
/*
2133-
* When planstate is NULL, pruning_steps is known not to
2134-
* contain any expressions that depend on the parent plan.
2135-
* Information of any available EXTERN parameters must be
2136-
* passed explicitly in that case, which the caller must have
2137-
* made available via econtext.
2138-
*/
2139-
if (planstate == NULL)
2140-
context->exprstates[stateidx] =
2141-
ExecInitExprWithParams(expr,
2142-
econtext->ecxt_param_list_info);
2143-
else
2144-
context->exprstates[stateidx] =
2145-
ExecInitExpr(expr, context->planstate);
2129+
/* not needed for Consts */
2130+
if (!IsA(expr, Const))
2131+
{
2132+
int stateidx = PruneCxtStateIdx(partnatts,
2133+
step->step.step_id,
2134+
keyno);
2135+
2136+
/*
2137+
* When planstate is NULL, pruning_steps is known not to
2138+
* contain any expressions that depend on the parent plan.
2139+
* Information of any available EXTERN parameters must be
2140+
* passed explicitly in that case, which the caller must
2141+
* have made available via econtext.
2142+
*/
2143+
if (planstate == NULL)
2144+
context->exprstates[stateidx] =
2145+
ExecInitExprWithParams(expr,
2146+
econtext->ecxt_param_list_info);
2147+
else
2148+
context->exprstates[stateidx] =
2149+
ExecInitExpr(expr, context->planstate);
2150+
}
2151+
lc2 = lnext(step->exprs, lc2);
21462152
}
2147-
keyno++;
21482153
}
21492154
}
21502155
}

src/test/regress/expected/partition_prune.out

+21-1
Original file line numberDiff line numberDiff line change
@@ -1948,7 +1948,6 @@ explain (costs off) select * from hp where a = 1 and b = 'abcde' and
19481948
One-Time Filter: false
19491949
(2 rows)
19501950

1951-
drop table hp;
19521951
--
19531952
-- Test runtime partition pruning
19541953
--
@@ -2070,6 +2069,27 @@ explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2);
20702069
Filter: ((b >= $1) AND (b <= $2) AND (a < $0))
20712070
(10 rows)
20722071

2072+
--
2073+
-- Test runtime pruning with hash partitioned tables
2074+
--
2075+
-- recreate partitions dropped above
2076+
create table hp1 partition of hp for values with (modulus 4, remainder 1);
2077+
create table hp2 partition of hp for values with (modulus 4, remainder 2);
2078+
create table hp3 partition of hp for values with (modulus 4, remainder 3);
2079+
-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual
2080+
prepare hp_q1 (text) as
2081+
select * from hp where a is null and b = $1;
2082+
explain (costs off) execute hp_q1('xxx');
2083+
QUERY PLAN
2084+
--------------------------------------------
2085+
Append
2086+
Subplans Removed: 3
2087+
-> Seq Scan on hp2 hp_1
2088+
Filter: ((a IS NULL) AND (b = $1))
2089+
(4 rows)
2090+
2091+
deallocate hp_q1;
2092+
drop table hp;
20732093
-- Test a backwards Append scan
20742094
create table list_part (a int) partition by list (a);
20752095
create table list_part1 partition of list_part for values in (1);

src/test/regress/sql/partition_prune.sql

+19-2
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,6 @@ drop table hp2;
384384
explain (costs off) select * from hp where a = 1 and b = 'abcde' and
385385
(c = 2 or c = 3);
386386

387-
drop table hp;
388-
389387
--
390388
-- Test runtime partition pruning
391389
--
@@ -436,6 +434,25 @@ select a from ab where b between $1 and $2 and a < (select 3);
436434

437435
explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2);
438436

437+
--
438+
-- Test runtime pruning with hash partitioned tables
439+
--
440+
441+
-- recreate partitions dropped above
442+
create table hp1 partition of hp for values with (modulus 4, remainder 1);
443+
create table hp2 partition of hp for values with (modulus 4, remainder 2);
444+
create table hp3 partition of hp for values with (modulus 4, remainder 3);
445+
446+
-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual
447+
prepare hp_q1 (text) as
448+
select * from hp where a is null and b = $1;
449+
450+
explain (costs off) execute hp_q1('xxx');
451+
452+
deallocate hp_q1;
453+
454+
drop table hp;
455+
439456
-- Test a backwards Append scan
440457
create table list_part (a int) partition by list (a);
441458
create table list_part1 partition of list_part for values in (1);

0 commit comments

Comments
 (0)