This is an automated email from the ASF dual-hosted git repository.

tuhaihe pushed a commit to branch REL_2_STABLE
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit 0303870cd10a59aa580e06d4e4f95ebe64ca4589
Author: David Rowley <[email protected]>
AuthorDate: Fri Oct 13 01:13:59 2023 +1300

    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/[email protected]
    Backpatch-through: 11, where runtime partition pruning was added.
    (cherry picked from commit dd80563c5ce76229dcead78aa284ffae5b1d0937)
---
 src/backend/executor/execPartition.c          | 29 ++++++++++++++++-----------
 src/test/regress/expected/partition_prune.out | 22 +++++++++++++++++++-
 src/test/regress/sql/partition_prune.sql      | 21 +++++++++++++++++--
 3 files changed, 57 insertions(+), 15 deletions(-)

diff --git a/src/backend/executor/execPartition.c 
b/src/backend/executor/execPartition.c
index 629eca05483..fcb51b2fbbd 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -2008,7 +2008,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 */
@@ -2017,22 +2017,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);
+
+                               /* not needed for Consts */
+                               if (!IsA(expr, Const))
+                               {
+                                       int                     stateidx = 
PruneCxtStateIdx(partnatts,
+                                                                               
                                        step->step.step_id,
+                                                                               
                                        keyno);
 
-                               context->exprstates[stateidx] =
-                                       ExecInitExpr(expr, context->planstate);
+                                       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 566779d21a1..b3e82a7b229 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2034,7 +2034,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
 --
@@ -2168,6 +2167,27 @@ explain (analyze, costs off, summary off, timing off) 
execute ab_q3 (2, 2);
  Optimizer: Postgres query optimizer
 (12 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 0dca83079e4..cf87264b69b 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -375,8 +375,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
 --
@@ -427,6 +425,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);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to