aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Rowley <drowley@postgresql.org>2023-10-13 01:14:22 +1300
committerDavid Rowley <drowley@postgresql.org>2023-10-13 01:14:22 +1300
commit890a73ba3ac0ea9ec2ad6a828ffe03ca12b11564 (patch)
tree12d4b7b30bc96f58b964b608b1a270c95007134b
parentd6370a39da322fdfa4ad2f487b63e06cbdca07df (diff)
downloadpostgresql-890a73ba3ac0ea9ec2ad6a828ffe03ca12b11564.tar.gz
postgresql-890a73ba3ac0ea9ec2ad6a828ffe03ca12b11564.zip
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.
-rw-r--r--src/backend/executor/execPartition.c29
-rw-r--r--src/test/regress/expected/partition_prune.out22
-rw-r--r--src/test/regress/sql/partition_prune.sql21
3 files changed, 57 insertions, 15 deletions
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index e3e6676a0e3..84a07065ce3 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -1878,7 +1878,7 @@ ExecInitPruningContext(PartitionPruneContext *context,
foreach(lc, pruning_steps)
{
PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc);
- ListCell *lc2;
+ ListCell *lc2 = list_head(step->exprs);
int keyno;
/* not needed for other step kinds */
@@ -1887,22 +1887,27 @@ ExecInitPruningContext(PartitionPruneContext *context,
Assert(list_length(step->exprs) <= partnatts);
- keyno = 0;
- foreach(lc2, step->exprs)
+ for (keyno = 0; keyno < partnatts; keyno++)
{
- Expr *expr = (Expr *) lfirst(lc2);
+ if (bms_is_member(keyno, step->nullkeys))
+ continue;
- /* not needed for Consts */
- if (!IsA(expr, Const))
+ if (lc2 != NULL)
{
- int stateidx = PruneCxtStateIdx(partnatts,
- step->step.step_id,
- keyno);
+ Expr *expr = lfirst(lc2);
- context->exprstates[stateidx] =
- ExecInitExpr(expr, context->planstate);
+ /* not needed for Consts */
+ if (!IsA(expr, Const))
+ {
+ int stateidx = PruneCxtStateIdx(partnatts,
+ step->step.step_id,
+ keyno);
+
+ context->exprstates[stateidx] =
+ ExecInitExpr(expr, context->planstate);
+ }
+ lc2 = lnext(step->exprs, lc2);
}
- keyno++;
}
}
}
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index dd05b9b74f2..ffd141c67e6 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -1948,7 +1948,6 @@ explain (costs off) select * from hp where a = 1 and b = 'abcde' and
One-Time Filter: false
(2 rows)
-drop table hp;
--
-- Test runtime partition pruning
--
@@ -2070,6 +2069,27 @@ explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2);
Filter: ((b >= $1) AND (b <= $2) AND (a < $0))
(10 rows)
+--
+-- Test runtime pruning with hash partitioned tables
+--
+-- recreate partitions dropped above
+create table hp1 partition of hp for values with (modulus 4, remainder 1);
+create table hp2 partition of hp for values with (modulus 4, remainder 2);
+create table hp3 partition of hp for values with (modulus 4, remainder 3);
+-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual
+prepare hp_q1 (text) as
+select * from hp where a is null and b = $1;
+explain (costs off) execute hp_q1('xxx');
+ QUERY PLAN
+--------------------------------------------
+ Append
+ Subplans Removed: 3
+ -> Seq Scan on hp2 hp_1
+ Filter: ((a IS NULL) AND (b = $1))
+(4 rows)
+
+deallocate hp_q1;
+drop table hp;
-- Test a backwards Append scan
create table list_part (a int) partition by list (a);
create table list_part1 partition of list_part for values in (1);
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index 95ab6bbeba6..27747dcd128 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -384,8 +384,6 @@ drop table hp2;
explain (costs off) select * from hp where a = 1 and b = 'abcde' and
(c = 2 or c = 3);
-drop table hp;
-
--
-- Test runtime partition pruning
--
@@ -436,6 +434,25 @@ select a from ab where b between $1 and $2 and a < (select 3);
explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2);
+--
+-- Test runtime pruning with hash partitioned tables
+--
+
+-- recreate partitions dropped above
+create table hp1 partition of hp for values with (modulus 4, remainder 1);
+create table hp2 partition of hp for values with (modulus 4, remainder 2);
+create table hp3 partition of hp for values with (modulus 4, remainder 3);
+
+-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual
+prepare hp_q1 (text) as
+select * from hp where a is null and b = $1;
+
+explain (costs off) execute hp_q1('xxx');
+
+deallocate hp_q1;
+
+drop table hp;
+
-- Test a backwards Append scan
create table list_part (a int) partition by list (a);
create table list_part1 partition of list_part for values in (1);