Commit 969f4918 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-7005 NULLIF does not work as documented

MDEV-7146 NULLIF returns unexpected result with a YEAR field
parent 9f4abde6
......@@ -2900,7 +2900,7 @@ SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`d` date DEFAULT NULL,
`bad` varbinary(10) DEFAULT NULL
`bad` date DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1, t2;
SET NAMES latin1;
......
......@@ -3292,7 +3292,7 @@ SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`d` date DEFAULT NULL,
`bad` varchar(10) CHARACTER SET cp1251 DEFAULT NULL
`bad` date DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1, t2;
SET NAMES latin1;
......
......@@ -3576,7 +3576,7 @@ SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`d` date DEFAULT NULL,
`bad` varchar(10) DEFAULT NULL
`bad` date DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1, t2;
SET NAMES latin1;
......
......@@ -4483,7 +4483,7 @@ SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`d` date DEFAULT NULL,
`bad` varchar(10) CHARACTER SET ucs2 DEFAULT NULL
`bad` date DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1, t2;
SET NAMES latin1;
......
......@@ -5351,7 +5351,7 @@ SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`d` date DEFAULT NULL,
`bad` varchar(10) CHARACTER SET utf8 DEFAULT NULL
`bad` date DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1, t2;
SET NAMES latin1;
......
This diff is collapsed.
This diff is collapsed.
......@@ -525,6 +525,32 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
}
/*
Make a special case of compare with fields to get nicer comparisons
of bigint numbers with constant string.
This directly contradicts the manual (number and a string should
be compared as doubles), but seems to provide more
"intuitive" behavior in some cases (but less intuitive in others).
*/
void Item_func::convert_const_compared_to_int_field(THD *thd)
{
DBUG_ASSERT(arg_count == 2);
if (!thd->lex->is_ps_or_view_context_analysis())
{
int field;
if (args[field= 0]->real_item()->type() == FIELD_ITEM ||
args[field= 1]->real_item()->type() == FIELD_ITEM)
{
Item_field *field_item= (Item_field*) (args[field]->real_item());
if ((field_item->field_type() == MYSQL_TYPE_LONGLONG ||
field_item->field_type() == MYSQL_TYPE_YEAR) &&
convert_const_to_int(thd, field_item, &args[!field]))
args[0]->cmp_context= args[1]->cmp_context= INT_RESULT;
}
}
}
void Item_bool_func2::fix_length_and_dec()
{
max_length= 1; // Function returns 0 or 1
......@@ -557,29 +583,9 @@ void Item_bool_func2::fix_length_and_dec()
args[0]->cmp_context= args[1]->cmp_context=
item_cmp_type(args[0]->result_type(), args[1]->result_type());
/*
Make a special case of compare with fields to get nicer comparisons
of bigint numbers with constant string.
This directly contradicts the manual (number and a string should
be compared as doubles), but seems to provide more
"intuitive" behavior in some cases (but less intuitive in others).
But disable conversion in case of LIKE function.
*/
THD *thd= current_thd;
if (functype() != LIKE_FUNC && !thd->lex->is_ps_or_view_context_analysis())
{
int field;
if (args[field= 0]->real_item()->type() == FIELD_ITEM ||
args[field= 1]->real_item()->type() == FIELD_ITEM)
{
Item_field *field_item= (Item_field*) (args[field]->real_item());
if ((field_item->field_type() == MYSQL_TYPE_LONGLONG ||
field_item->field_type() == MYSQL_TYPE_YEAR) &&
convert_const_to_int(thd, field_item, &args[!field]))
args[0]->cmp_context= args[1]->cmp_context= INT_RESULT;
}
}
// Convert constants when compared to int/year field, unless this is LIKE
if (functype() != LIKE_FUNC)
convert_const_compared_to_int_field(current_thd);
set_cmp_func();
}
......@@ -2723,18 +2729,21 @@ bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate)
void
Item_func_nullif::fix_length_and_dec()
{
Item_bool_func2::fix_length_and_dec();
if (!args[0]) // Only false if EOM
return;
cached_result_type= args[0]->result_type();
cached_field_type= args[0]->field_type();
collation.set(args[0]->collation);
decimals= args[0]->decimals;
unsigned_flag= args[0]->unsigned_flag;
fix_char_length(args[0]->max_char_length());
convert_const_compared_to_int_field(current_thd);
args[0]->cmp_context= args[1]->cmp_context=
item_cmp_type(args[0]->result_type(), args[1]->result_type());
cmp.set_cmp_func(this, tmp_arg, tmp_arg + 1, args[0]->cmp_context);
maybe_null=1;
if (args[0]) // Only false if EOM
{
decimals=args[0]->decimals;
unsigned_flag= args[0]->unsigned_flag;
cached_result_type= args[0]->result_type();
if (cached_result_type == STRING_RESULT &&
agg_arg_charsets_for_comparison(collation, args, arg_count))
return;
fix_char_length(args[0]->max_char_length());
}
}
......@@ -2749,7 +2758,7 @@ Item_func_nullif::fix_length_and_dec()
*/
double
Item_func_nullif::val_real()
Item_func_nullif::real_op()
{
DBUG_ASSERT(fixed == 1);
double value;
......@@ -2758,13 +2767,13 @@ Item_func_nullif::val_real()
null_value=1;
return 0.0;
}
value= args[0]->val_real();
null_value=args[0]->null_value;
value= m_args0_copy->val_real();
null_value=m_args0_copy->null_value;
return value;
}
longlong
Item_func_nullif::val_int()
Item_func_nullif::int_op()
{
DBUG_ASSERT(fixed == 1);
longlong value;
......@@ -2773,13 +2782,13 @@ Item_func_nullif::val_int()
null_value=1;
return 0;
}
value=args[0]->val_int();
null_value=args[0]->null_value;
value=m_args0_copy->val_int();
null_value=m_args0_copy->null_value;
return value;
}
String *
Item_func_nullif::val_str(String *str)
Item_func_nullif::str_op(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res;
......@@ -2788,14 +2797,14 @@ Item_func_nullif::val_str(String *str)
null_value=1;
return 0;
}
res=args[0]->val_str(str);
null_value=args[0]->null_value;
res=m_args0_copy->val_str(str);
null_value=m_args0_copy->null_value;
return res;
}
my_decimal *
Item_func_nullif::val_decimal(my_decimal * decimal_value)
Item_func_nullif::decimal_op(my_decimal * decimal_value)
{
DBUG_ASSERT(fixed == 1);
my_decimal *res;
......@@ -2804,16 +2813,26 @@ Item_func_nullif::val_decimal(my_decimal * decimal_value)
null_value=1;
return 0;
}
res= args[0]->val_decimal(decimal_value);
null_value= args[0]->null_value;
res= m_args0_copy->val_decimal(decimal_value);
null_value= m_args0_copy->null_value;
return res;
}
bool
Item_func_nullif::date_op(MYSQL_TIME *ltime, uint fuzzydate)
{
DBUG_ASSERT(fixed == 1);
if (!cmp.compare())
return (null_value= true);
return (null_value= m_args0_copy->get_date(ltime, fuzzydate));
}
bool
Item_func_nullif::is_null()
{
return (null_value= (!cmp.compare() ? 1 : args[0]->null_value));
return (null_value= (!cmp.compare() ? 1 : m_args0_copy->null_value));
}
......
......@@ -822,20 +822,30 @@ class Item_func_if :public Item_func_case_abbreviation2
};
class Item_func_nullif :public Item_bool_func2
class Item_func_nullif :public Item_func_hybrid_field_type
{
enum Item_result cached_result_type;
Arg_comparator cmp;
/*
Remember the first argument in case it will be substituted by either of:
- convert_const_compared_to_int_field()
- agg_item_set_converter() in set_cmp_func()
- cache_converted_constant() in set_cmp_func()
The original item will be stored in m_arg0_copy, to return result.
The substituted item will be stored in args[0], for comparison purposes.
*/
Item *m_args0_copy;
public:
Item_func_nullif(Item *a,Item *b)
:Item_bool_func2(a,b), cached_result_type(INT_RESULT)
:Item_func_hybrid_field_type(a, b),
m_args0_copy(a)
{}
double val_real();
longlong val_int();
String *val_str(String *str);
my_decimal *val_decimal(my_decimal *);
enum Item_result result_type () const { return cached_result_type; }
bool date_op(MYSQL_TIME *ltime, uint fuzzydate);
double real_op();
longlong int_op();
String *str_op(String *str);
my_decimal *decimal_op(my_decimal *);
void fix_length_and_dec();
uint decimal_precision() const { return args[0]->decimal_precision(); }
uint decimal_precision() const { return m_args0_copy->decimal_precision(); }
const char *func_name() const { return "nullif"; }
virtual inline void print(String *str, enum_query_type query_type)
......
......@@ -396,6 +396,7 @@ class Item_func :public Item_result_field
info.bool_function= &Item::restore_to_before_no_rows_in_result;
walk(&Item::call_bool_func_processor, FALSE, (uchar*) &info);
}
void convert_const_compared_to_int_field(THD *thd);
};
......
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