MDEV-8663: IF Statement returns multiple values erroneously (or Assertion `!null_value' failed in Item::send(Protocol*, String*))
Postreview addons by Bar Fix: keeping contract: NULL value mean NULL pointer in val_str and val_deciman.
This commit is contained in:
parent
fa51f70dc6
commit
102a85f9f3
@ -234,3 +234,20 @@ SELECT if(1, NULL, (SELECT min('hello')));
|
|||||||
if(1, NULL, (SELECT min('hello')))
|
if(1, NULL, (SELECT min('hello')))
|
||||||
NULL
|
NULL
|
||||||
End of 5.2 tests
|
End of 5.2 tests
|
||||||
|
#
|
||||||
|
# MDEV-8663: IF Statement returns multiple values erroneously
|
||||||
|
# (or Assertion `!null_value' failed in Item::send(Protocol*, String*)
|
||||||
|
#
|
||||||
|
CREATE TABLE `t1` (
|
||||||
|
`datas` VARCHAR(25) NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
Warnings:
|
||||||
|
Warning 1286 Unknown storage engine 'InnoDB'
|
||||||
|
Warning 1266 Using storage engine MyISAM for table 't1'
|
||||||
|
INSERT INTO `t1` VALUES ('1,2'), ('2,3'), ('3,4');
|
||||||
|
SELECT IF(FIND_IN_SET('1', `datas`), 1.5, IF(FIND_IN_SET('2', `datas`), 2, NULL)) AS `First`, '1' AS `Second`, '2' AS `Third` FROM `t1`;
|
||||||
|
First Second Third
|
||||||
|
1.5 1 2
|
||||||
|
2.0 1 2
|
||||||
|
NULL 1 2
|
||||||
|
drop table t1;
|
||||||
|
@ -206,6 +206,20 @@ SELECT if(1, NULL, (SELECT min('hello')));
|
|||||||
|
|
||||||
--echo End of 5.2 tests
|
--echo End of 5.2 tests
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-8663: IF Statement returns multiple values erroneously
|
||||||
|
--echo # (or Assertion `!null_value' failed in Item::send(Protocol*, String*)
|
||||||
|
--echo #
|
||||||
|
CREATE TABLE `t1` (
|
||||||
|
`datas` VARCHAR(25) NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
INSERT INTO `t1` VALUES ('1,2'), ('2,3'), ('3,4');
|
||||||
|
|
||||||
|
SELECT IF(FIND_IN_SET('1', `datas`), 1.5, IF(FIND_IN_SET('2', `datas`), 2, NULL)) AS `First`, '1' AS `Second`, '2' AS `Third` FROM `t1`;
|
||||||
|
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
--disable_query_log
|
--disable_query_log
|
||||||
# Restore timezone to default
|
# Restore timezone to default
|
||||||
set time_zone= @@global.time_zone;
|
set time_zone= @@global.time_zone;
|
||||||
|
@ -2692,7 +2692,8 @@ Item_func_if::str_op(String *str)
|
|||||||
String *res=arg->val_str(str);
|
String *res=arg->val_str(str);
|
||||||
if (res)
|
if (res)
|
||||||
res->set_charset(collation.collation);
|
res->set_charset(collation.collation);
|
||||||
null_value=arg->null_value;
|
if (null_value=arg->null_value)
|
||||||
|
res= NULL;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2703,7 +2704,8 @@ Item_func_if::decimal_op(my_decimal *decimal_value)
|
|||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
Item *arg= args[0]->val_bool() ? args[1] : args[2];
|
Item *arg= args[0]->val_bool() ? args[1] : args[2];
|
||||||
my_decimal *value= arg->val_decimal(decimal_value);
|
my_decimal *value= arg->val_decimal(decimal_value);
|
||||||
null_value= arg->null_value;
|
if (null_value= arg->null_value)
|
||||||
|
value= NULL;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,7 +887,7 @@ String *Item_func_hybrid_result_type::val_str(String *str)
|
|||||||
case DECIMAL_RESULT:
|
case DECIMAL_RESULT:
|
||||||
{
|
{
|
||||||
my_decimal decimal_value, *val;
|
my_decimal decimal_value, *val;
|
||||||
if (!(val= decimal_op(&decimal_value)))
|
if (!(val= decimal_op_with_null_check(&decimal_value)))
|
||||||
return 0; // null is set
|
return 0; // null is set
|
||||||
my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val);
|
my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val);
|
||||||
str->set_charset(collation.collation);
|
str->set_charset(collation.collation);
|
||||||
@ -914,24 +914,22 @@ String *Item_func_hybrid_result_type::val_str(String *str)
|
|||||||
if (is_temporal_type(field_type()))
|
if (is_temporal_type(field_type()))
|
||||||
{
|
{
|
||||||
MYSQL_TIME ltime;
|
MYSQL_TIME ltime;
|
||||||
if (date_op(<ime,
|
if (date_op_with_null_check(<ime) ||
|
||||||
field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0) ||
|
(null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH)))
|
||||||
str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
|
||||||
{
|
|
||||||
null_value= 1;
|
|
||||||
return (String *) 0;
|
return (String *) 0;
|
||||||
}
|
|
||||||
ltime.time_type= mysql_type_to_time_type(field_type());
|
ltime.time_type= mysql_type_to_time_type(field_type());
|
||||||
str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals));
|
str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals));
|
||||||
str->set_charset(&my_charset_bin);
|
str->set_charset(&my_charset_bin);
|
||||||
|
DBUG_ASSERT(!null_value);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
return str_op(&str_value);
|
return str_op_with_null_check(&str_value);
|
||||||
case TIME_RESULT:
|
case TIME_RESULT:
|
||||||
case ROW_RESULT:
|
case ROW_RESULT:
|
||||||
case IMPOSSIBLE_RESULT:
|
case IMPOSSIBLE_RESULT:
|
||||||
DBUG_ASSERT(0);
|
DBUG_ASSERT(0);
|
||||||
}
|
}
|
||||||
|
DBUG_ASSERT(!null_value || (str == NULL));
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -944,7 +942,7 @@ double Item_func_hybrid_result_type::val_real()
|
|||||||
{
|
{
|
||||||
my_decimal decimal_value, *val;
|
my_decimal decimal_value, *val;
|
||||||
double result;
|
double result;
|
||||||
if (!(val= decimal_op(&decimal_value)))
|
if (!(val= decimal_op_with_null_check(&decimal_value)))
|
||||||
return 0.0; // null is set
|
return 0.0; // null is set
|
||||||
my_decimal2double(E_DEC_FATAL_ERROR, val, &result);
|
my_decimal2double(E_DEC_FATAL_ERROR, val, &result);
|
||||||
return result;
|
return result;
|
||||||
@ -961,18 +959,14 @@ double Item_func_hybrid_result_type::val_real()
|
|||||||
if (is_temporal_type(field_type()))
|
if (is_temporal_type(field_type()))
|
||||||
{
|
{
|
||||||
MYSQL_TIME ltime;
|
MYSQL_TIME ltime;
|
||||||
if (date_op(<ime,
|
if (date_op_with_null_check(<ime))
|
||||||
field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0 ))
|
|
||||||
{
|
|
||||||
null_value= 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
ltime.time_type= mysql_type_to_time_type(field_type());
|
ltime.time_type= mysql_type_to_time_type(field_type());
|
||||||
return TIME_to_double(<ime);
|
return TIME_to_double(<ime);
|
||||||
}
|
}
|
||||||
char *end_not_used;
|
char *end_not_used;
|
||||||
int err_not_used;
|
int err_not_used;
|
||||||
String *res= str_op(&str_value);
|
String *res= str_op_with_null_check(&str_value);
|
||||||
return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
|
return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
|
||||||
&end_not_used, &err_not_used) : 0.0);
|
&end_not_used, &err_not_used) : 0.0);
|
||||||
}
|
}
|
||||||
@ -992,7 +986,7 @@ longlong Item_func_hybrid_result_type::val_int()
|
|||||||
case DECIMAL_RESULT:
|
case DECIMAL_RESULT:
|
||||||
{
|
{
|
||||||
my_decimal decimal_value, *val;
|
my_decimal decimal_value, *val;
|
||||||
if (!(val= decimal_op(&decimal_value)))
|
if (!(val= decimal_op_with_null_check(&decimal_value)))
|
||||||
return 0; // null is set
|
return 0; // null is set
|
||||||
longlong result;
|
longlong result;
|
||||||
my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result);
|
my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result);
|
||||||
@ -1007,18 +1001,14 @@ longlong Item_func_hybrid_result_type::val_int()
|
|||||||
if (is_temporal_type(field_type()))
|
if (is_temporal_type(field_type()))
|
||||||
{
|
{
|
||||||
MYSQL_TIME ltime;
|
MYSQL_TIME ltime;
|
||||||
if (date_op(<ime,
|
if (date_op_with_null_check(<ime))
|
||||||
field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0))
|
|
||||||
{
|
|
||||||
null_value= 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
ltime.time_type= mysql_type_to_time_type(field_type());
|
ltime.time_type= mysql_type_to_time_type(field_type());
|
||||||
return TIME_to_ulonglong(<ime);
|
return TIME_to_ulonglong(<ime);
|
||||||
}
|
}
|
||||||
int err_not_used;
|
int err_not_used;
|
||||||
String *res;
|
String *res;
|
||||||
if (!(res= str_op(&str_value)))
|
if (!(res= str_op_with_null_check(&str_value)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
char *end= (char*) res->ptr() + res->length();
|
char *end= (char*) res->ptr() + res->length();
|
||||||
@ -1040,17 +1030,21 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value)
|
|||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
switch (cached_result_type) {
|
switch (cached_result_type) {
|
||||||
case DECIMAL_RESULT:
|
case DECIMAL_RESULT:
|
||||||
val= decimal_op(decimal_value);
|
val= decimal_op_with_null_check(decimal_value);
|
||||||
break;
|
break;
|
||||||
case INT_RESULT:
|
case INT_RESULT:
|
||||||
{
|
{
|
||||||
longlong result= int_op();
|
longlong result= int_op();
|
||||||
|
if (null_value)
|
||||||
|
return NULL;
|
||||||
int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, decimal_value);
|
int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, decimal_value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case REAL_RESULT:
|
case REAL_RESULT:
|
||||||
{
|
{
|
||||||
double result= (double)real_op();
|
double result= (double)real_op();
|
||||||
|
if (null_value)
|
||||||
|
return NULL;
|
||||||
double2my_decimal(E_DEC_FATAL_ERROR, result, decimal_value);
|
double2my_decimal(E_DEC_FATAL_ERROR, result, decimal_value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1059,19 +1053,20 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value)
|
|||||||
if (is_temporal_type(field_type()))
|
if (is_temporal_type(field_type()))
|
||||||
{
|
{
|
||||||
MYSQL_TIME ltime;
|
MYSQL_TIME ltime;
|
||||||
if (date_op(<ime,
|
if (date_op_with_null_check(<ime))
|
||||||
field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0))
|
|
||||||
{
|
{
|
||||||
my_decimal_set_zero(decimal_value);
|
my_decimal_set_zero(decimal_value);
|
||||||
null_value= 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ltime.time_type= mysql_type_to_time_type(field_type());
|
ltime.time_type= mysql_type_to_time_type(field_type());
|
||||||
return date2my_decimal(<ime, decimal_value);
|
return date2my_decimal(<ime, decimal_value);
|
||||||
}
|
}
|
||||||
String *res;
|
String *res;
|
||||||
if (!(res= str_op(&str_value)))
|
if (!(res= str_op_with_null_check(&str_value)))
|
||||||
|
{
|
||||||
|
null_value= 1;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
str2my_decimal(E_DEC_FATAL_ERROR, (char*) res->ptr(),
|
str2my_decimal(E_DEC_FATAL_ERROR, (char*) res->ptr(),
|
||||||
res->length(), res->charset(), decimal_value);
|
res->length(), res->charset(), decimal_value);
|
||||||
@ -1094,7 +1089,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
|
|||||||
case DECIMAL_RESULT:
|
case DECIMAL_RESULT:
|
||||||
{
|
{
|
||||||
my_decimal value, *res;
|
my_decimal value, *res;
|
||||||
if (!(res= decimal_op(&value)) ||
|
if (!(res= decimal_op_with_null_check(&value)) ||
|
||||||
decimal_to_datetime_with_warn(res, ltime, fuzzydate,
|
decimal_to_datetime_with_warn(res, ltime, fuzzydate,
|
||||||
field_name_or_null()))
|
field_name_or_null()))
|
||||||
goto err;
|
goto err;
|
||||||
@ -1124,7 +1119,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
|
|||||||
return date_op(ltime, fuzzydate);
|
return date_op(ltime, fuzzydate);
|
||||||
char buff[40];
|
char buff[40];
|
||||||
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
|
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
|
||||||
if (!(res= str_op(&tmp)) ||
|
if (!(res= str_op_with_null_check(&tmp)) ||
|
||||||
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
|
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
|
||||||
ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
|
ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -411,6 +411,29 @@ public:
|
|||||||
|
|
||||||
class Item_func_hybrid_result_type: public Item_func
|
class Item_func_hybrid_result_type: public Item_func
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
Helper methods to make sure that the result of
|
||||||
|
decimal_op(), str_op() and date_op() is properly synched with null_value.
|
||||||
|
*/
|
||||||
|
bool date_op_with_null_check(MYSQL_TIME *ltime)
|
||||||
|
{
|
||||||
|
bool rc= date_op(ltime,
|
||||||
|
field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0);
|
||||||
|
DBUG_ASSERT(!rc ^ null_value);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
String *str_op_with_null_check(String *str)
|
||||||
|
{
|
||||||
|
String *res= str_op(str);
|
||||||
|
DBUG_ASSERT((res != NULL) ^ null_value);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
my_decimal *decimal_op_with_null_check(my_decimal *decimal_buffer)
|
||||||
|
{
|
||||||
|
my_decimal *res= decimal_op(decimal_buffer);
|
||||||
|
DBUG_ASSERT((res != NULL) ^ null_value);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
protected:
|
protected:
|
||||||
Item_result cached_result_type;
|
Item_result cached_result_type;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user