Commit 102a85f9 authored by Oleksandr Byelkin's avatar Oleksandr Byelkin

MDEV-8663: IF Statement returns multiple values erroneously (or Assertion...

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.
parent fa51f70d
...@@ -234,3 +234,20 @@ SELECT if(1, NULL, (SELECT min('hello'))); ...@@ -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'))); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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(&ltime, if (date_op_with_null_check(&ltime) ||
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(&ltime, const_cast<char*>(str->ptr()), decimals)); str->length(my_TIME_to_str(&ltime, 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() ...@@ -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() ...@@ -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(&ltime, if (date_op_with_null_check(&ltime))
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(&ltime); return TIME_to_double(&ltime);
} }
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() ...@@ -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() ...@@ -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(&ltime, if (date_op_with_null_check(&ltime))
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(&ltime); return TIME_to_ulonglong(&ltime);
} }
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) ...@@ -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) ...@@ -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(&ltime, if (date_op_with_null_check(&ltime))
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(&ltime, decimal_value); return date2my_decimal(&ltime, 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, ...@@ -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, ...@@ -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 @@ class Item_real_func :public Item_func ...@@ -411,6 +411,29 @@ class Item_real_func :public Item_func
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;
......
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