Hi Thibaut,

On 2019/03/19 23:58, Thibaut Madelaine wrote:
> I kept on testing with sub-partitioning.
Thanks.

> I found a case, using 2 default partitions, where a default partition is
> not pruned:
> 
> --------------
> 
> create table test2(id int, val text) partition by range (id);
> create table test2_20_plus_def partition of test2 default;
> create table test2_0_20 partition of test2 for values from (0) to (20)
>   partition by range (id);
> create table test2_0_10 partition of test2_0_20 for values from (0) to (10);
> create table test2_10_20_def partition of test2_0_20 default;
> 
> # explain (costs off) select * from test2 where id=5 or id=25;
>                QUERY PLAN               
> -----------------------------------------
>  Append
>    ->  Seq Scan on test2_0_10
>          Filter: ((id = 5) OR (id = 25))
>    ->  Seq Scan on test2_10_20_def
>          Filter: ((id = 5) OR (id = 25))
>    ->  Seq Scan on test2_20_plus_def
>          Filter: ((id = 5) OR (id = 25))
> (7 rows)
> 
> --------------
> 
> I have the same output using Amit's v1-delta.patch or Hosoya's
> v2_default_partition_pruning.patch.

I think I've figured what may be wrong.

Partition pruning step generation code should ignore any arguments of an
OR clause that won't be true for a sub-partitioned partition, given its
partition constraint.

In this case, id = 25 contradicts test2_0_20's partition constraint (which
is, a IS NOT NULL AND a >= 0 AND a < 20), so the OR clause should really
be simplified to id = 5, ignoring the id = 25 argument.  Note that we
remove id = 25 only for the considerations of pruning and not from the
actual clause that's passed to the final plan, although it wouldn't be a
bad idea to try to do that.

Attached revised delta patch, which includes the fix described above.

Thanks,
Amit
diff --git a/src/backend/partitioning/partprune.c 
b/src/backend/partitioning/partprune.c
index 8317318156..aa32a50477 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -795,6 +795,27 @@ gen_partprune_steps_internal(GeneratePruningStepsContext 
*context,
                                        Expr       *arg = lfirst(lc1);
                                        bool            arg_contradictory;
                                        List       *argsteps;
+                                       List       *partconstr = 
rel->partition_qual;
+
+                                       /*
+                                        * If this OR argument can be proved 
false for this
+                                        * partition, given its partition 
constraint, we can
+                                        * ignore it, that is not try to pass 
it to the pruning
+                                        * code.  We should do that especially 
to avoid pruning
+                                        * code wrongly failing to prune the 
default partition.
+                                        */
+                                       if (partconstr)
+                                       {
+                                               partconstr = (List *)
+                                                       
expression_planner((Expr *) partconstr);
+                                               if (rel->relid != 1)
+                                                       ChangeVarNodes((Node *) 
partconstr, 1,
+                                                                               
   rel->relid, 0);
+                                               if 
(predicate_refuted_by(partconstr,
+                                                                               
                 list_make1(arg),
+                                                                               
                 false))
+                                                       continue;
+                                       }
 
                                        argsteps =
                                                
gen_partprune_steps_internal(context, rel,
@@ -822,32 +843,13 @@ gen_partprune_steps_internal(GeneratePruningStepsContext 
*context,
                                                 * an arg.  To indicate that to 
the pruning code, we
                                                 * must construct a dummy 
PartitionPruneStepCombine
                                                 * whose source_stepids is set 
to an empty List.
-                                                * However, if we can prove 
using constraint exclusion
-                                                * that the clause refutes the 
table's partition
-                                                * constraint (if it's 
sub-partitioned), we need not
-                                                * bother with that.  That is, 
we effectively ignore
-                                                * this OR arm.
                                                 */
-                                               List       *partconstr = 
rel->partition_qual;
                                                PartitionPruneStep *orstep;
 
                                                /* Just ignore this argument. */
                                                if (arg_contradictory)
                                                        continue;
 
-                                               if (partconstr)
-                                               {
-                                                       partconstr = (List *)
-                                                               
expression_planner((Expr *) partconstr);
-                                                       if (rel->relid != 1)
-                                                               
ChangeVarNodes((Node *) partconstr, 1,
-                                                                               
           rel->relid, 0);
-                                                       if 
(predicate_refuted_by(partconstr,
-                                                                               
                         list_make1(arg),
-                                                                               
                         false))
-                                                               continue;
-                                               }
-
                                                orstep = 
gen_prune_step_combine(context, NIL,
                                                                                
                                PARTPRUNE_COMBINE_UNION);
                                                arg_stepids = 
lappend_int(arg_stepids, orstep->step_id);
@@ -2759,15 +2761,19 @@ get_matching_range_bounds(PartitionPruneContext 
*context,
                                 * instead as the offset of the upper bound of 
the greatest
                                 * partition that may contain lookup value.  If 
the lookup
                                 * value had exactly matched the bound, but it 
isn't
-                                * inclusive, no need add the adjacent 
partition.  If 'off' is
-                                * -1 indicating that all bounds are greater, 
then we simply
-                                * end up adding the first bound's offset, that 
is, 0.
+                                * inclusive, no need add the adjacent 
partition.
                                 */
-                               else if (off < 0 || !is_equal || inclusive)
+                               else if (!is_equal || inclusive)
                                        maxoff = off + 1;
                                else
                                        maxoff = off;
                        }
+                       else
+                               /*
+                                * 'off' is -1 indicating that all bounds are 
greater, so just
+                                * set the first bound's offset as maxoff.
+                                */
+                               maxoff = off + 1;
                        break;
 
                default:

Reply via email to