Commit 328f8fa7 authored by sergefp@mysql.com's avatar sergefp@mysql.com

WL#2985 "Partition pruning", postreview fixes: Small code fixes and better comments

parent 530cddb6
...@@ -148,3 +148,10 @@ t1 CREATE TABLE `t1` ( ...@@ -148,3 +148,10 @@ t1 CREATE TABLE `t1` (
`b` int(11) default NULL `b` int(11) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) (PARTITION x1 VALUES LESS THAN (6) ENGINE = MyISAM, PARTITION x3 VALUES LESS THAN (8) ENGINE = MyISAM, PARTITION x4 VALUES LESS THAN (10) ENGINE = MyISAM, PARTITION x5 VALUES LESS THAN (12) ENGINE = MyISAM, PARTITION x6 VALUES LESS THAN (14) ENGINE = MyISAM, PARTITION x7 VALUES LESS THAN (16) ENGINE = MyISAM, PARTITION x8 VALUES LESS THAN (18) ENGINE = MyISAM, PARTITION x9 VALUES LESS THAN (20) ENGINE = MyISAM) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) (PARTITION x1 VALUES LESS THAN (6) ENGINE = MyISAM, PARTITION x3 VALUES LESS THAN (8) ENGINE = MyISAM, PARTITION x4 VALUES LESS THAN (10) ENGINE = MyISAM, PARTITION x5 VALUES LESS THAN (12) ENGINE = MyISAM, PARTITION x6 VALUES LESS THAN (14) ENGINE = MyISAM, PARTITION x7 VALUES LESS THAN (16) ENGINE = MyISAM, PARTITION x8 VALUES LESS THAN (18) ENGINE = MyISAM, PARTITION x9 VALUES LESS THAN (20) ENGINE = MyISAM)
drop table t1; drop table t1;
create table t1 (a int not null, b int not null) partition by LIST (a+b) (
partition p0 values in (12),
partition p1 values in (14)
);
insert into t1 values (10,1);
ERROR HY000: Table has no partition for value 11
drop table t1;
...@@ -203,3 +203,14 @@ ALTER TABLE t1 REORGANISE PARTITION x0,x1,x2 INTO ...@@ -203,3 +203,14 @@ ALTER TABLE t1 REORGANISE PARTITION x0,x1,x2 INTO
(PARTITION x1 VALUES LESS THAN (6)); (PARTITION x1 VALUES LESS THAN (6));
show create table t1; show create table t1;
drop table t1; drop table t1;
# Testcase for BUG#15819
create table t1 (a int not null, b int not null) partition by LIST (a+b) (
partition p0 values in (12),
partition p1 values in (14)
);
--error 1500
insert into t1 values (10,1);
drop table t1;
...@@ -381,13 +381,20 @@ class Name_resolution_context_state ...@@ -381,13 +381,20 @@ class Name_resolution_context_state
put values of field_i into table record buffer; put values of field_i into table record buffer;
return item->val_int(); return item->val_int();
} }
NOTE
At the moment function monotonicity is not well defined (and so may be
incorrect) for Item trees with parameters/return types that are different
from INT_RESULT, may be NULL, or are unsigned.
It will be possible to address this issue once the related partitioning bugs
(BUG#16002, BUG#15447, BUG#13436) are fixed.
*/ */
typedef enum monotonicity_info typedef enum monotonicity_info
{ {
NON_MONOTONIC, /* none of the below holds */ NON_MONOTONIC, /* none of the below holds */
MONOTONIC_INCREASING, /* F() is unary and "x < y" => "F(x) < F(y)" */ MONOTONIC_INCREASING, /* F() is unary and (x < y) => (F(x) <= F(y)) */
MONOTONIC_STRICT_INCREASING /* F() is unary and "x < y" => "F(x) <= F(y)" */ MONOTONIC_STRICT_INCREASING /* F() is unary and (x < y) => (F(x) < F(y)) */
} enum_monotonicity_info; } enum_monotonicity_info;
/*************************************************************************/ /*************************************************************************/
......
...@@ -885,6 +885,21 @@ longlong Item_func_to_days::val_int() ...@@ -885,6 +885,21 @@ longlong Item_func_to_days::val_int()
return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day); return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day);
} }
/*
Get information about this Item tree monotonicity
SYNOPSIS
Item_func_to_days::get_monotonicity_info()
DESCRIPTION
Get information about monotonicity of the function represented by this item
tree.
RETURN
See enum_monotonicity_info.
*/
enum_monotonicity_info Item_func_to_days::get_monotonicity_info() const enum_monotonicity_info Item_func_to_days::get_monotonicity_info() const
{ {
if (args[0]->type() == Item::FIELD_ITEM) if (args[0]->type() == Item::FIELD_ITEM)
...@@ -1080,6 +1095,21 @@ longlong Item_func_year::val_int() ...@@ -1080,6 +1095,21 @@ longlong Item_func_year::val_int()
return (longlong) ltime.year; return (longlong) ltime.year;
} }
/*
Get information about this Item tree monotonicity
SYNOPSIS
Item_func_to_days::get_monotonicity_info()
DESCRIPTION
Get information about monotonicity of the function represented by this item
tree.
RETURN
See enum_monotonicity_info.
*/
enum_monotonicity_info Item_func_year::get_monotonicity_info() const enum_monotonicity_info Item_func_year::get_monotonicity_info() const
{ {
if (args[0]->type() == Item::FIELD_ITEM && if (args[0]->type() == Item::FIELD_ITEM &&
......
...@@ -313,11 +313,46 @@ class SEL_ARG :public Sql_alloc ...@@ -313,11 +313,46 @@ class SEL_ARG :public Sql_alloc
} }
SEL_ARG *clone_tree(); SEL_ARG *clone_tree();
/* Return TRUE if this represents "keypartK = const" or "keypartK IS NULL" */
/*
Check if this SEL_ARG object represents a single-point interval
SYNOPSIS
is_singlepoint()
DESCRIPTION
Check if this SEL_ARG object (not tree) represents a single-point
interval, i.e. if it represents a "keypart = const" or
"keypart IS NULL".
RETURN
TRUE This SEL_ARG object represents a singlepoint interval
FALSE Otherwise
*/
bool is_singlepoint() bool is_singlepoint()
{ {
return !min_flag && !max_flag && /*
!field->key_cmp((byte*) min_value, (byte*)max_value); Check for NEAR_MIN ("strictly less") and NO_MIN_RANGE (-inf < field)
flags, and the same for right edge.
*/
if (min_flag || max_flag)
return FALSE;
byte *min_val= min_value;
byte *max_val= min_value;
if (maybe_null)
{
/* First byte is a NULL value indicator */
if (*min_val != *max_val)
return FALSE;
if (*min_val)
return TRUE; /* This "x IS NULL" */
min_val++;
max_val++;
}
return !field->key_cmp(min_val, max_val);
} }
}; };
...@@ -2110,7 +2145,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, ...@@ -2110,7 +2145,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
Putting it all together, partitioning module works as follows: Putting it all together, partitioning module works as follows:
prune_partitions() { prune_partitions() {
call create_partition_index_descrition(); call create_partition_index_description();
call get_mm_tree(); // invoke the RangeAnalysisModule call get_mm_tree(); // invoke the RangeAnalysisModule
...@@ -2229,7 +2264,7 @@ typedef struct st_part_prune_param ...@@ -2229,7 +2264,7 @@ typedef struct st_part_prune_param
part_num_to_partition_id_func part_num_to_part_id; part_num_to_partition_id_func part_num_to_part_id;
} PART_PRUNE_PARAM; } PART_PRUNE_PARAM;
static bool create_partition_index_descrition(PART_PRUNE_PARAM *prune_par); static bool create_partition_index_description(PART_PRUNE_PARAM *prune_par);
static int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree); static int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree);
static int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, static int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar,
SEL_IMERGE *imerge); SEL_IMERGE *imerge);
...@@ -2243,7 +2278,7 @@ static uint32 part_num_to_part_id_range(PART_PRUNE_PARAM* prune_par, ...@@ -2243,7 +2278,7 @@ static uint32 part_num_to_part_id_range(PART_PRUNE_PARAM* prune_par,
static void print_partitioning_index(KEY_PART *parts, KEY_PART *parts_end); static void print_partitioning_index(KEY_PART *parts, KEY_PART *parts_end);
static void dbug_print_field(Field *field); static void dbug_print_field(Field *field);
static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part); static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part);
static void dbug_print_onepoint_range(SEL_ARG **start, uint num); static void dbug_print_singlepoint_range(SEL_ARG **start, uint num);
#endif #endif
...@@ -2297,7 +2332,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) ...@@ -2297,7 +2332,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
range_par->mem_root= &alloc; range_par->mem_root= &alloc;
range_par->old_root= thd->mem_root; range_par->old_root= thd->mem_root;
if (create_partition_index_descrition(&prune_param)) if (create_partition_index_description(&prune_param))
{ {
mark_all_partitions_as_used(part_info); mark_all_partitions_as_used(part_info);
free_root(&alloc,MYF(0)); // Return memory & allocator free_root(&alloc,MYF(0)); // Return memory & allocator
...@@ -2338,6 +2373,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) ...@@ -2338,6 +2373,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
if (tree->merges.is_empty()) if (tree->merges.is_empty())
{ {
/* Range analysis has produced a single list of intervals. */
prune_param.arg_stack_end= prune_param.arg_stack; prune_param.arg_stack_end= prune_param.arg_stack;
prune_param.cur_part_fields= 0; prune_param.cur_part_fields= 0;
prune_param.cur_subpart_fields= 0; prune_param.cur_subpart_fields= 0;
...@@ -2352,13 +2388,29 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) ...@@ -2352,13 +2388,29 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
{ {
if (tree->merges.elements == 1) if (tree->merges.elements == 1)
{ {
if (-1 == (res |= find_used_partitions_imerge(&prune_param, /*
Range analysis has produced a "merge" of several intervals lists, a
SEL_TREE that represents an expression in form
sel_imerge = (tree1 OR tree2 OR ... OR treeN)
that cannot be reduced to one tree. This can only happen when
partitioning index has several keyparts and the condition is OR of
conditions that refer to different key parts. For example, we'll get
here for "partitioning_field=const1 OR subpartitioning_field=const2"
*/
if (-1 == (res= find_used_partitions_imerge(&prune_param,
tree->merges.head()))) tree->merges.head())))
goto all_used; goto all_used;
} }
else else
{ {
if (-1 == (res |= find_used_partitions_imerge_list(&prune_param, /*
Range analysis has produced a list of several imerges, i.e. a
structure that represents a condition in form
imerge_list= (sel_imerge1 AND sel_imerge2 AND ... AND sel_imergeN)
This is produced for complicated WHERE clauses that range analyzer
can't really analyze properly.
*/
if (-1 == (res= find_used_partitions_imerge_list(&prune_param,
tree->merges))) tree->merges)))
goto all_used; goto all_used;
} }
...@@ -2384,12 +2436,19 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) ...@@ -2384,12 +2436,19 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
/* /*
Store key image to table record Store field key image to table record
SYNOPSIS SYNOPSIS
field Field which key image should be stored. store_key_image_to_rec()
ptr Field value in key format. field Field which key image should be stored
len Length of the value, in bytes. ptr Field value in key format
len Length of the value, in bytes
DESCRIPTION
Copy the field value from its key image to the table record. The source
is the value in key image format, occupying len bytes in buffer pointed
by ptr. The destination is table record, in "field value in table record"
format.
*/ */
static void store_key_image_to_rec(Field *field, char *ptr, uint len) static void store_key_image_to_rec(Field *field, char *ptr, uint len)
...@@ -2414,8 +2473,12 @@ static void store_key_image_to_rec(Field *field, char *ptr, uint len) ...@@ -2414,8 +2473,12 @@ static void store_key_image_to_rec(Field *field, char *ptr, uint len)
SYNOPSIS SYNOPSIS
store_selargs_to_rec() store_selargs_to_rec()
ppar Partition pruning context ppar Partition pruning context
start Array SEL_ARG* for which the minimum values should be stored start Array of SEL_ARG* for which the minimum values should be stored
num Number of elements in the array num Number of elements in the array
DESCRIPTION
For each SEL_ARG* interval in the specified array, store the left edge
field value (sel_arg->min, key image format) into the table record.
*/ */
static void store_selargs_to_rec(PART_PRUNE_PARAM *ppar, SEL_ARG **start, static void store_selargs_to_rec(PART_PRUNE_PARAM *ppar, SEL_ARG **start,
...@@ -2569,7 +2632,7 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge) ...@@ -2569,7 +2632,7 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge)
DESCRIPTION DESCRIPTION
This function This function
* recursively walks the SEL_ARG* tree, collecting partitioning * recursively walks the SEL_ARG* tree collecting partitioning
"intervals"; "intervals";
* finds the partitions one needs to use to get rows in these intervals; * finds the partitions one needs to use to get rows in these intervals;
* marks these partitions as used. * marks these partitions as used.
...@@ -2578,8 +2641,8 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge) ...@@ -2578,8 +2641,8 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge)
A partition pruning "interval" is equivalent to condition in one of the A partition pruning "interval" is equivalent to condition in one of the
forms: forms:
"partition_field1=const1 AND ... partition_fieldN=constN" (1) "partition_field1=const1 AND ... AND partition_fieldN=constN" (1)
"subpartition_field1=const1 AND ... subpartition_fieldN=constN" (2) "subpartition_field1=const1 AND ... AND subpartition_fieldN=constN" (2)
"(1) AND (2)" (3) "(1) AND (2)" (3)
In (1) and (2) all [sub]partitioning fields must be used, and "x=const" In (1) and (2) all [sub]partitioning fields must be used, and "x=const"
...@@ -2591,7 +2654,7 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge) ...@@ -2591,7 +2654,7 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge)
then the following is also an interval: then the following is also an interval:
" const1 OP1 single_partition_field OR const2" (4) " const1 OP1 single_partition_field OP2 const2" (4)
where OP1 and OP2 are '<' OR '<=', and const_i can be +/- inf. where OP1 and OP2 are '<' OR '<=', and const_i can be +/- inf.
Everything else is not a partition pruning "interval". Everything else is not a partition pruning "interval".
...@@ -2695,7 +2758,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) ...@@ -2695,7 +2758,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
fields. Save all constN constants into table record buffer. fields. Save all constN constants into table record buffer.
*/ */
store_selargs_to_rec(ppar, ppar->arg_stack, ppar->part_fields); store_selargs_to_rec(ppar, ppar->arg_stack, ppar->part_fields);
DBUG_EXECUTE("info", dbug_print_onepoint_range(ppar->arg_stack, DBUG_EXECUTE("info", dbug_print_singlepoint_range(ppar->arg_stack,
ppar->part_fields);); ppar->part_fields););
uint32 part_id; uint32 part_id;
/* then find in which partition the {const1, ...,constN} tuple goes */ /* then find in which partition the {const1, ...,constN} tuple goes */
...@@ -2725,7 +2788,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) ...@@ -2725,7 +2788,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
*/ */
store_selargs_to_rec(ppar, ppar->arg_stack_end - ppar->subpart_fields, store_selargs_to_rec(ppar, ppar->arg_stack_end - ppar->subpart_fields,
ppar->subpart_fields); ppar->subpart_fields);
DBUG_EXECUTE("info", dbug_print_onepoint_range(ppar->arg_stack_end - DBUG_EXECUTE("info", dbug_print_singlepoint_range(ppar->arg_stack_end-
ppar->subpart_fields, ppar->subpart_fields,
ppar->subpart_fields);); ppar->subpart_fields););
/* Find the subpartition (it's HASH/KEY so we always have one) */ /* Find the subpartition (it's HASH/KEY so we always have one) */
...@@ -2852,7 +2915,7 @@ static bool fields_ok_for_partition_index(Field **pfield) ...@@ -2852,7 +2915,7 @@ static bool fields_ok_for_partition_index(Field **pfield)
struct struct
SYNOPSIS SYNOPSIS
create_partition_index_descrition() create_partition_index_description()
prune_par INOUT Partition pruning context prune_par INOUT Partition pruning context
DESCRIPTION DESCRIPTION
...@@ -2869,7 +2932,7 @@ static bool fields_ok_for_partition_index(Field **pfield) ...@@ -2869,7 +2932,7 @@ static bool fields_ok_for_partition_index(Field **pfield)
FALSE OK FALSE OK
*/ */
static bool create_partition_index_descrition(PART_PRUNE_PARAM *ppar) static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
{ {
RANGE_OPT_PARAM *range_par= &(ppar->range_param); RANGE_OPT_PARAM *range_par= &(ppar->range_param);
partition_info *part_info= ppar->part_info; partition_info *part_info= ppar->part_info;
...@@ -3056,7 +3119,7 @@ static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part) ...@@ -3056,7 +3119,7 @@ static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part)
Print a singlepoint multi-keypart range interval to debug trace Print a singlepoint multi-keypart range interval to debug trace
SYNOPSIS SYNOPSIS
dbug_print_onepoint_range() dbug_print_singlepoint_range()
start Array of SEL_ARG* ptrs representing conditions on key parts start Array of SEL_ARG* ptrs representing conditions on key parts
num Number of elements in the array. num Number of elements in the array.
...@@ -3065,9 +3128,9 @@ static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part) ...@@ -3065,9 +3128,9 @@ static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part)
interval to debug trace. interval to debug trace.
*/ */
static void dbug_print_onepoint_range(SEL_ARG **start, uint num) static void dbug_print_singlepoint_range(SEL_ARG **start, uint num)
{ {
DBUG_ENTER("dbug_print_onepoint_range"); DBUG_ENTER("dbug_print_singlepoint_range");
DBUG_LOCK_FILE; DBUG_LOCK_FILE;
SEL_ARG **end= start + num; SEL_ARG **end= start + num;
......
...@@ -758,7 +758,10 @@ int THD::send_explain_fields(select_result *result) ...@@ -758,7 +758,10 @@ int THD::send_explain_fields(select_result *result)
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
if (lex->describe & DESCRIBE_PARTITIONS) if (lex->describe & DESCRIBE_PARTITIONS)
{ {
field_list.push_back(item= new Item_empty_string("partitions", 10, cs)); /* Maximum length of string that make_used_partitions_str() can produce */
item= new Item_empty_string("partitions", MAX_PARTITIONS * (1 + FN_LEN),
cs);
field_list.push_back(item);
item->maybe_null= 1; item->maybe_null= 1;
} }
#endif #endif
......
...@@ -104,7 +104,7 @@ enum enum_sql_command { ...@@ -104,7 +104,7 @@ enum enum_sql_command {
#define DESCRIBE_NORMAL 1 #define DESCRIBE_NORMAL 1
#define DESCRIBE_EXTENDED 2 #define DESCRIBE_EXTENDED 2
/* /*
This is not #ifdef'ed because we want "EXPLAIN PARTITIONS ..." to produce This is not within #ifdef because we want "EXPLAIN PARTITIONS ..." to produce
additional "partitions" column even if partitioning is not compiled in. additional "partitions" column even if partitioning is not compiled in.
*/ */
#define DESCRIBE_PARTITIONS 4 #define DESCRIBE_PARTITIONS 4
......
...@@ -3467,11 +3467,19 @@ void set_key_field_ptr(KEY *key_info, const byte *new_buf, ...@@ -3467,11 +3467,19 @@ void set_key_field_ptr(KEY *key_info, const byte *new_buf,
/* /*
Fill the string comma-separated line of used partitions names Return comma-separated list of used partitions in the provided given string
SYNOPSIS SYNOPSIS
make_used_partitions_str() make_used_partitions_str()
part_info IN Partitioning info part_info IN Partitioning info
parts_str OUT The string to fill parts_str OUT The string to fill
DESCRIPTION
Generate a list of used partitions (from bits in part_info->used_partitions
bitmap), asd store it into the provided String object.
NOTE
The produced string must not be longer then MAX_PARTITIONS * (1 + FN_LEN).
*/ */
void make_used_partitions_str(partition_info *part_info, String *parts_str) void make_used_partitions_str(partition_info *part_info, String *parts_str)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment