List:Commits« Previous MessageNext Message »
From:Sergey Petrunia Date:April 6 2006 5:23pm
Subject:bk commit into 5.1 tree (sergefp:1.2281) BUG#18558
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of psergey. When psergey does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.2281 06/04/06 21:23:33 sergefp@stripped +6 -0
  BUG#18558 "Partition pruning results are incorrect for certain class of WHERE clauses" : 
  * Produce right results for conditions that were transformed to "(partitioning_range) AND
    (list_of_subpartitioning_ranges)": make each partition id set iterator auto-reset itself
    after it has returned all partition ids in the sequence 
  * Fix "Range mapping" and "Range mapping" partitioning interval analysis functions to 
    correctly deal with NULL values. 

  sql/sql_partition.h
    1.7 06/04/06 21:23:25 sergefp@stripped +17 -1
    BUG#18558: 
     - Make each partition set iterator auto-reset itself after it has returned all 
       partition ids in the set it enumerates. 
     - Rename PARTITION_ITERATOR::has_null_value to ret_null_part

  sql/sql_partition.cc
    1.64 06/04/06 21:23:25 sergefp@stripped +67 -42
    BUG#18558: 
    - Make each partition set iterator auto-reset itself after it has returned all 
      partition ids in the set it enumerates. 
    - Fix partition interval analysis to correctly handle intervals with one or both
      NULL bounds. 

  sql/partition_info.h
    1.7 06/04/06 21:23:25 sergefp@stripped +2 -2
    BUG#18558: Make each partition set iterator auto-reset itself after it has returned all 
    partition ids in the set it enumerates. 

  sql/opt_range.cc
    1.210 06/04/06 21:23:25 sergefp@stripped +1 -3
    BUG#18558: Move partition set iterator initialization to sql_partition.cc, comment fixes

  mysql-test/t/partition_pruning.test
    1.11 06/04/06 21:23:25 sergefp@stripped +59 -0
    Testcase for BUG#18558

  mysql-test/r/partition_pruning.result
    1.12 06/04/06 21:23:25 sergefp@stripped +62 -0
    Testcase for BUG#18558

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	sergefp
# Host:	newbox.mylan
# Root:	/home/psergey/mysql-5.1-bug18558-pnd

--- 1.209/sql/opt_range.cc	2006-03-31 15:19:57 +04:00
+++ 1.210/sql/opt_range.cc	2006-04-06 21:23:25 +04:00
@@ -2296,8 +2296,6 @@
   RANGE_OPT_PARAM  *range_par= &prune_param.range_param;
 
   prune_param.part_info= part_info;
-  prune_param.part_iter.has_null_value= FALSE;
-
   init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
   range_par->mem_root= &alloc;
   range_par->old_root= thd->mem_root;
@@ -2730,7 +2728,7 @@
                                       key_tree->min_flag | key_tree->max_flag,
                                       &ppar->part_iter);
       if (!res)
-        goto go_right; /* res=0 --> no satisfying partitions */
+        goto go_right; /* res==0 --> no satisfying partitions */
       if (res == -1)
       {
         //get a full range iterator

--- 1.63/sql/sql_partition.cc	2006-04-04 00:52:11 +04:00
+++ 1.64/sql/sql_partition.cc	2006-04-06 21:23:25 +04:00
@@ -5269,7 +5269,7 @@
   }
    
   /*
-    Check get_part_iter_for_interval_via_walking() can be used for
+    Check if get_part_iter_for_interval_via_walking() can be used for
     partitioning
   */
   if (part_info->no_part_fields == 1)
@@ -5291,7 +5291,7 @@
 
 setup_subparts:
   /*
-    Check get_part_iter_for_interval_via_walking() can be used for
+    Check if get_part_iter_for_interval_via_walking() can be used for
     subpartitioning
   */
   if (part_info->no_subpart_fields == 1)
@@ -5374,40 +5374,47 @@
     max_endpoint_val=    part_info->no_list_values;
     part_iter->get_next= get_next_partition_id_list;
     part_iter->part_info= part_info;
+    part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
   }
   else
     DBUG_ASSERT(0);
 
-  if (field->real_maybe_null() && part_info->has_null_value)
+  /* 
+    Find minimum: Do special handling if the interval has left bound in form
+     " NULL <= X ":
+  */
+  if (field->real_maybe_null() && part_info->has_null_value && 
+      !(flags & (NO_MIN_RANGE | NEAR_MIN)) && *min_value)
   {
-    if (*min_value)
+    part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
+    part_iter->part_nums.start= part_iter->part_nums.cur= 0;
+    if (*max_value && !(flags & NO_MAX_RANGE))
     {
-      if (*max_value && !(flags & (NO_MIN_RANGE | NO_MAX_RANGE)))
-      {
-        init_single_partition_iterator(part_info->has_null_part_id, part_iter);
-        return 1;
-      }
-      if (!(flags & NEAR_MIN))
-        part_iter->has_null_value= TRUE;
+      /* The right bound is X <= NULL, i.e. it is a "X IS NULL" interval */
+      part_iter->part_nums.end= 0;
+      return 1;
     }
   }
-  /* Find minimum */
-  if (flags & NO_MIN_RANGE)
-    part_iter->part_nums.start= 0;
   else
   {
-    /*
-      Store the interval edge in the record buffer, and call the
-      function that maps the edge in table-field space to an edge
-      in ordered-set-of-partitions (for RANGE partitioning) or 
-      index-in-ordered-array-of-list-constants (for LIST) space.
-    */
-    store_key_image_to_rec(field, min_value, field_len);
-    bool include_endp= part_info->range_analysis_include_bounds ||
-                       !test(flags & NEAR_MIN);
-    part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp);
-    if (part_iter->part_nums.start == max_endpoint_val)
-      return 0; /* No partitions */
+    if (flags & NO_MIN_RANGE)
+      part_iter->part_nums.start= part_iter->part_nums.cur= 0;
+    else
+    {
+      /*
+        Store the interval edge in the record buffer, and call the
+        function that maps the edge in table-field space to an edge
+        in ordered-set-of-partitions (for RANGE partitioning) or 
+        index-in-ordered-array-of-list-constants (for LIST) space.
+      */
+      store_key_image_to_rec(field, min_value, field_len);
+      bool include_endp= part_info->range_analysis_include_bounds ||
+                         !test(flags & NEAR_MIN);
+      part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp);
+      part_iter->part_nums.cur= part_iter->part_nums.start;
+      if (part_iter->part_nums.start == max_endpoint_val)
+        return 0; /* No partitions */
+    }
   }
 
   /* Find maximum, do the same as above but for right interval bound */
@@ -5419,7 +5426,8 @@
     bool include_endp= part_info->range_analysis_include_bounds ||
                        !test(flags & NEAR_MAX);
     part_iter->part_nums.end= get_endpoint(part_info, 0, include_endp);
-    if (part_iter->part_nums.start== part_iter->part_nums.end)
+    if (part_iter->part_nums.start == part_iter->part_nums.end &&
+        !part_iter->ret_null_part)
       return 0; /* No partitions */
   }
   return 1; /* Ok, iterator initialized */
@@ -5534,8 +5542,13 @@
     return 0; /* No partitions match */
   }
 
-  if (flags & (NO_MIN_RANGE | NO_MAX_RANGE))
+  if ((field->real_maybe_null() && 
+       ((!(flags & NO_MIN_RANGE) && *min_value) ||  // NULL <? X
+        (!(flags & NO_MAX_RANGE) && *max_value))) ||  // X <? NULL
+      (flags & (NO_MIN_RANGE | NO_MAX_RANGE)))    // -inf at any bound
+  {
     return -1; /* Can't handle this interval, have to use all partitions */
+  }
   
   /* Get integers for left and right interval bound */
   longlong a, b;
@@ -5553,7 +5566,7 @@
   if (n_values > total_parts || n_values > MAX_RANGE_TO_WALK)
     return -1;
 
-  part_iter->field_vals.start= a;
+  part_iter->field_vals.start= part_iter->field_vals.cur= a;
   part_iter->field_vals.end=   b;
   part_iter->part_info= part_info;
   part_iter->get_next=  get_next_func;
@@ -5565,12 +5578,13 @@
   PARTITION_ITERATOR::get_next implementation: enumerate partitions in range
 
   SYNOPSIS
-    get_next_partition_id_list()
+    get_next_partition_id_range()
       part_iter  Partition set iterator structure
 
   DESCRIPTION
     This is implementation of PARTITION_ITERATOR::get_next() that returns
     [sub]partition ids in [min_partition_id, max_partition_id] range.
+    The function conforms to partition_iter_func type.
 
   RETURN
     partition id
@@ -5579,10 +5593,13 @@
 
 uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter)
 {
-  if (part_iter->part_nums.start== part_iter->part_nums.end)
+  if (part_iter->part_nums.cur == part_iter->part_nums.end)
+  {
+    part_iter->part_nums.cur= part_iter->part_nums.start;
     return NOT_A_PARTITION_ID;
+  }
   else
-    return part_iter->part_nums.start++;
+    return part_iter->part_nums.cur++;
 }
 
 
@@ -5597,6 +5614,7 @@
     This implementation of PARTITION_ITERATOR::get_next() is special for 
     LIST partitioning: it enumerates partition ids in 
     part_info->list_array[i] where i runs over [min_idx, max_idx] interval.
+    The function conforms to partition_iter_func type.
 
   RETURN 
     partition id
@@ -5605,18 +5623,20 @@
 
 uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter)
 {
-  if (part_iter->part_nums.start == part_iter->part_nums.end)
+  if (part_iter->part_nums.cur == part_iter->part_nums.end)
   {
-    if (part_iter->has_null_value)
+    if (part_iter->ret_null_part)
     {
-      part_iter->has_null_value= FALSE;
+      part_iter->ret_null_part= FALSE;
       return part_iter->part_info->has_null_part_id;
     }
+    part_iter->part_nums.cur= part_iter->part_nums.start;
+    part_iter->ret_null_part= part_iter->ret_null_part_orig;
     return NOT_A_PARTITION_ID;
   }
   else
     return part_iter->part_info->list_array[part_iter->
-                                            part_nums.start++].partition_id;
+                                            part_nums.cur++].partition_id;
 }
 
 
@@ -5631,6 +5651,7 @@
     This implementation of PARTITION_ITERATOR::get_next() returns ids of
     partitions that contain records with partitioning field value within
     [start_val, end_val] interval.
+    The function conforms to partition_iter_func type.
 
   RETURN 
     partition id
@@ -5641,11 +5662,10 @@
 {
   uint32 part_id;
   Field *field= part_iter->part_info->part_field_array[0];
-  while (part_iter->field_vals.start != part_iter->field_vals.end)
+  while (part_iter->field_vals.cur != part_iter->field_vals.end)
   {
-    field->store(part_iter->field_vals.start, FALSE);
-    part_iter->field_vals.start++;
     longlong dummy;
+    field->store(part_iter->field_vals.cur++, FALSE);
     if (part_iter->part_info->is_sub_partitioned() &&
         !part_iter->part_info->get_part_partition_id(part_iter->part_info,
                                                      &part_id, &dummy) ||
@@ -5653,6 +5673,9 @@
                                                 &part_id, &dummy))
       return part_id;
   }
+  //psergey-todo: return partition(part_func(NULL)) here...
+  
+  part_iter->field_vals.cur= part_iter->field_vals.start;
   return NOT_A_PARTITION_ID;
 }
 
@@ -5663,10 +5686,12 @@
 {
   uint32 part_id;
   Field *field= part_iter->part_info->subpart_field_array[0];
-  if (part_iter->field_vals.start == part_iter->field_vals.end)
+  if (part_iter->field_vals.cur == part_iter->field_vals.end)
+  {
+    part_iter->field_vals.cur= part_iter->field_vals.start;
     return NOT_A_PARTITION_ID;
-  field->store(part_iter->field_vals.start, FALSE);
-  part_iter->field_vals.start++;
+  }
+  field->store(part_iter->field_vals.cur++, FALSE);
   return part_iter->part_info->get_subpartition_id(part_iter->part_info);
 }
 #endif

--- 1.6/sql/partition_info.h	2006-03-31 21:39:35 +04:00
+++ 1.7/sql/partition_info.h	2006-04-06 21:23:25 +04:00
@@ -267,7 +267,7 @@
 static inline void init_single_partition_iterator(uint32 part_id,
                                            PARTITION_ITERATOR *part_iter)
 {
-  part_iter->part_nums.start= part_id;
+  part_iter->part_nums.start= part_iter->part_nums.cur= part_id;
   part_iter->part_nums.end= part_id+1;
   part_iter->get_next= get_next_partition_id_range;
 }
@@ -277,7 +277,7 @@
 void init_all_partitions_iterator(partition_info *part_info,
                                   PARTITION_ITERATOR *part_iter)
 {
-  part_iter->part_nums.start= 0;
+  part_iter->part_nums.start= part_iter->part_nums.cur= 0;
   part_iter->part_nums.end= part_info->no_parts;
   part_iter->get_next= get_next_partition_id_range;
 }

--- 1.6/sql/sql_partition.h	2006-03-28 16:25:13 +04:00
+++ 1.7/sql/sql_partition.h	2006-04-06 21:23:25 +04:00
@@ -94,10 +94,20 @@
 
 /*
   A "Get next" function for partition iterator.
+
   SYNOPSIS
     partition_iter_func()
       part_iter  Partition iterator, you call only "iter.get_next(&iter)"
 
+  DESCRIPTION
+    Depending on whether partitions or sub-partitions are iterated, the
+    function returns next subpartition id/partition number. The sequence of
+    returned numbers is not ordered and may contain duplicates.
+
+    When the end of sequence is reached, NOT_A_PARTITION_ID is returned, and 
+    the iterator resets itself (so next get_next() call will start to 
+    enumerate the set all over again).
+
   RETURN 
     NOT_A_PARTITION_ID if there are no more partitions.
     [sub]partition_id  of the next partition
@@ -124,16 +134,22 @@
 typedef struct st_partition_iter
 {
   partition_iter_func get_next;
-  bool has_null_value;
+  /* 
+    Valid for "Interval mapping" in LIST partitioning: if true, let the
+    iterator also produce id of the partition that contains NULL value.
+  */
+  bool ret_null_part, ret_null_part_orig;
   struct st_part_num_range
   {
     uint32 start;
+    uint32 cur;
     uint32 end;
   };
 
   struct st_field_value_range
   {
     longlong start;
+    longlong cur;
     longlong end;
   };
 

--- 1.11/mysql-test/r/partition_pruning.result	2006-04-04 00:52:11 +04:00
+++ 1.12/mysql-test/r/partition_pruning.result	2006-04-06 21:23:25 +04:00
@@ -597,3 +597,65 @@
 explain partitions select * from t1 where f_int1 is null;
 id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	part4_p2sp0	system	NULL	NULL	NULL	NULL	1	
+drop table t1;
+create table t1 (a int not null, b int not null)
+partition by list(a) 
+subpartition by hash(b) subpartitions 4 
+(
+partition p0 values in (1),
+partition p1 values in (2),
+partition p2 values in (3)
+);
+insert into t1 values (1,1),(1,2),(1,3),(1,4),
+(2,1),(2,2),(2,3),(2,4);
+explain partitions select * from t1 where a=1 AND (b=1 OR b=2);
+id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	p0_p0sp1,p0_p0sp2	ALL	NULL	NULL	NULL	NULL	2	Using where
+drop table t1;
+create table t1 (a int, b int not null)
+partition by list(a) 
+subpartition by hash(b) subpartitions 2
+(
+partition p0 values in (1),
+partition p1 values in (2),
+partition p2 values in (3),
+partition pn values in (NULL)
+);
+insert into t1 values (1,1),(1,2),(1,3),(1,4),
+(2,1),(2,2),(2,3),(2,4), (NULL,1);
+explain partitions select * from t1 where a IS NULL AND (b=1 OR b=2);
+id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	pn_p3sp0,pn_p3sp1	system	NULL	NULL	NULL	NULL	1	
+explain partitions select * from t1 where (a IS NULL or a < 1) AND (b=1 OR b=2);
+id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	pn_p3sp0,pn_p3sp1	system	NULL	NULL	NULL	NULL	1	
+explain partitions select * from t1 where (a IS NULL or a < 2) AND (b=1 OR b=2);
+id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	p0_p0sp0,p0_p0sp1,pn_p3sp0,pn_p3sp1	ALL	NULL	NULL	NULL	NULL	5	Using where
+explain partitions select * from t1 where (a IS NULL or a <= 1) AND (b=1 OR b=2);
+id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	p0_p0sp0,p0_p0sp1,pn_p3sp0,pn_p3sp1	ALL	NULL	NULL	NULL	NULL	5	Using where
+drop table t1;
+create table t1 ( a int)  partition by list (MOD(a, 10))
+( partition p0  values in (0), partition p1 values in (1),
+partition p2 values in (2), partition p3 values in (3),
+partition p4 values in (4), partition p5 values in (5),
+partition p6 values in (6), partition pn values in (NULL)
+);
+insert into t1 values (NULL), (0),(1),(2),(3),(4),(5),(6);
+explain partitions select * from t1 where a is null or a < 2;
+id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	p0,p1,p2,p3,p4,p5,p6,pn	ALL	NULL	NULL	NULL	NULL	8	Using where
+drop table t1;
+create table t1 (s1 int) partition by list (s1) 
+(partition p1 values in (0), 
+partition p2 values in (1), 
+partition p3 values in (null));
+insert into t1 values (0),(1),(null);
+select count(*) from t1 where s1 < 0 or s1 is null;
+count(*)
+1
+explain partitions select count(*) from t1 where s1 < 0 or s1 is null;
+id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	p3	system	NULL	NULL	NULL	NULL	1	
+drop table t1;

