Commit d390e501 authored by Sergei Golubchik's avatar Sergei Golubchik

MDEV-11839 move value caching from get_datetime_value to fix_fields time

Refactor get_datetime_value() not to create Item_cache_temporal(),
but do it always in ::fix_fields() or ::fix_length_and_dec().

Creating items at the execution time doesn't work very well with
virtual columns and check constraints that are fixed and executed
in different THDs.
parent 1c6f6dc8
......@@ -7,7 +7,6 @@ a
2002-03-04
Warnings:
Note 1003 2000-01-01
Note 1003 2000-01-06
set debug_dbug='';
drop table t1;
create table t1 (id int not null, ut timestamp(6) not null);
......
......@@ -403,3 +403,37 @@ DROP TABLE t1;
#
# End of 10.1 test
#
select case 'foo' when time'10:00:00' then 'never' when '0' then 'bug' else 'ok' end;
case 'foo' when time'10:00:00' then 'never' when '0' then 'bug' else 'ok' end
ok
Warnings:
Warning 1292 Truncated incorrect time value: 'foo'
select 'foo' in (time'10:00:00','0');
'foo' in (time'10:00:00','0')
0
Warnings:
Warning 1292 Truncated incorrect time value: 'foo'
create table t1 (a time);
insert t1 values (100000), (102030), (203040);
select case 'foo' when a then 'never' when '0' then 'bug' else 'ok' end from t1;
case 'foo' when a then 'never' when '0' then 'bug' else 'ok' end
ok
ok
ok
Warnings:
Warning 1292 Truncated incorrect time value: 'foo'
Warning 1292 Truncated incorrect time value: 'foo'
Warning 1292 Truncated incorrect time value: 'foo'
select 'foo' in (a,'0') from t1;
'foo' in (a,'0')
0
0
0
Warnings:
Warning 1292 Truncated incorrect time value: 'foo'
Warning 1292 Truncated incorrect time value: 'foo'
Warning 1292 Truncated incorrect time value: 'foo'
drop table t1;
select case '20:10:05' when date'2020-10-10' then 'never' when time'20:10:5' then 'ok' else 'bug' end;
case '20:10:05' when date'2020-10-10' then 'never' when time'20:10:5' then 'ok' else 'bug' end
bug
......@@ -1595,8 +1595,6 @@ NULL
Warnings:
Warning 1411 Incorrect datetime value: '2007-20-00' for function str_to_date
Warning 1411 Incorrect datetime value: '2007-20-00' for function str_to_date
Warning 1411 Incorrect datetime value: '2007-20-00' for function str_to_date
Warning 1411 Incorrect datetime value: '2007-20-00' for function str_to_date
SELECT str_to_date('2007-10-00', '%Y-%m-%d') BETWEEN '' AND '2007/10/20';
str_to_date('2007-10-00', '%Y-%m-%d') BETWEEN '' AND '2007/10/20'
1
......
......@@ -1597,8 +1597,6 @@ NULL
Warnings:
Warning 1411 Incorrect datetime value: '2007-20-00' for function str_to_date
Warning 1411 Incorrect datetime value: '2007-20-00' for function str_to_date
Warning 1411 Incorrect datetime value: '2007-20-00' for function str_to_date
Warning 1411 Incorrect datetime value: '2007-20-00' for function str_to_date
SELECT str_to_date('2007-10-00', '%Y-%m-%d') BETWEEN '' AND '2007/10/20';
str_to_date('2007-10-00', '%Y-%m-%d') BETWEEN '' AND '2007/10/20'
1
......
......@@ -287,3 +287,22 @@ DROP TABLE t1;
--echo #
--echo # End of 10.1 test
--echo #
#
# caching of first argument in CASE/IN for temporal types
#
#
# should not convert all values to time
select case 'foo' when time'10:00:00' then 'never' when '0' then 'bug' else 'ok' end;
select 'foo' in (time'10:00:00','0');
create table t1 (a time);
insert t1 values (100000), (102030), (203040);
# only one warning, TIME('foo') should be cached
select case 'foo' when a then 'never' when '0' then 'bug' else 'ok' end from t1;
select 'foo' in (a,'0') from t1;
drop table t1;
# first comparison should be as date, second as time
select case '20:10:05' when date'2020-10-10' then 'never' when time'20:10:5' then 'ok' else 'bug' end;
......@@ -9361,13 +9361,16 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
switch (res_type) {
case TIME_RESULT:
{
bool is_null;
Item **ref_copy= ref;
/* the following call creates a constant and puts it in new_item */
enum_field_types type= item->field_type_for_temporal_comparison(comp_item);
get_datetime_value(thd, &ref_copy, &new_item, type, &is_null);
if (is_null)
longlong value= item->val_temporal_packed(type);
if (item->null_value)
new_item= new (mem_root) Item_null(thd, name);
else
{
Item_cache_temporal *cache= new (mem_root) Item_cache_temporal(thd, type);
cache->store_packed(value, item);
new_item= cache;
}
break;
}
case STRING_RESULT:
......@@ -9702,8 +9705,7 @@ Item_cache_temporal::Item_cache_temporal(THD *thd,
longlong Item_cache_temporal::val_datetime_packed()
{
DBUG_ASSERT(fixed == 1);
if (Item_cache_temporal::field_type() == MYSQL_TYPE_TIME)
return Item::val_datetime_packed(); // TIME-to-DATETIME conversion needed
DBUG_ASSERT(Item_cache_temporal::field_type() != MYSQL_TYPE_TIME);
if ((!value_cached && !cache_value()) || null_value)
{
null_value= TRUE;
......@@ -9716,8 +9718,7 @@ longlong Item_cache_temporal::val_datetime_packed()
longlong Item_cache_temporal::val_time_packed()
{
DBUG_ASSERT(fixed == 1);
if (Item_cache_temporal::field_type() != MYSQL_TYPE_TIME)
return Item::val_time_packed(); // DATETIME-to-TIME conversion needed
DBUG_ASSERT(Item_cache_temporal::field_type() == MYSQL_TYPE_TIME);
if ((!value_cached && !cache_value()) || null_value)
{
null_value= TRUE;
......@@ -9779,14 +9780,22 @@ bool Item_cache_temporal::cache_value()
{
if (!example)
return false;
value_cached= true;
MYSQL_TIME ltime;
if (example->get_date_result(&ltime, 0))
value=0;
else
uint fuzzydate= TIME_FUZZY_DATES | TIME_INVALID_DATES;
if (Item_cache_temporal::field_type() == MYSQL_TYPE_TIME)
fuzzydate|= TIME_TIME_ONLY;
value= 0;
if (!example->get_date_result(&ltime, fuzzydate))
{
if (ltime.time_type == MYSQL_TIMESTAMP_TIME &&
!(fuzzydate & TIME_TIME_ONLY) &&
convert_time_to_datetime(current_thd, &ltime, fuzzydate))
return true;
value= pack_time(&ltime);
}
null_value= example->null_value;
return true;
}
......@@ -9805,12 +9814,16 @@ bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
unpack_time(value, ltime);
ltime->time_type= mysql_type_to_time_type(field_type());
if (ltime->time_type == MYSQL_TIMESTAMP_TIME)
{
if (fuzzydate & TIME_TIME_ONLY)
{
ltime->hour+= (ltime->month*32+ltime->day)*24;
ltime->month= ltime->day= 0;
}
else if (convert_time_to_datetime(current_thd, ltime, fuzzydate))
return true;
}
return 0;
}
......
......@@ -41,10 +41,14 @@ static Item** cache_converted_constant(THD *thd, Item **value,
find an temporal type (item) that others will be converted to
for the purpose of comparison.
for IN/CASE conversion only happens if the first item defines the
comparison context.
this is the type that will be used in warnings like
"Incorrect <<TYPE>> value".
*/
static Item *find_date_time_item(Item **args, uint nargs, uint col)
static Item *find_date_time_item(THD *thd, Item **args, uint nargs, uint col,
bool in_case)
{
Item *date_arg= 0, **arg, **arg_end;
for (arg= args, arg_end= args + nargs; arg != arg_end ; arg++)
......@@ -52,10 +56,22 @@ static Item *find_date_time_item(Item **args, uint nargs, uint col)
Item *item= arg[0]->element_index(col);
if (item->cmp_type() != TIME_RESULT)
continue;
if (item->field_type() == MYSQL_TYPE_DATETIME)
return item;
if (!date_arg)
date_arg= item;
if (item->field_type() == MYSQL_TYPE_DATETIME)
break;
}
if (in_case ? date_arg == args[0]->element_index(col) : date_arg != NULL)
{
enum_field_types f_type= date_arg->field_type();
for (arg= args, arg_end= args + nargs; arg != arg_end ; arg++)
{
Item *cache, **a= arg[0]->addr(col);
if (!a)
a= arg;
if (cache_converted_constant(thd, a, &cache, TIME_RESULT, f_type) != a)
thd->change_item_tree(a, cache);
}
}
return date_arg;
}
......@@ -658,6 +674,8 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
&Arg_comparator::compare_datetime;
}
a= cache_converted_constant(thd, a, &a_cache, m_compare_type, f_type);
b= cache_converted_constant(thd, b, &b_cache, m_compare_type, f_type);
return 0;
}
......@@ -685,9 +703,11 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
&Arg_comparator::compare_datetime;
}
a= cache_converted_constant(thd, a, &a_cache, m_compare_type);
b= cache_converted_constant(thd, b, &b_cache, m_compare_type);
else
{
a= cache_converted_constant(thd, a, &a_cache, m_compare_type, (*a)->field_type());
b= cache_converted_constant(thd, b, &b_cache, m_compare_type, (*b)->field_type());
}
return set_compare_func(owner_arg, m_compare_type);
}
......@@ -710,18 +730,13 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
*/
static Item** cache_converted_constant(THD *thd, Item **value,
Item **cache_item, Item_result type)
Item **cache_item, Item_result type, enum_field_types f_type)
{
/*
Don't need cache if doing context analysis only.
Also, get_datetime_value creates Item_cache internally.
Unless fixed, we should not do it here.
*/
/* Don't need cache if doing context analysis only. */
if (!thd->lex->is_ps_or_view_context_analysis() &&
(*value)->const_item() && type != (*value)->result_type() &&
type != TIME_RESULT)
(*value)->const_item() && type != (*value)->result_type())
{
Item_cache *cache= Item_cache::get_cache(thd, *value, type);
Item_cache *cache= Item_cache::get_cache(thd, *value, type, f_type);
cache->setup(thd, *value);
*cache_item= cache;
return cache_item;
......@@ -761,26 +776,11 @@ static Item** cache_converted_constant(THD *thd, Item **value,
MYSQL_TIME value, packed in a longlong, suitable for comparison.
*/
longlong
get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
enum_field_types f_type, bool *is_null)
longlong get_datetime_value(Item *item, enum_field_types f_type, bool *is_null)
{
longlong UNINIT_VAR(value);
Item *item= **item_arg;
value= item->val_temporal_packed(f_type);
longlong value= item->val_temporal_packed(f_type);
if ((*is_null= item->null_value))
return ~(ulonglong) 0;
if (cache_arg && item->const_item() &&
!(item->type() == Item::CACHE_ITEM && item->cmp_type() == TIME_RESULT))
{
if (!thd)
thd= current_thd;
Item_cache_temporal *cache= new (thd->mem_root) Item_cache_temporal(thd, f_type);
cache->store_packed(value, item);
*cache_arg= cache;
*item_arg= cache_arg;
}
return value;
}
......@@ -811,12 +811,12 @@ int Arg_comparator::compare_temporal(enum_field_types type)
owner->null_value= 1;
/* Get DATE/DATETIME/TIME value of the 'a' item. */
a_value= get_datetime_value(0, &a, &a_cache, type, &a_is_null);
a_value= get_datetime_value(*a, type, &a_is_null);
if (a_is_null)
return -1;
/* Get DATE/DATETIME/TIME value of the 'b' item. */
b_value= get_datetime_value(0, &b, &b_cache, type, &b_is_null);
b_value= get_datetime_value(*b, type, &b_is_null);
if (b_is_null)
return -1;
......@@ -834,10 +834,10 @@ int Arg_comparator::compare_e_temporal(enum_field_types type)
longlong a_value, b_value;
/* Get DATE/DATETIME/TIME value of the 'a' item. */
a_value= get_datetime_value(0, &a, &a_cache, type, &a_is_null);
a_value= get_datetime_value(*a, type, &a_is_null);
/* Get DATE/DATETIME/TIME value of the 'b' item. */
b_value= get_datetime_value(0, &b, &b_cache, type, &b_is_null);
b_value= get_datetime_value(*b, type, &b_is_null);
return a_is_null || b_is_null ? a_is_null == b_is_null
: a_value == b_value;
}
......@@ -2170,7 +2170,7 @@ void Item_func_between::fix_length_and_dec()
strings as.
*/
if (m_compare_type == TIME_RESULT)
compare_as_dates= find_date_time_item(args, 3, 0);
compare_as_dates= find_date_time_item(thd, args, 3, 0, false);
/* See the comment for Item_func::convert_const_compared_to_int_field */
if (args[0]->real_item()->type() == FIELD_ITEM &&
......@@ -2196,29 +2196,17 @@ longlong Item_func_between::val_int()
switch (m_compare_type) {
case TIME_RESULT:
{
THD *thd= current_thd;
longlong value, a, b;
Item *cache, **ptr;
bool value_is_null, a_is_null, b_is_null;
ptr= &args[0];
enum_field_types f_type= field_type_for_temporal_comparison(compare_as_dates);
value= get_datetime_value(thd, &ptr, &cache, f_type, &value_is_null);
if (ptr != &args[0])
thd->change_item_tree(&args[0], *ptr);
value= get_datetime_value(args[0], f_type, &value_is_null);
if ((null_value= value_is_null))
return 0;
ptr= &args[1];
a= get_datetime_value(thd, &ptr, &cache, f_type, &a_is_null);
if (ptr != &args[1])
thd->change_item_tree(&args[1], *ptr);
ptr= &args[2];
b= get_datetime_value(thd, &ptr, &cache, f_type, &b_is_null);
if (ptr != &args[2])
thd->change_item_tree(&args[2], *ptr);
a= get_datetime_value(args[1], f_type, &a_is_null);
b= get_datetime_value(args[2], f_type, &b_is_null);
if (!a_is_null && !b_is_null)
return (longlong) ((value >= a && value <= b) != negated);
......@@ -3248,7 +3236,7 @@ void Item_func_case::fix_length_and_dec()
Item *date_arg= 0;
if (m_found_types & (1U << TIME_RESULT))
date_arg= find_date_time_item(args, nwhens + 1, 0);
date_arg= find_date_time_item(current_thd, args, nwhens + 1, 0, true);
if (m_found_types & (1U << STRING_RESULT))
{
......@@ -3790,10 +3778,8 @@ void in_datetime::set(uint pos,Item *item)
uchar *in_datetime::get_value(Item *item)
{
bool is_null;
Item **tmp_item= lval_cache ? &lval_cache : &item;
enum_field_types f_type=
tmp_item[0]->field_type_for_temporal_comparison(warn_item);
tmp.val= get_datetime_value(0, &tmp_item, &lval_cache, f_type, &is_null);
enum_field_types f_type= item->field_type_for_temporal_comparison(warn_item);
tmp.val= get_datetime_value(item, f_type, &is_null);
if (item->null_value)
return 0;
tmp.unsigned_flag= 1L;
......@@ -4055,10 +4041,8 @@ cmp_item* cmp_item_decimal::make_same()
void cmp_item_datetime::store_value(Item *item)
{
bool is_null;
Item **tmp_item= lval_cache ? &lval_cache : &item;
enum_field_types f_type=
tmp_item[0]->field_type_for_temporal_comparison(warn_item);
value= get_datetime_value(0, &tmp_item, &lval_cache, f_type, &is_null);
enum_field_types f_type= item->field_type_for_temporal_comparison(warn_item);
value= get_datetime_value(item, f_type, &is_null);
m_null_value= item->null_value;
}
......@@ -4263,7 +4247,7 @@ void Item_func_in::fix_length_and_dec()
for (uint col= 0; col < cols; col++)
{
date_arg= find_date_time_item(args, arg_count, col);
date_arg= find_date_time_item(thd, args, arg_count, col, true);
if (date_arg)
{
cmp_item **cmp= 0;
......@@ -4329,7 +4313,7 @@ void Item_func_in::fix_length_and_dec()
array= new (thd->mem_root) in_decimal(thd, arg_count - 1);
break;
case TIME_RESULT:
date_arg= find_date_time_item(args, arg_count, 0);
date_arg= find_date_time_item(thd, args, arg_count, 0, true);
array= new (thd->mem_root) in_datetime(thd, date_arg, arg_count - 1);
break;
}
......@@ -4356,7 +4340,7 @@ void Item_func_in::fix_length_and_dec()
else
{
if (found_types & (1U << TIME_RESULT))
date_arg= find_date_time_item(args, arg_count, 0);
date_arg= find_date_time_item(thd, args, arg_count, 0, true);
if (found_types & (1U << STRING_RESULT) &&
agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
return;
......
......@@ -1267,19 +1267,15 @@ class in_longlong :public in_vector
/*
Class to represent a vector of constant DATE/DATETIME values.
Values are obtained with help of the get_datetime_value() function.
If the left item is a constant one then its value is cached in the
lval_cache variable.
*/
class in_datetime :public in_longlong
{
public:
/* An item used to issue warnings. */
Item *warn_item;
/* Cache for the left item. */
Item *lval_cache;
in_datetime(THD *thd, Item *warn_item_arg, uint elements)
:in_longlong(thd, elements), warn_item(warn_item_arg), lval_cache(0) {};
:in_longlong(thd, elements), warn_item(warn_item_arg) {}
void set(uint pos,Item *item);
uchar *get_value(Item *item);
Item *create_item(THD *thd);
......@@ -1441,8 +1437,6 @@ class cmp_item_int : public cmp_item_scalar
/*
Compare items in the DATETIME context.
Values are obtained with help of the get_datetime_value() function.
If the left item is a constant one then its value is cached in the
lval_cache variable.
*/
class cmp_item_datetime : public cmp_item_scalar
{
......@@ -1450,11 +1444,9 @@ class cmp_item_datetime : public cmp_item_scalar
public:
/* Item used for issuing warnings. */
Item *warn_item;
/* Cache for the left item. */
Item *lval_cache;
cmp_item_datetime(Item *warn_item_arg)
: warn_item(warn_item_arg), lval_cache(0) {}
: warn_item(warn_item_arg) {}
void store_value(Item *item);
int cmp(Item *arg);
int compare(cmp_item *ci);
......@@ -2622,7 +2614,7 @@ inline bool is_cond_or(Item *item)
Item *and_expressions(Item *a, Item *b, Item **org_item);
longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
longlong get_datetime_value(Item ***item_arg, Item **cache_arg,
enum_field_types f_type, bool *is_null);
......
......@@ -25,6 +25,6 @@ id title average max
SELECT * FROM running_records
WHERE average BETWEEN "-838:59:59.000000" AND "01:00:00.000001";
id title average max
3 record failure -838:59:59.000000 -838:59:59.000000
1 normal condition 01:00:00.000001 01:05:00.000001
3 record failure -838:59:59.000000 -838:59:59.000000
DROP TABLE running_records;
......@@ -25,6 +25,6 @@ id title average max
SELECT * FROM running_records
WHERE average BETWEEN "-838:59:59" AND "01:00:00";
id title average max
3 record failure -838:59:59 -838:59:59
1 normal condition 01:00:00 01:05:00
3 record failure -838:59:59 -838:59:59
DROP TABLE running_records;
......@@ -2080,7 +2080,7 @@ explain
select kp1,kp2 from t1 force index (kp1)
where kp1 between '09:01:00' and '09:05:00';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range kp1 kp1 4 NULL # Using where; Using index
1 SIMPLE t1 index kp1 kp1 9 NULL # Using where; Using index
select kp1,kp2 from t1 force index (kp1)
where kp1 between '09:01:00' and '09:05:00';
kp1 kp2
......@@ -2103,7 +2103,7 @@ explain
select kp1,kp2 from t2 force index (kp1)
where kp1 between '09:01:00' and '09:05:00';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 range kp1 kp1 3 NULL # Using where; Using index
1 SIMPLE t2 index kp1 kp1 8 NULL # Using where; Using index
select kp1,kp2 from t2 force index (kp1)
where kp1 between '09:01:00' and '09:05:00';
kp1 kp2
......
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