Commit edb71b0c authored by Mattias Jonsson's avatar Mattias Jonsson

merge

parents 83112949 2c5e2278
# Created for verifying bug#20577.
# expects TABLE t1 (... , a DATE, ...)
--sorted_result
SELECT * FROM t1 WHERE a < '1001-01-01';
--sorted_result
SELECT * FROM t1 WHERE a <= '1001-01-01';
--sorted_result
SELECT * FROM t1 WHERE a >= '1001-01-01';
--sorted_result
SELECT * FROM t1 WHERE a > '1001-01-01';
--sorted_result
SELECT * FROM t1 WHERE a = '1001-01-01';
--sorted_result
SELECT * FROM t1 WHERE a < '1001-00-00';
--sorted_result
SELECT * FROM t1 WHERE a <= '1001-00-00';
--sorted_result
SELECT * FROM t1 WHERE a >= '1001-00-00';
--sorted_result
SELECT * FROM t1 WHERE a > '1001-00-00';
--sorted_result
SELECT * FROM t1 WHERE a = '1001-00-00';
--sorted_result
SELECT * FROM t1 WHERE a < '1999-02-31';
--sorted_result
SELECT * FROM t1 WHERE a <= '1999-02-31';
--sorted_result
SELECT * FROM t1 WHERE a >= '1999-02-31';
--sorted_result
SELECT * FROM t1 WHERE a > '1999-02-31';
--sorted_result
SELECT * FROM t1 WHERE a = '1999-02-31';
--sorted_result
SELECT * FROM t1 WHERE a BETWEEN '0000-00-00' AND '1002-00-00';
--sorted_result
SELECT * FROM t1 WHERE a BETWEEN '0000-00-00' AND '1001-01-01';
--sorted_result
SELECT * FROM t1 WHERE a BETWEEN '0001-01-02' AND '1002-00-00';
--sorted_result
SELECT * FROM t1 WHERE a BETWEEN '0001-01-01' AND '1001-01-01';
if ($explain_partitions)
{
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < '1001-01-01';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= '1001-01-01';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a >= '1001-01-01';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a > '1001-01-01';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a = '1001-01-01';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < '1001-00-00';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= '1001-00-00';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a >= '1001-00-00';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a > '1001-00-00';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a = '1001-00-00';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < '1999-02-31';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= '1999-02-31';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a >= '1999-02-31';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a > '1999-02-31';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a = '1999-02-31';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0000-00-00' AND '1002-00-00';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0000-00-00' AND '1001-01-01';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0001-01-02' AND '1002-00-00';
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0001-01-01' AND '1001-01-01';
}
This diff is collapsed.
...@@ -745,7 +745,7 @@ a ...@@ -745,7 +745,7 @@ a
EXPLAIN PARTITIONS SELECT * FROM t1 EXPLAIN PARTITIONS SELECT * FROM t1
WHERE a >= '2004-07-01' AND a <= '2004-09-30'; WHERE a >= '2004-07-01' AND a <= '2004-09-30';
id select_type table partitions type possible_keys key key_len ref rows Extra id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 p407,p408,p409 ALL NULL NULL NULL NULL 9 Using where 1 SIMPLE t1 p3xx,p407,p408,p409 ALL NULL NULL NULL NULL 18 Using where
SELECT * from t1 SELECT * from t1
WHERE (a >= '2004-07-01' AND a <= '2004-09-30') OR WHERE (a >= '2004-07-01' AND a <= '2004-09-30') OR
(a >= '2005-07-01' AND a <= '2005-09-30'); (a >= '2005-07-01' AND a <= '2005-09-30');
...@@ -772,7 +772,7 @@ EXPLAIN PARTITIONS SELECT * from t1 ...@@ -772,7 +772,7 @@ EXPLAIN PARTITIONS SELECT * from t1
WHERE (a >= '2004-07-01' AND a <= '2004-09-30') OR WHERE (a >= '2004-07-01' AND a <= '2004-09-30') OR
(a >= '2005-07-01' AND a <= '2005-09-30'); (a >= '2005-07-01' AND a <= '2005-09-30');
id select_type table partitions type possible_keys key key_len ref rows Extra id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 p407,p408,p409,p507,p508,p509 ALL NULL NULL NULL NULL 18 Using where 1 SIMPLE t1 p3xx,p407,p408,p409,p507,p508,p509 ALL NULL NULL NULL NULL 27 Using where
DROP TABLE t1; DROP TABLE t1;
create table t1 (a int); create table t1 (a int);
insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
......
This diff is collapsed.
...@@ -450,9 +450,7 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, ...@@ -450,9 +450,7 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
} }
} }
DBUG_RETURN(l_time->time_type= DBUG_RETURN(l_time->time_type);
(number_of_fields <= 3 ? MYSQL_TIMESTAMP_DATE :
MYSQL_TIMESTAMP_DATETIME));
err: err:
bzero((char*) l_time, sizeof(*l_time)); bzero((char*) l_time, sizeof(*l_time));
......
...@@ -6853,14 +6853,21 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) ...@@ -6853,14 +6853,21 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
} }
/** /**
Return true if the value stored in the field is equal to the const Compare the value stored in field, with the original item.
item.
We need to use this on the range optimizer because in some cases @param field field which the item is converted and stored in
we can't store the value in the field without some precision/character loss. @param item original item
@return Return an integer greater than, equal to, or less than 0 if
the value stored in the field is greater than, equal to,
or less than the original item
@note We only use this on the range optimizer/partition pruning,
because in some cases we can't store the value in the field
without some precision/character loss.
*/ */
bool field_is_equal_to_item(Field *field,Item *item) int stored_field_cmp_to_item(Field *field, Item *item)
{ {
Item_result res_type=item_cmp_type(field->result_type(), Item_result res_type=item_cmp_type(field->result_type(),
...@@ -6871,28 +6878,49 @@ bool field_is_equal_to_item(Field *field,Item *item) ...@@ -6871,28 +6878,49 @@ bool field_is_equal_to_item(Field *field,Item *item)
char field_buff[MAX_FIELD_WIDTH]; char field_buff[MAX_FIELD_WIDTH];
String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin),*item_result; String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin),*item_result;
String field_tmp(field_buff,sizeof(field_buff),&my_charset_bin); String field_tmp(field_buff,sizeof(field_buff),&my_charset_bin);
enum_field_types field_type;
item_result=item->val_str(&item_tmp); item_result=item->val_str(&item_tmp);
if (item->null_value) if (item->null_value)
return 1; // This must be true return 0;
field->val_str(&field_tmp); field->val_str(&field_tmp);
return !stringcmp(&field_tmp,item_result);
/*
If comparing DATE with DATETIME, append the time-part to the DATE.
So that the strings are equally formatted.
A DATE converted to string is 10 characters, and a DATETIME converted
to string is 19 characters.
*/
field_type= field->type();
if (field_type == MYSQL_TYPE_DATE &&
item_result->length() == 19)
field_tmp.append(" 00:00:00");
else if (field_type == MYSQL_TYPE_DATETIME &&
item_result->length() == 10)
item_result->append(" 00:00:00");
return stringcmp(&field_tmp,item_result);
} }
if (res_type == INT_RESULT) if (res_type == INT_RESULT)
return 1; // Both where of type int return 0; // Both are of type int
if (res_type == DECIMAL_RESULT) if (res_type == DECIMAL_RESULT)
{ {
my_decimal item_buf, *item_val, my_decimal item_buf, *item_val,
field_buf, *field_val; field_buf, *field_val;
item_val= item->val_decimal(&item_buf); item_val= item->val_decimal(&item_buf);
if (item->null_value) if (item->null_value)
return 1; // This must be true return 0;
field_val= field->val_decimal(&field_buf); field_val= field->val_decimal(&field_buf);
return !my_decimal_cmp(item_val, field_val); return my_decimal_cmp(item_val, field_val);
} }
double result= item->val_real(); double result= item->val_real();
if (item->null_value) if (item->null_value)
return 0;
double field_result= field->val_real();
if (field_result < result)
return -1;
else if (field_result > result)
return 1; return 1;
return result == field->val_real(); return 0;
} }
Item_cache* Item_cache::get_cache(const Item *item) Item_cache* Item_cache::get_cache(const Item *item)
......
...@@ -397,13 +397,20 @@ public: ...@@ -397,13 +397,20 @@ public:
from INT_RESULT, may be NULL, or are unsigned. from INT_RESULT, may be NULL, or are unsigned.
It will be possible to address this issue once the related partitioning bugs It will be possible to address this issue once the related partitioning bugs
(BUG#16002, BUG#15447, BUG#13436) are fixed. (BUG#16002, BUG#15447, BUG#13436) are fixed.
The NOT_NULL enums are used in TO_DAYS, since TO_DAYS('2001-00-00') returns
NULL which puts those rows into the NULL partition, but
'2000-12-31' < '2001-00-00' < '2001-01-01'. So special handling is needed
for this (see Bug#20577).
*/ */
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_INCREASING_NOT_NULL, /* But only for valid/real x and y */
MONOTONIC_STRICT_INCREASING,/* F() is unary and (x < y) => (F(x) < F(y)) */
MONOTONIC_STRICT_INCREASING_NOT_NULL /* But only for valid/real x and y */
} enum_monotonicity_info; } enum_monotonicity_info;
/*************************************************************************/ /*************************************************************************/
...@@ -576,8 +583,8 @@ public: ...@@ -576,8 +583,8 @@ public:
left_endp FALSE <=> The interval is "x < const" or "x <= const" left_endp FALSE <=> The interval is "x < const" or "x <= const"
TRUE <=> The interval is "x > const" or "x >= const" TRUE <=> The interval is "x > const" or "x >= const"
incl_endp IN TRUE <=> the comparison is '<' or '>' incl_endp IN FALSE <=> the comparison is '<' or '>'
FALSE <=> the comparison is '<=' or '>=' TRUE <=> the comparison is '<=' or '>='
OUT The same but for the "F(x) $CMP$ F(const)" comparison OUT The same but for the "F(x) $CMP$ F(const)" comparison
DESCRIPTION DESCRIPTION
...@@ -3118,4 +3125,4 @@ void mark_select_range_as_dependent(THD *thd, ...@@ -3118,4 +3125,4 @@ void mark_select_range_as_dependent(THD *thd,
extern Cached_item *new_Cached_item(THD *thd, Item *item); extern Cached_item *new_Cached_item(THD *thd, Item *item);
extern Item_result item_cmp_type(Item_result a,Item_result b); extern Item_result item_cmp_type(Item_result a,Item_result b);
extern void resolve_const_item(THD *thd, Item **ref, Item *cmp_item); extern void resolve_const_item(THD *thd, Item **ref, Item *cmp_item);
extern bool field_is_equal_to_item(Field *field,Item *item); extern int stored_field_cmp_to_item(Field *field, Item *item);
...@@ -960,9 +960,9 @@ enum_monotonicity_info Item_func_to_days::get_monotonicity_info() const ...@@ -960,9 +960,9 @@ 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)
{ {
if (args[0]->field_type() == MYSQL_TYPE_DATE) if (args[0]->field_type() == MYSQL_TYPE_DATE)
return MONOTONIC_STRICT_INCREASING; return MONOTONIC_STRICT_INCREASING_NOT_NULL;
if (args[0]->field_type() == MYSQL_TYPE_DATETIME) if (args[0]->field_type() == MYSQL_TYPE_DATETIME)
return MONOTONIC_INCREASING; return MONOTONIC_INCREASING_NOT_NULL;
} }
return NON_MONOTONIC; return NON_MONOTONIC;
} }
...@@ -973,12 +973,27 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) ...@@ -973,12 +973,27 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime; MYSQL_TIME ltime;
longlong res; longlong res;
if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE)) int dummy; /* unused */
if (get_arg0_date(&ltime, TIME_FUZZY_DATE))
{ {
/* got NULL, leave the incl_endp intact */ /* got NULL, leave the incl_endp intact */
return LONGLONG_MIN; return LONGLONG_MIN;
} }
res=(longlong) calc_daynr(ltime.year,ltime.month,ltime.day); res=(longlong) calc_daynr(ltime.year,ltime.month,ltime.day);
/* Set to NULL if invalid date, but keep the value */
null_value= check_date(&ltime,
(ltime.year || ltime.month || ltime.day),
(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE),
&dummy);
if (null_value)
{
/*
Even if the evaluation return NULL, the calc_daynr is useful for pruning
*/
if (args[0]->field_type() != MYSQL_TYPE_DATE)
*incl_endp= TRUE;
return res;
}
if (args[0]->field_type() == MYSQL_TYPE_DATE) if (args[0]->field_type() == MYSQL_TYPE_DATE)
{ {
...@@ -991,15 +1006,19 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) ...@@ -991,15 +1006,19 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
point to day bound ("strictly less" comparison stays intact): point to day bound ("strictly less" comparison stays intact):
col < '2007-09-15 00:00:00' -> TO_DAYS(col) < TO_DAYS('2007-09-15') col < '2007-09-15 00:00:00' -> TO_DAYS(col) < TO_DAYS('2007-09-15')
col > '2007-09-15 23:59:59' -> TO_DAYS(col) > TO_DAYS('2007-09-15')
which is different from the general case ("strictly less" changes to which is different from the general case ("strictly less" changes to
"less or equal"): "less or equal"):
col < '2007-09-15 12:34:56' -> TO_DAYS(col) <= TO_DAYS('2007-09-15') col < '2007-09-15 12:34:56' -> TO_DAYS(col) <= TO_DAYS('2007-09-15')
*/ */
if (!left_endp && !(ltime.hour || ltime.minute || ltime.second || if ((!left_endp && !(ltime.hour || ltime.minute || ltime.second ||
ltime.second_part)) ltime.second_part)) ||
; /* do nothing */ (left_endp && ltime.hour == 23 && ltime.minute == 59 &&
ltime.second == 59))
/* do nothing */
;
else else
*incl_endp= TRUE; *incl_endp= TRUE;
return res; return res;
......
...@@ -2277,7 +2277,8 @@ enum enum_explain_filename_mode ...@@ -2277,7 +2277,8 @@ enum enum_explain_filename_mode
{ {
EXPLAIN_ALL_VERBOSE= 0, EXPLAIN_ALL_VERBOSE= 0,
EXPLAIN_PARTITIONS_VERBOSE, EXPLAIN_PARTITIONS_VERBOSE,
EXPLAIN_PARTITIONS_AS_COMMENT EXPLAIN_PARTITIONS_AS_COMMENT,
EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING
}; };
uint explain_filename(const char *from, char *to, uint to_length, uint explain_filename(const char *from, char *to, uint to_length,
enum_explain_filename_mode explain_mode); enum_explain_filename_mode explain_mode);
......
...@@ -5826,6 +5826,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, ...@@ -5826,6 +5826,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
{ {
tree= new (alloc) SEL_ARG(field, 0, 0); tree= new (alloc) SEL_ARG(field, 0, 0);
tree->type= SEL_ARG::IMPOSSIBLE; tree->type= SEL_ARG::IMPOSSIBLE;
field->table->in_use->variables.sql_mode= orig_sql_mode;
goto end; goto end;
} }
else else
...@@ -5855,13 +5856,16 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, ...@@ -5855,13 +5856,16 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
but we'll need to convert '>' to '>=' and '<' to '<='. This will but we'll need to convert '>' to '>=' and '<' to '<='. This will
be done together with other types at the end of this function be done together with other types at the end of this function
(grep for field_is_equal_to_item) (grep for stored_field_cmp_to_item)
*/ */
} }
else else
{
field->table->in_use->variables.sql_mode= orig_sql_mode;
goto end; goto end;
} }
} }
}
/* /*
guaranteed at this point: err > 0; field and const of same type guaranteed at this point: err > 0; field and const of same type
...@@ -5930,7 +5934,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, ...@@ -5930,7 +5934,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
switch (type) { switch (type) {
case Item_func::LT_FUNC: case Item_func::LT_FUNC:
if (field_is_equal_to_item(field,value)) if (stored_field_cmp_to_item(field,value) == 0)
tree->max_flag=NEAR_MAX; tree->max_flag=NEAR_MAX;
/* fall through */ /* fall through */
case Item_func::LE_FUNC: case Item_func::LE_FUNC:
...@@ -5944,11 +5948,16 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, ...@@ -5944,11 +5948,16 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
break; break;
case Item_func::GT_FUNC: case Item_func::GT_FUNC:
/* Don't use open ranges for partial key_segments */ /* Don't use open ranges for partial key_segments */
if (field_is_equal_to_item(field,value) && if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
!(key_part->flag & HA_PART_KEY_SEG)) (stored_field_cmp_to_item(field, value) <= 0))
tree->min_flag=NEAR_MIN; tree->min_flag=NEAR_MIN;
/* fall through */ tree->max_flag= NO_MAX_RANGE;
break;
case Item_func::GE_FUNC: case Item_func::GE_FUNC:
/* Don't use open ranges for partial key_segments */
if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
(stored_field_cmp_to_item(field,value) < 0))
tree->min_flag= NEAR_MIN;
tree->max_flag=NO_MAX_RANGE; tree->max_flag=NO_MAX_RANGE;
break; break;
case Item_func::SP_EQUALS_FUNC: case Item_func::SP_EQUALS_FUNC:
...@@ -6439,13 +6448,6 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) ...@@ -6439,13 +6448,6 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
return 0; // Can't optimize this return 0; // Can't optimize this
} }
if ((key1->min_flag | key2->min_flag) & GEOM_FLAG)
{
key1->free_tree();
key2->free_tree();
return 0; // Can't optimize this
}
key1->use_count--; key1->use_count--;
key2->use_count--; key2->use_count--;
SEL_ARG *e1=key1->first(), *e2=key2->first(), *new_tree=0; SEL_ARG *e1=key1->first(), *e2=key2->first(), *new_tree=0;
......
...@@ -300,6 +300,7 @@ static inline void init_single_partition_iterator(uint32 part_id, ...@@ -300,6 +300,7 @@ static inline void init_single_partition_iterator(uint32 part_id,
{ {
part_iter->part_nums.start= part_iter->part_nums.cur= part_id; part_iter->part_nums.start= part_iter->part_nums.cur= part_id;
part_iter->part_nums.end= part_id+1; part_iter->part_nums.end= part_id+1;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
part_iter->get_next= get_next_partition_id_range; part_iter->get_next= get_next_partition_id_range;
} }
......
...@@ -6184,17 +6184,17 @@ ER_FUNC_INEXISTENT_NAME_COLLISION 42000 ...@@ -6184,17 +6184,17 @@ ER_FUNC_INEXISTENT_NAME_COLLISION 42000
# When updating these, please update EXPLAIN_FILENAME_MAX_EXTRA_LENGTH in # When updating these, please update EXPLAIN_FILENAME_MAX_EXTRA_LENGTH in
# mysql_priv.h with the new maximal additional length for explain_filename. # mysql_priv.h with the new maximal additional length for explain_filename.
ER_DATABASE_NAME ER_DATABASE_NAME
eng "Database `%s`" eng "Database"
swe "Databas `%s`" swe "Databas"
ER_TABLE_NAME ER_TABLE_NAME
eng "Table `%s`" eng "Table"
swe "Tabell `%s`" swe "Tabell"
ER_PARTITION_NAME ER_PARTITION_NAME
eng "Partition `%s`" eng "Partition"
swe "Partition `%s`" swe "Partition"
ER_SUBPARTITION_NAME ER_SUBPARTITION_NAME
eng "Subpartition `%s`" eng "Subpartition"
swe "Subpartition `%s`" swe "Subpartition"
ER_TEMPORARY_NAME ER_TEMPORARY_NAME
eng "Temporary" eng "Temporary"
swe "Temporr" swe "Temporr"
......
...@@ -2766,8 +2766,24 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info, ...@@ -2766,8 +2766,24 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
if (part_info->part_expr->null_value) if (part_info->part_expr->null_value)
{ {
/*
Special handling for MONOTONIC functions that can return NULL for
values that are comparable. I.e.
'2000-00-00' can be compared to '2000-01-01' but TO_DAYS('2000-00-00')
returns NULL which cannot be compared used <, >, <=, >= etc.
Otherwise, just return the the first index (lowest value).
*/
enum_monotonicity_info monotonic;
monotonic= part_info->part_expr->get_monotonicity_info();
if (monotonic != MONOTONIC_INCREASING_NOT_NULL &&
monotonic != MONOTONIC_STRICT_INCREASING_NOT_NULL)
{
/* F(col) can not return NULL, return index with lowest value */
DBUG_RETURN(0); DBUG_RETURN(0);
} }
}
if (unsigned_flag) if (unsigned_flag)
part_func_value-= 0x8000000000000000ULL; part_func_value-= 0x8000000000000000ULL;
DBUG_ASSERT(part_info->no_list_values); DBUG_ASSERT(part_info->no_list_values);
...@@ -2916,11 +2932,29 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info, ...@@ -2916,11 +2932,29 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
if (part_info->part_expr->null_value) if (part_info->part_expr->null_value)
{ {
uint32 ret_part_id= 0; /*
Special handling for MONOTONIC functions that can return NULL for
values that are comparable. I.e.
'2000-00-00' can be compared to '2000-01-01' but TO_DAYS('2000-00-00')
returns NULL which cannot be compared used <, >, <=, >= etc.
Otherwise, just return the first partition
(may be included if not left endpoint)
*/
enum_monotonicity_info monotonic;
monotonic= part_info->part_expr->get_monotonicity_info();
if (monotonic != MONOTONIC_INCREASING_NOT_NULL &&
monotonic != MONOTONIC_STRICT_INCREASING_NOT_NULL)
{
/* F(col) can not return NULL, return partition with lowest value */
if (!left_endpoint && include_endpoint) if (!left_endpoint && include_endpoint)
ret_part_id= 1; DBUG_RETURN(1);
DBUG_RETURN(ret_part_id); DBUG_RETURN(0);
} }
}
if (unsigned_flag) if (unsigned_flag)
part_func_value-= 0x8000000000000000ULL; part_func_value-= 0x8000000000000000ULL;
if (left_endpoint && !include_endpoint) if (left_endpoint && !include_endpoint)
...@@ -6696,6 +6730,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, ...@@ -6696,6 +6730,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
Field *field= part_info->part_field_array[0]; Field *field= part_info->part_field_array[0];
uint32 max_endpoint_val; uint32 max_endpoint_val;
get_endpoint_func get_endpoint; get_endpoint_func get_endpoint;
bool can_match_multiple_values; /* is not '=' */
uint field_len= field->pack_length_in_rec(); uint field_len= field->pack_length_in_rec();
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE; part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
...@@ -6734,6 +6769,23 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, ...@@ -6734,6 +6769,23 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
else else
assert(0); assert(0);
can_match_multiple_values= (flags || !min_value || !max_value ||
memcmp(min_value, max_value, field_len));
if (can_match_multiple_values &&
(part_info->part_type == RANGE_PARTITION ||
part_info->has_null_value))
{
/* Range scan on RANGE or LIST partitioned table */
enum_monotonicity_info monotonic;
monotonic= part_info->part_expr->get_monotonicity_info();
if (monotonic == MONOTONIC_INCREASING_NOT_NULL ||
monotonic == MONOTONIC_STRICT_INCREASING_NOT_NULL)
{
/* col is NOT NULL, but F(col) can return NULL, add NULL partition */
part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
}
}
/* /*
Find minimum: Do special handling if the interval has left bound in form Find minimum: Do special handling if the interval has left bound in form
" NULL <= X ": " NULL <= X ":
...@@ -6765,6 +6817,14 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info, ...@@ -6765,6 +6817,14 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
store_key_image_to_rec(field, min_value, field_len); store_key_image_to_rec(field, min_value, field_len);
bool include_endp= !test(flags & NEAR_MIN); bool include_endp= !test(flags & NEAR_MIN);
part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp); part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp);
if (!can_match_multiple_values && part_info->part_expr->null_value)
{
/* col = x and F(x) = NULL -> only search NULL partition */
part_iter->part_nums.cur= part_iter->part_nums.start= 0;
part_iter->part_nums.end= 0;
part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
return 1;
}
part_iter->part_nums.cur= part_iter->part_nums.start; part_iter->part_nums.cur= part_iter->part_nums.start;
if (part_iter->part_nums.start == max_endpoint_val) if (part_iter->part_nums.start == max_endpoint_val)
return 0; /* No partitions */ return 0; /* No partitions */
...@@ -6959,7 +7019,13 @@ uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter) ...@@ -6959,7 +7019,13 @@ uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter)
{ {
if (part_iter->part_nums.cur >= part_iter->part_nums.end) if (part_iter->part_nums.cur >= part_iter->part_nums.end)
{ {
if (part_iter->ret_null_part)
{
part_iter->ret_null_part= FALSE;
return 0; /* NULL always in first range partition */
}
part_iter->part_nums.cur= part_iter->part_nums.start; 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; return NOT_A_PARTITION_ID;
} }
else else
...@@ -6987,7 +7053,7 @@ uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter) ...@@ -6987,7 +7053,7 @@ uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter)
uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter) uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter)
{ {
if (part_iter->part_nums.cur == part_iter->part_nums.end) if (part_iter->part_nums.cur >= part_iter->part_nums.end)
{ {
if (part_iter->ret_null_part) if (part_iter->ret_null_part)
{ {
......
...@@ -72,7 +72,7 @@ static void wait_for_kill_signal(THD *thd) ...@@ -72,7 +72,7 @@ static void wait_for_kill_signal(THD *thd)
@brief Helper function for explain_filename @brief Helper function for explain_filename
*/ */
static char* add_identifier(char *to_p, const char * end_p, static char* add_identifier(char *to_p, const char * end_p,
const char* name, uint name_len, int errcode) const char* name, uint name_len, bool add_quotes)
{ {
uint res; uint res;
uint errors; uint errors;
...@@ -92,18 +92,44 @@ static char* add_identifier(char *to_p, const char * end_p, ...@@ -92,18 +92,44 @@ static char* add_identifier(char *to_p, const char * end_p,
res= strconvert(&my_charset_filename, conv_name, system_charset_info, res= strconvert(&my_charset_filename, conv_name, system_charset_info,
conv_string, FN_REFLEN, &errors); conv_string, FN_REFLEN, &errors);
if (!res || errors) if (!res || errors)
{
DBUG_PRINT("error", ("strconvert of '%s' failed with %u (errors: %u)", conv_name, res, errors));
conv_name= name; conv_name= name;
}
else else
{ {
DBUG_PRINT("info", ("conv '%s' -> '%s'", conv_name, conv_string)); DBUG_PRINT("info", ("conv '%s' -> '%s'", conv_name, conv_string));
conv_name= conv_string; conv_name= conv_string;
} }
if (errcode) if (add_quotes && (end_p - to_p > 2))
to_p+= my_snprintf(to_p, end_p - to_p, ER(errcode), conv_name); {
*(to_p++)= '`';
while (*conv_name && (end_p - to_p - 1) > 0)
{
uint length= my_mbcharlen(system_charset_info, *conv_name);
if (!length)
length= 1;
if (length == 1 && *conv_name == '`')
{
if ((end_p - to_p) < 3)
break;
*(to_p++)= '`';
*(to_p++)= *(conv_name++);
}
else if (length < (end_p - to_p))
{
to_p= strnmov(to_p, conv_name, length);
conv_name+= length;
}
else else
to_p+= my_snprintf(to_p, end_p - to_p, "`%s`", conv_name); break; /* string already filled */
return to_p; }
to_p= strnmov(to_p, "`", end_p - to_p);
}
else
to_p= strnmov(to_p, conv_name, end_p - to_p);
DBUG_RETURN(to_p);
} }
...@@ -135,6 +161,8 @@ static char* add_identifier(char *to_p, const char * end_p, ...@@ -135,6 +161,8 @@ static char* add_identifier(char *to_p, const char * end_p,
[,[ Temporary| Renamed] Partition `p` [,[ Temporary| Renamed] Partition `p`
[, Subpartition `sp`]] *| [, Subpartition `sp`]] *|
(| is really a /, and it is all in one line) (| is really a /, and it is all in one line)
EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING ->
same as above but no quotes are added.
@retval Length of returned string @retval Length of returned string
*/ */
...@@ -245,28 +273,39 @@ uint explain_filename(const char *from, ...@@ -245,28 +273,39 @@ uint explain_filename(const char *from,
part_name_len-= 5; part_name_len-= 5;
} }
} }
else
table_name_len= strlen(table_name);
if (db_name) if (db_name)
{ {
if (explain_mode == EXPLAIN_ALL_VERBOSE) if (explain_mode == EXPLAIN_ALL_VERBOSE)
{ {
to_p= add_identifier(to_p, end_p, db_name, db_name_len, to_p= strnmov(to_p, ER(ER_DATABASE_NAME), end_p - to_p);
ER_DATABASE_NAME); *(to_p++)= ' ';
to_p= add_identifier(to_p, end_p, db_name, db_name_len, 1);
to_p= strnmov(to_p, ", ", end_p - to_p); to_p= strnmov(to_p, ", ", end_p - to_p);
} }
else else
{ {
to_p= add_identifier(to_p, end_p, db_name, db_name_len, 0); to_p= add_identifier(to_p, end_p, db_name, db_name_len,
(explain_mode !=
EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING));
to_p= strnmov(to_p, ".", end_p - to_p); to_p= strnmov(to_p, ".", end_p - to_p);
} }
} }
if (explain_mode == EXPLAIN_ALL_VERBOSE) if (explain_mode == EXPLAIN_ALL_VERBOSE)
to_p= add_identifier(to_p, end_p, table_name, table_name_len, {
ER_TABLE_NAME); to_p= strnmov(to_p, ER(ER_TABLE_NAME), end_p - to_p);
*(to_p++)= ' ';
to_p= add_identifier(to_p, end_p, table_name, table_name_len, 1);
}
else else
to_p= add_identifier(to_p, end_p, table_name, table_name_len, 0); to_p= add_identifier(to_p, end_p, table_name, table_name_len,
(explain_mode !=
EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING));
if (part_name) if (part_name)
{ {
if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT) if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT ||
explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING)
to_p= strnmov(to_p, " /* ", end_p - to_p); to_p= strnmov(to_p, " /* ", end_p - to_p);
else if (explain_mode == EXPLAIN_PARTITIONS_VERBOSE) else if (explain_mode == EXPLAIN_PARTITIONS_VERBOSE)
to_p= strnmov(to_p, " ", end_p - to_p); to_p= strnmov(to_p, " ", end_p - to_p);
...@@ -280,15 +319,22 @@ uint explain_filename(const char *from, ...@@ -280,15 +319,22 @@ uint explain_filename(const char *from,
to_p= strnmov(to_p, ER(ER_RENAMED_NAME), end_p - to_p); to_p= strnmov(to_p, ER(ER_RENAMED_NAME), end_p - to_p);
to_p= strnmov(to_p, " ", end_p - to_p); to_p= strnmov(to_p, " ", end_p - to_p);
} }
to_p= strnmov(to_p, ER(ER_PARTITION_NAME), end_p - to_p);
*(to_p++)= ' ';
to_p= add_identifier(to_p, end_p, part_name, part_name_len, to_p= add_identifier(to_p, end_p, part_name, part_name_len,
ER_PARTITION_NAME); (explain_mode !=
EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING));
if (subpart_name) if (subpart_name)
{ {
to_p= strnmov(to_p, ", ", end_p - to_p); to_p= strnmov(to_p, ", ", end_p - to_p);
to_p= strnmov(to_p, ER(ER_SUBPARTITION_NAME), end_p - to_p);
*(to_p++)= ' ';
to_p= add_identifier(to_p, end_p, subpart_name, subpart_name_len, to_p= add_identifier(to_p, end_p, subpart_name, subpart_name_len,
ER_SUBPARTITION_NAME); (explain_mode !=
EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING));
} }
if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT) if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT ||
explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING)
to_p= strnmov(to_p, " */", end_p - to_p); to_p= strnmov(to_p, " */", end_p - to_p);
} }
DBUG_PRINT("exit", ("to '%s'", to)); DBUG_PRINT("exit", ("to '%s'", to));
......
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