--- 1.10/mysql-test/t/partition_pruning.test	2006-04-04 00:52:11 +04:00
+++ 1.11/mysql-test/t/partition_pruning.test	2006-04-06 21:23:25 +04:00
@@ -492,6 +492,65 @@
 
 select * from t1 where f_int1 is null;
 explain partitions select * from t1 where f_int1 is null;
+drop table t1;
+
+#
+# BUG#18558
+#
+create table t1 (a int not null, b int not null)
+partition by list(a) 
+  subpartition by hash(b) subpartitions 4 
+(
+  partition p0 values in (1),
+  partition p1 values in (2),
+  partition p2 values in (3)
+);
+insert into t1 values (1,1),(1,2),(1,3),(1,4),
+  (2,1),(2,2),(2,3),(2,4);
+explain partitions select * from t1 where a=1 AND (b=1 OR b=2);
+drop table t1;
+
+create table t1 (a int, b int not null)
+partition by list(a) 
+  subpartition by hash(b) subpartitions 2
+(
+  partition p0 values in (1),
+  partition p1 values in (2),
+  partition p2 values in (3),
+  partition pn values in (NULL)
+);
+insert into t1 values (1,1),(1,2),(1,3),(1,4),
+  (2,1),(2,2),(2,3),(2,4), (NULL,1);
+
+explain partitions select * from t1 where a IS NULL AND (b=1 OR b=2);
+
+explain partitions select * from t1 where (a IS NULL or a < 1) AND (b=1 OR b=2);
+explain partitions select * from t1 where (a IS NULL or a < 2) AND (b=1 OR b=2);
+explain partitions select * from t1 where (a IS NULL or a <= 1) AND (b=1 OR b=2);
+
+drop table t1;
+
+create table t1 ( a int)  partition by list (MOD(a, 10))
+( partition p0  values in (0), partition p1 values in (1),
+   partition p2 values in (2), partition p3 values in (3),
+   partition p4 values in (4), partition p5 values in (5),
+   partition p6 values in (6), partition pn values in (NULL)
+);
+insert into t1 values (NULL), (0),(1),(2),(3),(4),(5),(6);
+explain partitions select * from t1 where a is null or a < 2;
+drop table t1;
+
+# Testcase from BUG#18751
+create table t1 (s1 int) partition by list (s1) 
+  (partition p1 values in (0), 
+   partition p2 values in (1), 
+   partition p3 values in (null));
+ 
+insert into t1 values (0),(1),(null);
+ 
+select count(*) from t1 where s1 < 0 or s1 is null;
+explain partitions select count(*) from t1 where s1 < 0 or s1 is null;
+drop table t1;
 
 # No tests for NULLs in RANGE(monotonic_expr()) - they depend on BUG#15447
 # being fixed.
Thread
bk commit into 5.1 tree (sergefp:1.2281) BUG#18558Sergey Petrunia6 Apr