Commit 4655118b authored by Mattias Jonsson's avatar Mattias Jonsson

Bug#46362: Endpoint should be set to false for TO_DAYS(DATE)

There were a problem since pruning uses the field
for comparison (while evaluate_join_record uses longlong),
resulting in pruning failures when comparing DATE to DATETIME.

Fix was to always comparing DATE vs DATETIME as DATETIME,
by adding ' 00:00:00' to the DATE string.

And adding optimization for comparing with 23:59:59, so that
DATETIME_col > '2001-02-03 23:59:59' ->
TO_DAYS(DATETIME_col) > TO_DAYS('2001-02-03 23:59:59') instead
of '>='.

mysql-test/r/partition_pruning.result:
  Bug#46362: Endpoint should be set to false for TO_DAYS(DATE)
  
  Updated result-file
mysql-test/t/partition_pruning.test:
  Bug#46362: Endpoint should be set to false for TO_DAYS(DATE)
  
  Added testcases.
sql-common/my_time.c:
  Bug#46362: Endpoint should be set to false for TO_DAYS(DATE)
  
  removed duplicate assignment.
sql/item.cc:
  Bug#46362: Endpoint should be set to false for TO_DAYS(DATE)
  
  Changed field_is_equal_to_item into field_cmp_to_item, to
  better handling DATE vs DATETIME comparision.
sql/item.h:
  Bug#46362: Endpoint should be set to false for TO_DAYS(DATE)
  
  Updated comment
sql/item_timefunc.cc:
  Bug#46362: Endpoint should be set to false for TO_DAYS(DATE)
  
  Added optimization (pruning) of DATETIME where time-part is
  23:59:59
sql/opt_range.cc:
  Bug#46362: Endpoint should be set to false for TO_DAYS(DATE)
  
  Using the new stored_field_cmp_to_item for better pruning.
parent fce4fa36
This diff is collapsed.
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));
......
...@@ -6846,14 +6846,21 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) ...@@ -6846,14 +6846,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(),
...@@ -6864,28 +6871,49 @@ bool field_is_equal_to_item(Field *field,Item *item) ...@@ -6864,28 +6871,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)
......
...@@ -576,8 +576,8 @@ public: ...@@ -576,8 +576,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
...@@ -3117,4 +3117,4 @@ void mark_select_range_as_dependent(THD *thd, ...@@ -3117,4 +3117,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);
...@@ -991,15 +991,19 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) ...@@ -991,15 +991,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;
......
...@@ -5855,7 +5855,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, ...@@ -5855,7 +5855,7 @@ 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
...@@ -5930,7 +5930,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, ...@@ -5930,7 +5930,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 +5944,16 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, ...@@ -5944,11 +5944,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:
......
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