Commit bc43bff7 authored by Evgeny Potemkin's avatar Evgeny Potemkin

Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)

MySQL manual describes values of the YEAR(2) field type as follows:
values 00 - 69 mean 2000 - 2069 years and values 70 - 99 mean 1970 - 1999
years. MIN/MAX and comparison functions was comparing them as int values
thus producing wrong result.

Now the Arg_comparator class is extended with compare_year function which
performs correct comparison of the YEAR type.
The Item_sum_hybrid class now uses Item_cache and Arg_comparator objects to
correctly calculate its value.
To allow Arg_comparator to use func_name() function for Item_func and Item_sum
objects the func_name declaration is moved to the Item_result_field class.
A helper function is_owner_equal_func is added to the Arg_comparator class.
It checks whether the Arg_comparator object owner is the <=> function or not.
A helper function setup is added to the Item_sum_hybrid class. It sets up
cache item and comparator.

mysql-test/r/func_group.result:
  Added a test case for the bug#43668.
mysql-test/t/func_group.test:
  Added a test case for the bug#43668.
sql/item.cc:
  Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
  Now Item_cache_int returns the type of cached item.
sql/item.h:
  Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
  To allow Arg_comparator to use func_name() function for Item_func and Item_sum
  objects the func_name declaration is moved to the Item_result_field class.
sql/item_cmpfunc.cc:
  Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
  The Arg_comparator class is extended with compare_year function which
  performs correct comparison of the YEAR type.
sql/item_cmpfunc.h:
  Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
  The year_as_datetime variable is added to the Arg_comparator class.
  It's set to TRUE when YEAR value should be converted to the
  YYYY-00-00 00:00:00 format for correct YEAR-DATETIME comparison.
sql/item_geofunc.cc:
  Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
  Item_func_spatial_rel::val_int chenged to use Arg_comparator's string
  buffers.
sql/item_subselect.h:
  Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
  Added an implementation of the virtual func_name function.
sql/item_sum.cc:
  Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
  The Item_sum_hybrid class now uses Item_cache and Arg_comparator objects to
  correctly calculate its value.
  A helper function setup is added to the Item_sum_hybrid class. It sets up
  cache item and comparator.
sql/item_sum.h:
  Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
  The Item_sum_hybrid class now uses Item_cache and Arg_comparator objects to
  correctly calculate its value.
  Added an implementation of the virtual func_name function.
parent 61021314
...@@ -885,7 +885,7 @@ cast(sum(distinct df) as signed) ...@@ -885,7 +885,7 @@ cast(sum(distinct df) as signed)
3 3
select cast(min(df) as signed) from t1; select cast(min(df) as signed) from t1;
cast(min(df) as signed) cast(min(df) as signed)
0 1
select 1e8 * sum(distinct df) from t1; select 1e8 * sum(distinct df) from t1;
1e8 * sum(distinct df) 1e8 * sum(distinct df)
330000000 330000000
...@@ -1477,3 +1477,196 @@ COUNT(*) ...@@ -1477,3 +1477,196 @@ COUNT(*)
SET SQL_MODE=default; SET SQL_MODE=default;
DROP TABLE t1; DROP TABLE t1;
End of 5.0 tests End of 5.0 tests
#
# Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
#
create table t1 (f1 year(2), f2 year(4), f3 date, f4 datetime);
insert into t1 values
(98,1998,19980101,"1998-01-01 00:00:00"),
(00,2000,20000101,"2000-01-01 00:00:01"),
(02,2002,20020101,"2002-01-01 23:59:59"),
(60,2060,20600101,"2060-01-01 11:11:11"),
(70,1970,19700101,"1970-11-11 22:22:22"),
(NULL,NULL,NULL,NULL);
select min(f1),max(f1) from t1;
min(f1) max(f1)
70 60
select min(f2),max(f2) from t1;
min(f2) max(f2)
1970 2060
select min(f3),max(f3) from t1;
min(f3) max(f3)
1970-01-01 2060-01-01
select min(f4),max(f4) from t1;
min(f4) max(f4)
1970-11-11 22:22:22 2060-01-01 11:11:11
select a.f1 as a, b.f1 as b, a.f1 > b.f1 as gt,
a.f1 < b.f1 as lt, a.f1<=>b.f1 as eq
from t1 a, t1 b;
a b gt lt eq
98 98 0 0 1
00 98 1 0 0
02 98 1 0 0
60 98 1 0 0
70 98 0 1 0
NULL 98 NULL NULL 0
98 00 0 1 0
00 00 0 0 1
02 00 1 0 0
60 00 1 0 0
70 00 0 1 0
NULL 00 NULL NULL 0
98 02 0 1 0
00 02 0 1 0
02 02 0 0 1
60 02 1 0 0
70 02 0 1 0
NULL 02 NULL NULL 0
98 60 0 1 0
00 60 0 1 0
02 60 0 1 0
60 60 0 0 1
70 60 0 1 0
NULL 60 NULL NULL 0
98 70 1 0 0
00 70 1 0 0
02 70 1 0 0
60 70 1 0 0
70 70 0 0 1
NULL 70 NULL NULL 0
98 NULL NULL NULL 0
00 NULL NULL NULL 0
02 NULL NULL NULL 0
60 NULL NULL NULL 0
70 NULL NULL NULL 0
NULL NULL NULL NULL 1
select a.f1 as a, b.f2 as b, a.f1 > b.f2 as gt,
a.f1 < b.f2 as lt, a.f1<=>b.f2 as eq
from t1 a, t1 b;
a b gt lt eq
98 1998 0 0 1
00 1998 1 0 0
02 1998 1 0 0
60 1998 1 0 0
70 1998 0 1 0
NULL 1998 NULL NULL 0
98 2000 0 1 0
00 2000 0 0 1
02 2000 1 0 0
60 2000 1 0 0
70 2000 0 1 0
NULL 2000 NULL NULL 0
98 2002 0 1 0
00 2002 0 1 0
02 2002 0 0 1
60 2002 1 0 0
70 2002 0 1 0
NULL 2002 NULL NULL 0
98 2060 0 1 0
00 2060 0 1 0
02 2060 0 1 0
60 2060 0 0 1
70 2060 0 1 0
NULL 2060 NULL NULL 0
98 1970 1 0 0
00 1970 1 0 0
02 1970 1 0 0
60 1970 1 0 0
70 1970 0 0 1
NULL 1970 NULL NULL 0
98 NULL NULL NULL 0
00 NULL NULL NULL 0
02 NULL NULL NULL 0
60 NULL NULL NULL 0
70 NULL NULL NULL 0
NULL NULL NULL NULL 1
select a.f1 as a, b.f3 as b, a.f1 > b.f3 as gt,
a.f1 < b.f3 as lt, a.f1<=>b.f3 as eq
from t1 a, t1 b;
a b gt lt eq
98 1998-01-01 0 1 0
00 1998-01-01 1 0 0
02 1998-01-01 1 0 0
60 1998-01-01 1 0 0
70 1998-01-01 0 1 0
NULL 1998-01-01 NULL NULL 0
98 2000-01-01 0 1 0
00 2000-01-01 0 1 0
02 2000-01-01 1 0 0
60 2000-01-01 1 0 0
70 2000-01-01 0 1 0
NULL 2000-01-01 NULL NULL 0
98 2002-01-01 0 1 0
00 2002-01-01 0 1 0
02 2002-01-01 0 1 0
60 2002-01-01 1 0 0
70 2002-01-01 0 1 0
NULL 2002-01-01 NULL NULL 0
98 2060-01-01 0 1 0
00 2060-01-01 0 1 0
02 2060-01-01 0 1 0
60 2060-01-01 0 1 0
70 2060-01-01 0 1 0
NULL 2060-01-01 NULL NULL 0
98 1970-01-01 1 0 0
00 1970-01-01 1 0 0
02 1970-01-01 1 0 0
60 1970-01-01 1 0 0
70 1970-01-01 0 1 0
NULL 1970-01-01 NULL NULL 0
98 NULL NULL NULL 0
00 NULL NULL NULL 0
02 NULL NULL NULL 0
60 NULL NULL NULL 0
70 NULL NULL NULL 0
NULL NULL NULL NULL 1
select a.f1 as a, b.f4 as b, a.f1 > b.f4 as gt,
a.f1 < b.f4 as lt, a.f1<=>b.f4 as eq
from t1 a, t1 b;
a b gt lt eq
98 1998-01-01 00:00:00 0 1 0
00 1998-01-01 00:00:00 1 0 0
02 1998-01-01 00:00:00 1 0 0
60 1998-01-01 00:00:00 1 0 0
70 1998-01-01 00:00:00 0 1 0
NULL 1998-01-01 00:00:00 NULL NULL 0
98 2000-01-01 00:00:01 0 1 0
00 2000-01-01 00:00:01 0 1 0
02 2000-01-01 00:00:01 1 0 0
60 2000-01-01 00:00:01 1 0 0
70 2000-01-01 00:00:01 0 1 0
NULL 2000-01-01 00:00:01 NULL NULL 0
98 2002-01-01 23:59:59 0 1 0
00 2002-01-01 23:59:59 0 1 0
02 2002-01-01 23:59:59 0 1 0
60 2002-01-01 23:59:59 1 0 0
70 2002-01-01 23:59:59 0 1 0
NULL 2002-01-01 23:59:59 NULL NULL 0
98 2060-01-01 11:11:11 0 1 0
00 2060-01-01 11:11:11 0 1 0
02 2060-01-01 11:11:11 0 1 0
60 2060-01-01 11:11:11 0 1 0
70 2060-01-01 11:11:11 0 1 0
NULL 2060-01-01 11:11:11 NULL NULL 0
98 1970-11-11 22:22:22 1 0 0
00 1970-11-11 22:22:22 1 0 0
02 1970-11-11 22:22:22 1 0 0
60 1970-11-11 22:22:22 1 0 0
70 1970-11-11 22:22:22 0 1 0
NULL 1970-11-11 22:22:22 NULL NULL 0
98 NULL NULL NULL 0
00 NULL NULL NULL 0
02 NULL NULL NULL 0
60 NULL NULL NULL 0
70 NULL NULL NULL 0
NULL NULL NULL NULL 1
select *, f1 = f2 from t1;
f1 f2 f3 f4 f1 = f2
98 1998 1998-01-01 1998-01-01 00:00:00 1
00 2000 2000-01-01 2000-01-01 00:00:01 1
02 2002 2002-01-01 2002-01-01 23:59:59 1
60 2060 2060-01-01 2060-01-01 11:11:11 1
70 1970 1970-01-01 1970-11-11 22:22:22 1
NULL NULL NULL NULL NULL
drop table t1;
#
...@@ -1006,3 +1006,35 @@ DROP TABLE t1; ...@@ -1006,3 +1006,35 @@ DROP TABLE t1;
### ###
--echo End of 5.0 tests --echo End of 5.0 tests
--echo #
--echo # Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
--echo #
create table t1 (f1 year(2), f2 year(4), f3 date, f4 datetime);
insert into t1 values
(98,1998,19980101,"1998-01-01 00:00:00"),
(00,2000,20000101,"2000-01-01 00:00:01"),
(02,2002,20020101,"2002-01-01 23:59:59"),
(60,2060,20600101,"2060-01-01 11:11:11"),
(70,1970,19700101,"1970-11-11 22:22:22"),
(NULL,NULL,NULL,NULL);
select min(f1),max(f1) from t1;
select min(f2),max(f2) from t1;
select min(f3),max(f3) from t1;
select min(f4),max(f4) from t1;
select a.f1 as a, b.f1 as b, a.f1 > b.f1 as gt,
a.f1 < b.f1 as lt, a.f1<=>b.f1 as eq
from t1 a, t1 b;
select a.f1 as a, b.f2 as b, a.f1 > b.f2 as gt,
a.f1 < b.f2 as lt, a.f1<=>b.f2 as eq
from t1 a, t1 b;
select a.f1 as a, b.f3 as b, a.f1 > b.f3 as gt,
a.f1 < b.f3 as lt, a.f1<=>b.f3 as eq
from t1 a, t1 b;
select a.f1 as a, b.f4 as b, a.f1 > b.f4 as gt,
a.f1 < b.f4 as lt, a.f1<=>b.f4 as eq
from t1 a, t1 b;
select *, f1 = f2 from t1;
drop table t1;
--echo #
...@@ -6940,7 +6940,7 @@ Item_cache* Item_cache::get_cache(const Item *item) ...@@ -6940,7 +6940,7 @@ Item_cache* Item_cache::get_cache(const Item *item)
{ {
switch (item->result_type()) { switch (item->result_type()) {
case INT_RESULT: case INT_RESULT:
return new Item_cache_int(); return new Item_cache_int(item->field_type());
case REAL_RESULT: case REAL_RESULT:
return new Item_cache_real(); return new Item_cache_real();
case DECIMAL_RESULT: case DECIMAL_RESULT:
......
...@@ -2135,6 +2135,23 @@ class Item_result_field :public Item /* Item with result field */ ...@@ -2135,6 +2135,23 @@ class Item_result_field :public Item /* Item with result field */
save_in_field(result_field, no_conversions); save_in_field(result_field, no_conversions);
} }
void cleanup(); void cleanup();
/*
This method is used for debug purposes to print the name of an
item to the debug log. The second use of this method is as
a helper function of print() and error messages, where it is
applicable. To suit both goals it should return a meaningful,
distinguishable and sintactically correct string. This method
should not be used for runtime type identification, use enum
{Sum}Functype and Item_func::functype()/Item_sum::sum_func()
instead.
Added here, to the parent class of both Item_func and Item_sum_func.
NOTE: for Items inherited from Item_sum, func_name() return part of
function name till first argument (including '(') to make difference in
names for functions with 'distinct' clause and without 'distinct' and
also to make printing of items inherited from Item_sum uniform.
*/
virtual const char *func_name() const= 0;
}; };
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
#include "sql_select.h" #include "sql_select.h"
static bool convert_constant_item(THD *, Item_field *, Item **); static bool convert_constant_item(THD *, Item_field *, Item **);
static longlong
get_year_value(THD *thd, Item ***item_arg, Item **cache_arg,
Item *warn_item, bool *is_null);
static Item_result item_store_type(Item_result a, Item *item, static Item_result item_store_type(Item_result a, Item *item,
my_bool unsigned_flag) my_bool unsigned_flag)
...@@ -533,11 +536,12 @@ void Item_bool_func2::fix_length_and_dec() ...@@ -533,11 +536,12 @@ void Item_bool_func2::fix_length_and_dec()
} }
int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
{ {
owner= item; owner= item;
func= comparator_matrix[type] func= comparator_matrix[type]
[test(owner->functype() == Item_func::EQUAL_FUNC)]; [is_owner_equal_func()];
switch (type) { switch (type) {
case ROW_RESULT: case ROW_RESULT:
{ {
...@@ -557,7 +561,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) ...@@ -557,7 +561,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols()); my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols());
return 1; return 1;
} }
if (comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i))) if (comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i),
set_null))
return 1; return 1;
} }
break; break;
...@@ -571,7 +576,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) ...@@ -571,7 +576,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
if (cmp_collation.set((*a)->collation, (*b)->collation) || if (cmp_collation.set((*a)->collation, (*b)->collation) ||
cmp_collation.derivation == DERIVATION_NONE) cmp_collation.derivation == DERIVATION_NONE)
{ {
my_coll_agg_error((*a)->collation, (*b)->collation, owner->func_name()); my_coll_agg_error((*a)->collation, (*b)->collation,
owner->func_name());
return 1; return 1;
} }
if (cmp_collation.collation == &my_charset_bin) if (cmp_collation.collation == &my_charset_bin)
...@@ -849,7 +855,7 @@ get_time_value(THD *thd, Item ***item_arg, Item **cache_arg, ...@@ -849,7 +855,7 @@ get_time_value(THD *thd, Item ***item_arg, Item **cache_arg,
} }
int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
Item **a1, Item **a2, Item **a1, Item **a2,
Item_result type) Item_result type)
{ {
...@@ -857,11 +863,11 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, ...@@ -857,11 +863,11 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg,
ulonglong const_value= (ulonglong)-1; ulonglong const_value= (ulonglong)-1;
a= a1; a= a1;
b= a2; b= a2;
owner= owner_arg;
thd= current_thd;
if ((cmp_type= can_compare_as_dates(*a, *b, &const_value))) if ((cmp_type= can_compare_as_dates(*a, *b, &const_value)))
{ {
thd= current_thd;
owner= owner_arg;
a_type= (*a)->field_type(); a_type= (*a)->field_type();
b_type= (*b)->field_type(); b_type= (*b)->field_type();
a_cache= 0; a_cache= 0;
...@@ -885,22 +891,22 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, ...@@ -885,22 +891,22 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg,
b= (Item **)&b_cache; b= (Item **)&b_cache;
} }
} }
is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC); is_nulls_eq= is_owner_equal_func();
func= &Arg_comparator::compare_datetime; func= &Arg_comparator::compare_datetime;
get_value_func= &get_datetime_value; get_value_a_func= &get_datetime_value;
get_value_b_func= &get_datetime_value;
return 0; return 0;
} }
else if (type == STRING_RESULT && (*a)->field_type() == MYSQL_TYPE_TIME && else if (type == STRING_RESULT && (*a)->field_type() == MYSQL_TYPE_TIME &&
(*b)->field_type() == MYSQL_TYPE_TIME) (*b)->field_type() == MYSQL_TYPE_TIME)
{ {
/* Compare TIME values as integers. */ /* Compare TIME values as integers. */
thd= current_thd;
owner= owner_arg;
a_cache= 0; a_cache= 0;
b_cache= 0; b_cache= 0;
is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC); is_nulls_eq= is_owner_equal_func();
func= &Arg_comparator::compare_datetime; func= &Arg_comparator::compare_datetime;
get_value_func= &get_time_value; get_value_a_func= &get_time_value;
get_value_b_func= &get_time_value;
return 0; return 0;
} }
else if (type == STRING_RESULT && else if (type == STRING_RESULT &&
...@@ -909,20 +915,39 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, ...@@ -909,20 +915,39 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg,
{ {
DTCollation coll; DTCollation coll;
coll.set((*a)->collation.collation); coll.set((*a)->collation.collation);
if (agg_item_set_converter(coll, owner_arg->func_name(), if (agg_item_set_converter(coll, owner->func_name(),
b, 1, MY_COLL_CMP_CONV, 1)) b, 1, MY_COLL_CMP_CONV, 1))
return 1; return 1;
} } else if (type != ROW_RESULT && ((*a)->field_type() == MYSQL_TYPE_YEAR ||
(*b)->field_type() == MYSQL_TYPE_YEAR))
{
is_nulls_eq= is_owner_equal_func();
if ((*a)->is_datetime())
{
year_as_datetime= TRUE;
get_value_a_func= &get_datetime_value;
} else if ((*a)->field_type() == MYSQL_TYPE_YEAR)
get_value_a_func= &get_year_value;
if ((*b)->is_datetime())
{
year_as_datetime= TRUE;
get_value_b_func= &get_datetime_value;
} else if ((*b)->field_type() == MYSQL_TYPE_YEAR)
get_value_b_func= &get_year_value;
func= &Arg_comparator::compare_year;
return 0;
}
return set_compare_func(owner_arg, type); return set_compare_func(owner_arg, type);
} }
void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1) void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg,
Item **a1, Item **b1)
{ {
thd= current_thd; thd= current_thd;
/* A caller will handle null values by itself. */ owner= owner_arg;
owner= NULL;
a= a1; a= a1;
b= b1; b= b1;
a_type= (*a)->field_type(); a_type= (*a)->field_type();
...@@ -931,7 +956,8 @@ void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1) ...@@ -931,7 +956,8 @@ void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1)
b_cache= 0; b_cache= 0;
is_nulls_eq= FALSE; is_nulls_eq= FALSE;
func= &Arg_comparator::compare_datetime; func= &Arg_comparator::compare_datetime;
get_value_func= &get_datetime_value; get_value_a_func= &get_datetime_value;
get_value_b_func= &get_datetime_value;
} }
...@@ -1031,6 +1057,51 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, ...@@ -1031,6 +1057,51 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
return value; return value;
} }
/*
Retrieves YEAR value of 19XX form from given item.
SYNOPSIS
get_year_value()
thd thread handle
item_arg [in/out] item to retrieve YEAR value from
cache_arg [in/out] pointer to place to store the caching item to
warn_item [in] item for issuing the conversion warning
is_null [out] TRUE <=> the item_arg is null
DESCRIPTION
Retrieves the YEAR value of 19XX form from given item for comparison by the
compare_year() function.
RETURN
obtained value
*/
static longlong
get_year_value(THD *thd, Item ***item_arg, Item **cache_arg,
Item *warn_item, bool *is_null)
{
longlong value= 0;
Item *item= **item_arg;
value= item->val_int();
*is_null= item->null_value;
if (*is_null)
return ~(ulonglong) 0;
/*
Coerce value to the 19XX form in order to correctly compare
YEAR(2) & YEAR(4) types.
*/
if (value < 70)
value+= 100;
if (value <= 1900)
value+= 1900;
return value;
}
/* /*
Compare items values as dates. Compare items values as dates.
...@@ -1063,25 +1134,25 @@ int Arg_comparator::compare_datetime() ...@@ -1063,25 +1134,25 @@ int Arg_comparator::compare_datetime()
longlong a_value, b_value; longlong a_value, b_value;
/* Get DATE/DATETIME/TIME value of the 'a' item. */ /* Get DATE/DATETIME/TIME value of the 'a' item. */
a_value= (*get_value_func)(thd, &a, &a_cache, *b, &a_is_null); a_value= (*get_value_a_func)(thd, &a, &a_cache, *b, &a_is_null);
if (!is_nulls_eq && a_is_null) if (!is_nulls_eq && a_is_null)
{ {
if (owner) if (set_null)
owner->null_value= 1; owner->null_value= 1;
return -1; return -1;
} }
/* Get DATE/DATETIME/TIME value of the 'b' item. */ /* Get DATE/DATETIME/TIME value of the 'b' item. */
b_value= (*get_value_func)(thd, &b, &b_cache, *a, &b_is_null); b_value= (*get_value_b_func)(thd, &b, &b_cache, *a, &b_is_null);
if (a_is_null || b_is_null) if (a_is_null || b_is_null)
{ {
if (owner) if (set_null)
owner->null_value= is_nulls_eq ? 0 : 1; owner->null_value= is_nulls_eq ? 0 : 1;
return is_nulls_eq ? (a_is_null == b_is_null) : -1; return is_nulls_eq ? (a_is_null == b_is_null) : -1;
} }
/* Here we have two not-NULL values. */ /* Here we have two not-NULL values. */
if (owner) if (set_null)
owner->null_value= 0; owner->null_value= 0;
/* Compare values. */ /* Compare values. */
...@@ -1094,15 +1165,17 @@ int Arg_comparator::compare_datetime() ...@@ -1094,15 +1165,17 @@ int Arg_comparator::compare_datetime()
int Arg_comparator::compare_string() int Arg_comparator::compare_string()
{ {
String *res1,*res2; String *res1,*res2;
if ((res1= (*a)->val_str(&owner->tmp_value1))) if ((res1= (*a)->val_str(&value1)))
{ {
if ((res2= (*b)->val_str(&owner->tmp_value2))) if ((res2= (*b)->val_str(&value2)))
{ {
owner->null_value= 0; if (set_null)
owner->null_value= 0;
return sortcmp(res1,res2,cmp_collation.collation); return sortcmp(res1,res2,cmp_collation.collation);
} }
} }
owner->null_value= 1; if (set_null)
owner->null_value= 1;
return -1; return -1;
} }
...@@ -1121,18 +1194,20 @@ int Arg_comparator::compare_string() ...@@ -1121,18 +1194,20 @@ int Arg_comparator::compare_string()
int Arg_comparator::compare_binary_string() int Arg_comparator::compare_binary_string()
{ {
String *res1,*res2; String *res1,*res2;
if ((res1= (*a)->val_str(&owner->tmp_value1))) if ((res1= (*a)->val_str(&value1)))
{ {
if ((res2= (*b)->val_str(&owner->tmp_value2))) if ((res2= (*b)->val_str(&value2)))
{ {
owner->null_value= 0; if (set_null)
owner->null_value= 0;
uint res1_length= res1->length(); uint res1_length= res1->length();
uint res2_length= res2->length(); uint res2_length= res2->length();
int cmp= memcmp(res1->ptr(), res2->ptr(), min(res1_length,res2_length)); int cmp= memcmp(res1->ptr(), res2->ptr(), min(res1_length,res2_length));
return cmp ? cmp : (int) (res1_length - res2_length); return cmp ? cmp : (int) (res1_length - res2_length);
} }
} }
owner->null_value= 1; if (set_null)
owner->null_value= 1;
return -1; return -1;
} }
...@@ -1145,8 +1220,8 @@ int Arg_comparator::compare_binary_string() ...@@ -1145,8 +1220,8 @@ int Arg_comparator::compare_binary_string()
int Arg_comparator::compare_e_string() int Arg_comparator::compare_e_string()
{ {
String *res1,*res2; String *res1,*res2;
res1= (*a)->val_str(&owner->tmp_value1); res1= (*a)->val_str(&value1);
res2= (*b)->val_str(&owner->tmp_value2); res2= (*b)->val_str(&value2);
if (!res1 || !res2) if (!res1 || !res2)
return test(res1 == res2); return test(res1 == res2);
return test(sortcmp(res1, res2, cmp_collation.collation) == 0); return test(sortcmp(res1, res2, cmp_collation.collation) == 0);
...@@ -1156,8 +1231,8 @@ int Arg_comparator::compare_e_string() ...@@ -1156,8 +1231,8 @@ int Arg_comparator::compare_e_string()
int Arg_comparator::compare_e_binary_string() int Arg_comparator::compare_e_binary_string()
{ {
String *res1,*res2; String *res1,*res2;
res1= (*a)->val_str(&owner->tmp_value1); res1= (*a)->val_str(&value1);
res2= (*b)->val_str(&owner->tmp_value2); res2= (*b)->val_str(&value2);
if (!res1 || !res2) if (!res1 || !res2)
return test(res1 == res2); return test(res1 == res2);
return test(stringcmp(res1, res2) == 0); return test(stringcmp(res1, res2) == 0);
...@@ -1178,13 +1253,15 @@ int Arg_comparator::compare_real() ...@@ -1178,13 +1253,15 @@ int Arg_comparator::compare_real()
val2= (*b)->val_real(); val2= (*b)->val_real();
if (!(*b)->null_value) if (!(*b)->null_value)
{ {
owner->null_value= 0; if (set_null)
owner->null_value= 0;
if (val1 < val2) return -1; if (val1 < val2) return -1;
if (val1 == val2) return 0; if (val1 == val2) return 0;
return 1; return 1;
} }
} }
owner->null_value= 1; if (set_null)
owner->null_value= 1;
return -1; return -1;
} }
...@@ -1198,11 +1275,13 @@ int Arg_comparator::compare_decimal() ...@@ -1198,11 +1275,13 @@ int Arg_comparator::compare_decimal()
my_decimal *val2= (*b)->val_decimal(&value2); my_decimal *val2= (*b)->val_decimal(&value2);
if (!(*b)->null_value) if (!(*b)->null_value)
{ {
owner->null_value= 0; if (set_null)
owner->null_value= 0;
return my_decimal_cmp(val1, val2); return my_decimal_cmp(val1, val2);
} }
} }
owner->null_value= 1; if (set_null)
owner->null_value= 1;
return -1; return -1;
} }
...@@ -1240,7 +1319,8 @@ int Arg_comparator::compare_real_fixed() ...@@ -1240,7 +1319,8 @@ int Arg_comparator::compare_real_fixed()
val2= (*b)->val_real(); val2= (*b)->val_real();
if (!(*b)->null_value) if (!(*b)->null_value)
{ {
owner->null_value= 0; if (set_null)
owner->null_value= 0;
if (val1 == val2 || fabs(val1 - val2) < precision) if (val1 == val2 || fabs(val1 - val2) < precision)
return 0; return 0;
if (val1 < val2) if (val1 < val2)
...@@ -1248,7 +1328,8 @@ int Arg_comparator::compare_real_fixed() ...@@ -1248,7 +1328,8 @@ int Arg_comparator::compare_real_fixed()
return 1; return 1;
} }
} }
owner->null_value= 1; if (set_null)
owner->null_value= 1;
return -1; return -1;
} }
...@@ -1271,13 +1352,15 @@ int Arg_comparator::compare_int_signed() ...@@ -1271,13 +1352,15 @@ int Arg_comparator::compare_int_signed()
longlong val2= (*b)->val_int(); longlong val2= (*b)->val_int();
if (!(*b)->null_value) if (!(*b)->null_value)
{ {
owner->null_value= 0; if (set_null)
owner->null_value= 0;
if (val1 < val2) return -1; if (val1 < val2) return -1;
if (val1 == val2) return 0; if (val1 == val2) return 0;
return 1; return 1;
} }
} }
owner->null_value= 1; if (set_null)
owner->null_value= 1;
return -1; return -1;
} }
...@@ -1294,13 +1377,15 @@ int Arg_comparator::compare_int_unsigned() ...@@ -1294,13 +1377,15 @@ int Arg_comparator::compare_int_unsigned()
ulonglong val2= (*b)->val_int(); ulonglong val2= (*b)->val_int();
if (!(*b)->null_value) if (!(*b)->null_value)
{ {
owner->null_value= 0; if (set_null)
owner->null_value= 0;
if (val1 < val2) return -1; if (val1 < val2) return -1;
if (val1 == val2) return 0; if (val1 == val2) return 0;
return 1; return 1;
} }
} }
owner->null_value= 1; if (set_null)
owner->null_value= 1;
return -1; return -1;
} }
...@@ -1317,7 +1402,8 @@ int Arg_comparator::compare_int_signed_unsigned() ...@@ -1317,7 +1402,8 @@ int Arg_comparator::compare_int_signed_unsigned()
ulonglong uval2= (ulonglong)(*b)->val_int(); ulonglong uval2= (ulonglong)(*b)->val_int();
if (!(*b)->null_value) if (!(*b)->null_value)
{ {
owner->null_value= 0; if (set_null)
owner->null_value= 0;
if (sval1 < 0 || (ulonglong)sval1 < uval2) if (sval1 < 0 || (ulonglong)sval1 < uval2)
return -1; return -1;
if ((ulonglong)sval1 == uval2) if ((ulonglong)sval1 == uval2)
...@@ -1325,7 +1411,8 @@ int Arg_comparator::compare_int_signed_unsigned() ...@@ -1325,7 +1411,8 @@ int Arg_comparator::compare_int_signed_unsigned()
return 1; return 1;
} }
} }
owner->null_value= 1; if (set_null)
owner->null_value= 1;
return -1; return -1;
} }
...@@ -1342,7 +1429,8 @@ int Arg_comparator::compare_int_unsigned_signed() ...@@ -1342,7 +1429,8 @@ int Arg_comparator::compare_int_unsigned_signed()
longlong sval2= (*b)->val_int(); longlong sval2= (*b)->val_int();
if (!(*b)->null_value) if (!(*b)->null_value)
{ {
owner->null_value= 0; if (set_null)
owner->null_value= 0;
if (sval2 < 0) if (sval2 < 0)
return 1; return 1;
if (uval1 < (ulonglong)sval2) if (uval1 < (ulonglong)sval2)
...@@ -1352,7 +1440,8 @@ int Arg_comparator::compare_int_unsigned_signed() ...@@ -1352,7 +1440,8 @@ int Arg_comparator::compare_int_unsigned_signed()
return 1; return 1;
} }
} }
owner->null_value= 1; if (set_null)
owner->null_value= 1;
return -1; return -1;
} }
...@@ -1388,10 +1477,11 @@ int Arg_comparator::compare_row() ...@@ -1388,10 +1477,11 @@ int Arg_comparator::compare_row()
for (uint i= 0; i<n; i++) for (uint i= 0; i<n; i++)
{ {
res= comparators[i].compare(); res= comparators[i].compare();
if (owner->null_value) /* Aggregate functions don't need special null handling. */
if (owner->null_value && owner->type() == Item::FUNC_ITEM)
{ {
// NULL was compared // NULL was compared
switch (owner->functype()) { switch (((Item_func*)owner)->functype()) {
case Item_func::NE_FUNC: case Item_func::NE_FUNC:
break; // NE never aborts on NULL even if abort_on_null is set break; // NE never aborts on NULL even if abort_on_null is set
case Item_func::LT_FUNC: case Item_func::LT_FUNC:
...@@ -1400,7 +1490,7 @@ int Arg_comparator::compare_row() ...@@ -1400,7 +1490,7 @@ int Arg_comparator::compare_row()
case Item_func::GE_FUNC: case Item_func::GE_FUNC:
return -1; // <, <=, > and >= always fail on NULL return -1; // <, <=, > and >= always fail on NULL
default: // EQ_FUNC default: // EQ_FUNC
if (owner->abort_on_null) if (((Item_bool_func2*)owner)->abort_on_null)
return -1; // We do not need correct NULL returning return -1; // We do not need correct NULL returning
} }
was_null= 1; was_null= 1;
...@@ -1437,6 +1527,67 @@ int Arg_comparator::compare_e_row() ...@@ -1437,6 +1527,67 @@ int Arg_comparator::compare_e_row()
} }
/**
Compare values as YEAR.
@details
Compare items as YEAR for EQUAL_FUNC and for other comparison functions.
The YEAR values of form 19XX are obtained with help of the get_year_value()
function.
If one of arguments is of DATE/DATETIME type its value is obtained
with help of the get_datetime_value function. In this case YEAR values
prior to comparison are converted to the ulonglong YYYY-00-00 00:00:00
DATETIME form.
If an argument type neither YEAR nor DATE/DATEIME then val_int function
is used to obtain value for comparison.
RETURN
If is_nulls_eq is TRUE:
1 if items are equal or both are null
0 otherwise
If is_nulls_eq is FALSE:
-1 a < b
0 a == b or at least one of items is null
1 a > b
See the table:
is_nulls_eq | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
a_is_null | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
b_is_null | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
result | 1 | 0 | 0 |0/1| 0 | 0 | 0 |-1/0/1|
*/
int Arg_comparator::compare_year()
{
bool a_is_null, b_is_null;
ulonglong val1= get_value_a_func ?
(*get_value_a_func)(thd, &a, &a_cache, *b, &a_is_null) :
(*a)->val_int();
ulonglong val2= get_value_b_func ?
(*get_value_b_func)(thd, &b, &b_cache, *a, &b_is_null) :
(*b)->val_int();
if (!(*a)->null_value)
{
if (!(*b)->null_value)
{
if (set_null)
owner->null_value= 0;
/* Convert year to DATETIME of form YYYY-00-00 00:00:00 when necessary. */
if((*a)->field_type() == MYSQL_TYPE_YEAR && year_as_datetime)
val1*= 10000000000LL;
if((*b)->field_type() == MYSQL_TYPE_YEAR && year_as_datetime)
val2*= 10000000000LL;
if (val1 < val2) return is_nulls_eq ? 0 : -1;
if (val1 == val2) return is_nulls_eq ? 1 : 0;
return is_nulls_eq ? 0 : 1;
}
}
if (set_null)
owner->null_value= is_nulls_eq ? 0 : 1;
return (is_nulls_eq && (*a)->null_value == (*b)->null_value) ? 1 : 0;
}
void Item_func_truth::fix_length_and_dec() void Item_func_truth::fix_length_and_dec()
{ {
maybe_null= 0; maybe_null= 0;
...@@ -1711,8 +1862,8 @@ longlong Item_func_lt::val_int() ...@@ -1711,8 +1862,8 @@ longlong Item_func_lt::val_int()
longlong Item_func_strcmp::val_int() longlong Item_func_strcmp::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
String *a=args[0]->val_str(&tmp_value1); String *a=args[0]->val_str(&cmp.value1);
String *b=args[1]->val_str(&tmp_value2); String *b=args[1]->val_str(&cmp.value2);
if (!a || !b) if (!a || !b)
{ {
null_value=1; null_value=1;
...@@ -1995,8 +2146,8 @@ void Item_func_between::fix_length_and_dec() ...@@ -1995,8 +2146,8 @@ void Item_func_between::fix_length_and_dec()
if (compare_as_dates) if (compare_as_dates)
{ {
ge_cmp.set_datetime_cmp_func(args, args + 1); ge_cmp.set_datetime_cmp_func(this, args, args + 1);
le_cmp.set_datetime_cmp_func(args, args + 2); le_cmp.set_datetime_cmp_func(this, args, args + 2);
} }
else if (time_items_found == 3) else if (time_items_found == 3)
{ {
...@@ -4324,13 +4475,13 @@ void Item_func_isnotnull::print(String *str, enum_query_type query_type) ...@@ -4324,13 +4475,13 @@ void Item_func_isnotnull::print(String *str, enum_query_type query_type)
longlong Item_func_like::val_int() longlong Item_func_like::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
String* res = args[0]->val_str(&tmp_value1); String* res = args[0]->val_str(&cmp.value1);
if (args[0]->null_value) if (args[0]->null_value)
{ {
null_value=1; null_value=1;
return 0; return 0;
} }
String* res2 = args[1]->val_str(&tmp_value2); String* res2 = args[1]->val_str(&cmp.value2);
if (args[1]->null_value) if (args[1]->null_value)
{ {
null_value=1; null_value=1;
...@@ -4354,7 +4505,7 @@ Item_func::optimize_type Item_func_like::select_optimize() const ...@@ -4354,7 +4505,7 @@ Item_func::optimize_type Item_func_like::select_optimize() const
{ {
if (args[1]->const_item()) if (args[1]->const_item())
{ {
String* res2= args[1]->val_str((String *)&tmp_value2); String* res2= args[1]->val_str((String *)&cmp.value2);
if (!res2) if (!res2)
return OPTIMIZE_NONE; return OPTIMIZE_NONE;
...@@ -4385,7 +4536,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) ...@@ -4385,7 +4536,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
if (escape_item->const_item()) if (escape_item->const_item())
{ {
/* If we are on execution stage */ /* If we are on execution stage */
String *escape_str= escape_item->val_str(&tmp_value1); String *escape_str= escape_item->val_str(&cmp.value1);
if (escape_str) if (escape_str)
{ {
if (escape_used_in_parsing && ( if (escape_used_in_parsing && (
...@@ -4440,7 +4591,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) ...@@ -4440,7 +4591,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
if (args[1]->const_item() && !use_strnxfrm(collation.collation) && if (args[1]->const_item() && !use_strnxfrm(collation.collation) &&
!(specialflag & SPECIAL_NO_NEW_FUNC)) !(specialflag & SPECIAL_NO_NEW_FUNC))
{ {
String* res2 = args[1]->val_str(&tmp_value2); String* res2 = args[1]->val_str(&cmp.value2);
if (!res2) if (!res2)
return FALSE; // Null argument return FALSE; // Null argument
......
...@@ -32,7 +32,7 @@ class Arg_comparator: public Sql_alloc ...@@ -32,7 +32,7 @@ class Arg_comparator: public Sql_alloc
{ {
Item **a, **b; Item **a, **b;
arg_cmp_func func; arg_cmp_func func;
Item_bool_func2 *owner; Item_result_field *owner;
Arg_comparator *comparators; // used only for compare_row() Arg_comparator *comparators; // used only for compare_row()
double precision; double precision;
/* Fields used in DATE/DATETIME comparison. */ /* Fields used in DATE/DATETIME comparison. */
...@@ -40,30 +40,42 @@ class Arg_comparator: public Sql_alloc ...@@ -40,30 +40,42 @@ class Arg_comparator: public Sql_alloc
enum_field_types a_type, b_type; // Types of a and b items enum_field_types a_type, b_type; // Types of a and b items
Item *a_cache, *b_cache; // Cached values of a and b items Item *a_cache, *b_cache; // Cached values of a and b items
bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC
bool set_null; // TRUE <=> set owner->null_value
// when one of arguments is NULL.
bool year_as_datetime; // TRUE <=> convert YEAR value to
// the YYYY-00-00 00:00:00 DATETIME
// format. See compare_year.
enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE, enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE,
CMP_DATE_WITH_STR, CMP_STR_WITH_DATE }; CMP_DATE_WITH_STR, CMP_STR_WITH_DATE };
longlong (*get_value_func)(THD *thd, Item ***item_arg, Item **cache_arg, longlong (*get_value_a_func)(THD *thd, Item ***item_arg, Item **cache_arg,
Item *warn_item, bool *is_null); Item *warn_item, bool *is_null);
longlong (*get_value_b_func)(THD *thd, Item ***item_arg, Item **cache_arg,
Item *warn_item, bool *is_null);
public: public:
DTCollation cmp_collation; DTCollation cmp_collation;
/* Allow owner function to use string buffers. */
String value1, value2;
Arg_comparator(): thd(0), a_cache(0), b_cache(0) {}; Arg_comparator(): thd(0), a_cache(0), b_cache(0), set_null(0),
year_as_datetime(0), get_value_a_func(0), get_value_b_func(0) {};
Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), thd(0), Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), thd(0),
a_cache(0), b_cache(0) {}; a_cache(0), b_cache(0), set_null(0), year_as_datetime(0),
get_value_a_func(0), get_value_b_func(0) {};
int set_compare_func(Item_bool_func2 *owner, Item_result type); int set_compare_func(Item_result_field *owner, Item_result type);
inline int set_compare_func(Item_bool_func2 *owner_arg) inline int set_compare_func(Item_result_field *owner_arg)
{ {
return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(),
(*b)->result_type())); (*b)->result_type()));
} }
int set_cmp_func(Item_bool_func2 *owner_arg, int set_cmp_func(Item_result_field *owner_arg,
Item **a1, Item **a2, Item **a1, Item **a2,
Item_result type); Item_result type);
inline int set_cmp_func(Item_bool_func2 *owner_arg, inline int set_cmp_func(Item_result_field *owner_arg,
Item **a1, Item **a2) Item **a1, Item **a2, bool set_null_arg)
{ {
set_null= set_null_arg;
return set_cmp_func(owner_arg, a1, a2, return set_cmp_func(owner_arg, a1, a2,
item_cmp_type((*a1)->result_type(), item_cmp_type((*a1)->result_type(),
(*a2)->result_type())); (*a2)->result_type()));
...@@ -89,12 +101,18 @@ class Arg_comparator: public Sql_alloc ...@@ -89,12 +101,18 @@ class Arg_comparator: public Sql_alloc
int compare_real_fixed(); int compare_real_fixed();
int compare_e_real_fixed(); int compare_e_real_fixed();
int compare_datetime(); // compare args[0] & args[1] as DATETIMEs int compare_datetime(); // compare args[0] & args[1] as DATETIMEs
int compare_year();
static enum enum_date_cmp_type can_compare_as_dates(Item *a, Item *b, static enum enum_date_cmp_type can_compare_as_dates(Item *a, Item *b,
ulonglong *const_val_arg); ulonglong *const_val_arg);
void set_datetime_cmp_func(Item **a1, Item **b1); void set_datetime_cmp_func(Item_result_field *owner_arg, Item **a1, Item **b1);
static arg_cmp_func comparator_matrix [5][2]; static arg_cmp_func comparator_matrix [5][2];
inline bool is_owner_equal_func()
{
return (owner->type() == Item::FUNC_ITEM &&
((Item_func*)owner)->functype() == Item_func::EQUAL_FUNC);
}
friend class Item_func; friend class Item_func;
}; };
...@@ -324,7 +342,6 @@ class Item_bool_func2 :public Item_int_func ...@@ -324,7 +342,6 @@ class Item_bool_func2 :public Item_int_func
{ /* Bool with 2 string args */ { /* Bool with 2 string args */
protected: protected:
Arg_comparator cmp; Arg_comparator cmp;
String tmp_value1,tmp_value2;
bool abort_on_null; bool abort_on_null;
public: public:
...@@ -333,7 +350,7 @@ class Item_bool_func2 :public Item_int_func ...@@ -333,7 +350,7 @@ class Item_bool_func2 :public Item_int_func
void fix_length_and_dec(); void fix_length_and_dec();
void set_cmp_func() void set_cmp_func()
{ {
cmp.set_cmp_func(this, tmp_arg, tmp_arg+1); cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE);
} }
optimize_type select_optimize() const { return OPTIMIZE_OP; } optimize_type select_optimize() const { return OPTIMIZE_OP; }
virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; } virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; }
......
...@@ -124,17 +124,6 @@ class Item_func :public Item_result_field ...@@ -124,17 +124,6 @@ class Item_func :public Item_result_field
virtual optimize_type select_optimize() const { return OPTIMIZE_NONE; } virtual optimize_type select_optimize() const { return OPTIMIZE_NONE; }
virtual bool have_rev_func() const { return 0; } virtual bool have_rev_func() const { return 0; }
virtual Item *key_item() const { return args[0]; } virtual Item *key_item() const { return args[0]; }
/*
This method is used for debug purposes to print the name of an
item to the debug log. The second use of this method is as
a helper function of print(), where it is applicable.
To suit both goals it should return a meaningful,
distinguishable and sintactically correct string. This method
should not be used for runtime type identification, use enum
{Sum}Functype and Item_func::functype()/Item_sum::sum_func()
instead.
*/
virtual const char *func_name() const= 0;
virtual bool const_item() const { return const_item_cache; } virtual bool const_item() const { return const_item_cache; }
inline Item **arguments() const { return args; } inline Item **arguments() const { return args; }
void set_arguments(List<Item> &list); void set_arguments(List<Item> &list);
......
...@@ -506,8 +506,8 @@ String *Item_func_spatial_collection::val_str(String *str) ...@@ -506,8 +506,8 @@ String *Item_func_spatial_collection::val_str(String *str)
longlong Item_func_spatial_rel::val_int() longlong Item_func_spatial_rel::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
String *res1= args[0]->val_str(&tmp_value1); String *res1= args[0]->val_str(&cmp.value1);
String *res2= args[1]->val_str(&tmp_value2); String *res2= args[1]->val_str(&cmp.value2);
Geometry_buffer buffer1, buffer2; Geometry_buffer buffer1, buffer2;
Geometry *g1, *g2; Geometry *g1, *g2;
MBR mbr1, mbr2; MBR mbr1, mbr2;
......
...@@ -132,6 +132,7 @@ class Item_subselect :public Item_result_field ...@@ -132,6 +132,7 @@ class Item_subselect :public Item_result_field
@return the SELECT_LEX structure associated with this Item @return the SELECT_LEX structure associated with this Item
*/ */
st_select_lex* get_select_lex(); st_select_lex* get_select_lex();
const char *func_name() const { DBUG_ASSERT(0); return "subselect"; }
friend class select_subselect; friend class select_subselect;
friend class Item_in_optimizer; friend class Item_in_optimizer;
......
...@@ -614,35 +614,6 @@ Item_sum_num::fix_fields(THD *thd, Item **ref) ...@@ -614,35 +614,6 @@ Item_sum_num::fix_fields(THD *thd, Item **ref)
} }
Item_sum_hybrid::Item_sum_hybrid(THD *thd, Item_sum_hybrid *item)
:Item_sum(thd, item), value(item->value), hybrid_type(item->hybrid_type),
hybrid_field_type(item->hybrid_field_type), cmp_sign(item->cmp_sign),
was_values(item->was_values)
{
/* copy results from old value */
switch (hybrid_type) {
case INT_RESULT:
sum_int= item->sum_int;
break;
case DECIMAL_RESULT:
my_decimal2decimal(&item->sum_dec, &sum_dec);
break;
case REAL_RESULT:
sum= item->sum;
break;
case STRING_RESULT:
/*
This can happen with ROLLUP. Note that the value is already
copied at function call.
*/
break;
case ROW_RESULT:
default:
DBUG_ASSERT(0);
}
collation.set(item->collation);
}
bool bool
Item_sum_hybrid::fix_fields(THD *thd, Item **ref) Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
{ {
...@@ -662,15 +633,12 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) ...@@ -662,15 +633,12 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
switch (hybrid_type= item->result_type()) { switch (hybrid_type= item->result_type()) {
case INT_RESULT: case INT_RESULT:
max_length= 20; max_length= 20;
sum_int= 0;
break; break;
case DECIMAL_RESULT: case DECIMAL_RESULT:
max_length= item->max_length; max_length= item->max_length;
my_decimal_set_zero(&sum_dec);
break; break;
case REAL_RESULT: case REAL_RESULT:
max_length= float_length(decimals); max_length= float_length(decimals);
sum= 0.0;
break; break;
case STRING_RESULT: case STRING_RESULT:
max_length= item->max_length; max_length= item->max_length;
...@@ -679,10 +647,10 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) ...@@ -679,10 +647,10 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
default: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
}; };
setup(args[0], NULL);
/* MIN/MAX can return NULL for empty set indepedent of the used column */ /* MIN/MAX can return NULL for empty set indepedent of the used column */
maybe_null= 1; maybe_null= 1;
unsigned_flag=item->unsigned_flag; unsigned_flag=item->unsigned_flag;
collation.set(item->collation);
result_field=0; result_field=0;
null_value=1; null_value=1;
fix_length_and_dec(); fix_length_and_dec();
...@@ -700,6 +668,31 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) ...@@ -700,6 +668,31 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
return FALSE; return FALSE;
} }
/**
MIN/MAX function setup.
@param item argument of MIN/MAX function
@param value_arg calculated value of MIN/MAX function
@details
Setup cache/comparator of MIN/MAX functions. When called by the
copy_or_same function value_arg parameter contains calculated value
of the original MIN/MAX object and it is saved in this object's cache.
*/
void Item_sum_hybrid::setup(Item *item, Item *value_arg)
{
value= Item_cache::get_cache(item);
value->setup(item);
if (value_arg)
value->store(value_arg);
cmp= new Arg_comparator();
cmp->set_cmp_func(this, args, (Item**)&value, FALSE);
collation.set(item->collation);
}
Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table, Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
uint convert_blob_length) uint convert_blob_length)
{ {
...@@ -1591,19 +1584,7 @@ void Item_sum_variance::update_field() ...@@ -1591,19 +1584,7 @@ void Item_sum_variance::update_field()
void Item_sum_hybrid::clear() void Item_sum_hybrid::clear()
{ {
switch (hybrid_type) { value->null_value= 1;
case INT_RESULT:
sum_int= 0;
break;
case DECIMAL_RESULT:
my_decimal_set_zero(&sum_dec);
break;
case REAL_RESULT:
sum= 0.0;
break;
default:
value.length(0);
}
null_value= 1; null_value= 1;
} }
...@@ -1612,30 +1593,7 @@ double Item_sum_hybrid::val_real() ...@@ -1612,30 +1593,7 @@ double Item_sum_hybrid::val_real()
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (null_value) if (null_value)
return 0.0; return 0.0;
switch (hybrid_type) { return value->val_real();
case STRING_RESULT:
{
char *end_not_used;
int err_not_used;
String *res; res=val_str(&str_value);
return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
&end_not_used, &err_not_used) : 0.0);
}
case INT_RESULT:
if (unsigned_flag)
return ulonglong2double(sum_int);
return (double) sum_int;
case DECIMAL_RESULT:
my_decimal2double(E_DEC_FATAL_ERROR, &sum_dec, &sum);
return sum;
case REAL_RESULT:
return sum;
case ROW_RESULT:
default:
// This case should never be choosen
DBUG_ASSERT(0);
return 0;
}
} }
longlong Item_sum_hybrid::val_int() longlong Item_sum_hybrid::val_int()
...@@ -1643,18 +1601,7 @@ longlong Item_sum_hybrid::val_int() ...@@ -1643,18 +1601,7 @@ longlong Item_sum_hybrid::val_int()
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (null_value) if (null_value)
return 0; return 0;
switch (hybrid_type) { return value->val_int();
case INT_RESULT:
return sum_int;
case DECIMAL_RESULT:
{
longlong result;
my_decimal2int(E_DEC_FATAL_ERROR, &sum_dec, unsigned_flag, &result);
return sum_int;
}
default:
return (longlong) rint(Item_sum_hybrid::val_real());
}
} }
...@@ -1663,26 +1610,7 @@ my_decimal *Item_sum_hybrid::val_decimal(my_decimal *val) ...@@ -1663,26 +1610,7 @@ my_decimal *Item_sum_hybrid::val_decimal(my_decimal *val)
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (null_value) if (null_value)
return 0; return 0;
switch (hybrid_type) { return value->val_decimal(val);
case STRING_RESULT:
string2my_decimal(E_DEC_FATAL_ERROR, &value, val);
break;
case REAL_RESULT:
double2my_decimal(E_DEC_FATAL_ERROR, sum, val);
break;
case DECIMAL_RESULT:
val= &sum_dec;
break;
case INT_RESULT:
int2my_decimal(E_DEC_FATAL_ERROR, sum_int, unsigned_flag, val);
break;
case ROW_RESULT:
default:
// This case should never be choosen
DBUG_ASSERT(0);
break;
}
return val; // Keep compiler happy
} }
...@@ -1692,25 +1620,7 @@ Item_sum_hybrid::val_str(String *str) ...@@ -1692,25 +1620,7 @@ Item_sum_hybrid::val_str(String *str)
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (null_value) if (null_value)
return 0; return 0;
switch (hybrid_type) { return value->val_str(str);
case STRING_RESULT:
return &value;
case REAL_RESULT:
str->set_real(sum,decimals, &my_charset_bin);
break;
case DECIMAL_RESULT:
my_decimal2string(E_DEC_FATAL_ERROR, &sum_dec, 0, 0, 0, str);
return str;
case INT_RESULT:
str->set_int(sum_int, unsigned_flag, &my_charset_bin);
break;
case ROW_RESULT:
default:
// This case should never be choosen
DBUG_ASSERT(0);
break;
}
return str; // Keep compiler happy
} }
...@@ -1719,7 +1629,9 @@ void Item_sum_hybrid::cleanup() ...@@ -1719,7 +1629,9 @@ void Item_sum_hybrid::cleanup()
DBUG_ENTER("Item_sum_hybrid::cleanup"); DBUG_ENTER("Item_sum_hybrid::cleanup");
Item_sum::cleanup(); Item_sum::cleanup();
forced_const= FALSE; forced_const= FALSE;
if (cmp)
delete cmp;
cmp= 0;
/* /*
by default it is TRUE to avoid TRUE reporting by by default it is TRUE to avoid TRUE reporting by
Item_func_not_all/Item_func_nop_all if this item was never called. Item_func_not_all/Item_func_nop_all if this item was never called.
...@@ -1740,63 +1652,21 @@ void Item_sum_hybrid::no_rows_in_result() ...@@ -1740,63 +1652,21 @@ void Item_sum_hybrid::no_rows_in_result()
Item *Item_sum_min::copy_or_same(THD* thd) Item *Item_sum_min::copy_or_same(THD* thd)
{ {
return new (thd->mem_root) Item_sum_min(thd, this); Item_sum_min *item= new (thd->mem_root) Item_sum_min(thd, this);
item->setup(args[0], value);
return item;
} }
bool Item_sum_min::add() bool Item_sum_min::add()
{ {
switch (hybrid_type) { /* args[0] < value */
case STRING_RESULT: int res= cmp->compare();
if (!args[0]->null_value &&
(null_value || res < 0))
{ {
String *result=args[0]->val_str(&tmp_value); value->store(args[0]);
if (!args[0]->null_value && null_value= 0;
(null_value || sortcmp(&value,result,collation.collation) > 0))
{
value.copy(*result);
null_value=0;
}
}
break;
case INT_RESULT:
{
longlong nr=args[0]->val_int();
if (!args[0]->null_value && (null_value ||
(unsigned_flag &&
(ulonglong) nr < (ulonglong) sum_int) ||
(!unsigned_flag && nr < sum_int)))
{
sum_int=nr;
null_value=0;
}
}
break;
case DECIMAL_RESULT:
{
my_decimal value_buff, *val= args[0]->val_decimal(&value_buff);
if (!args[0]->null_value &&
(null_value || (my_decimal_cmp(&sum_dec, val) > 0)))
{
my_decimal2decimal(val, &sum_dec);
null_value= 0;
}
}
break;
case REAL_RESULT:
{
double nr= args[0]->val_real();
if (!args[0]->null_value && (null_value || nr < sum))
{
sum=nr;
null_value=0;
}
}
break;
case ROW_RESULT:
default:
// This case should never be choosen
DBUG_ASSERT(0);
break;
} }
return 0; return 0;
} }
...@@ -1804,63 +1674,21 @@ bool Item_sum_min::add() ...@@ -1804,63 +1674,21 @@ bool Item_sum_min::add()
Item *Item_sum_max::copy_or_same(THD* thd) Item *Item_sum_max::copy_or_same(THD* thd)
{ {
return new (thd->mem_root) Item_sum_max(thd, this); Item_sum_max *item= new (thd->mem_root) Item_sum_max(thd, this);
item->setup(args[0], value);
return item;
} }
bool Item_sum_max::add() bool Item_sum_max::add()
{ {
switch (hybrid_type) { /* args[0] > value */
case STRING_RESULT: int res= cmp->compare();
if (!args[0]->null_value &&
(null_value || res > 0))
{ {
String *result=args[0]->val_str(&tmp_value); value->store(args[0]);
if (!args[0]->null_value && null_value= 0;
(null_value || sortcmp(&value,result,collation.collation) < 0))
{
value.copy(*result);
null_value=0;
}
}
break;
case INT_RESULT:
{
longlong nr=args[0]->val_int();
if (!args[0]->null_value && (null_value ||
(unsigned_flag &&
(ulonglong) nr > (ulonglong) sum_int) ||
(!unsigned_flag && nr > sum_int)))
{
sum_int=nr;
null_value=0;
}
}
break;
case DECIMAL_RESULT:
{
my_decimal value_buff, *val= args[0]->val_decimal(&value_buff);
if (!args[0]->null_value &&
(null_value || (my_decimal_cmp(val, &sum_dec) > 0)))
{
my_decimal2decimal(val, &sum_dec);
null_value= 0;
}
}
break;
case REAL_RESULT:
{
double nr= args[0]->val_real();
if (!args[0]->null_value && (null_value || nr > sum))
{
sum=nr;
null_value=0;
}
}
break;
case ROW_RESULT:
default:
// This case should never be choosen
DBUG_ASSERT(0);
break;
} }
return 0; return 0;
} }
...@@ -2225,14 +2053,15 @@ void Item_sum_hybrid::update_field() ...@@ -2225,14 +2053,15 @@ void Item_sum_hybrid::update_field()
void void
Item_sum_hybrid::min_max_update_str_field() Item_sum_hybrid::min_max_update_str_field()
{ {
String *res_str=args[0]->val_str(&value); DBUG_ASSERT(cmp);
String *res_str=args[0]->val_str(&cmp->value1);
if (!args[0]->null_value) if (!args[0]->null_value)
{ {
result_field->val_str(&tmp_value); result_field->val_str(&cmp->value2);
if (result_field->is_null() || if (result_field->is_null() ||
(cmp_sign * sortcmp(res_str,&tmp_value,collation.collation)) < 0) (cmp_sign * sortcmp(res_str,&cmp->value2,collation.collation)) < 0)
result_field->store(res_str->ptr(),res_str->length(),res_str->charset()); result_field->store(res_str->ptr(),res_str->length(),res_str->charset());
result_field->set_notnull(); result_field->set_notnull();
} }
......
...@@ -323,22 +323,6 @@ class Item_sum :public Item_result_field ...@@ -323,22 +323,6 @@ class Item_sum :public Item_result_field
virtual void update_field()=0; virtual void update_field()=0;
virtual bool keep_field_type(void) const { return 0; } virtual bool keep_field_type(void) const { return 0; }
virtual void fix_length_and_dec() { maybe_null=1; null_value=1; } virtual void fix_length_and_dec() { maybe_null=1; null_value=1; }
/*
This method is used for debug purposes to print the name of an
item to the debug log. The second use of this method is as
a helper function of print(), where it is applicable.
To suit both goals it should return a meaningful,
distinguishable and sintactically correct string. This method
should not be used for runtime type identification, use enum
{Sum}Functype and Item_func::functype()/Item_sum::sum_func()
instead.
NOTE: for Items inherited from Item_sum, func_name() return part of
function name till first argument (including '(') to make difference in
names for functions with 'distinct' clause and without 'distinct' and
also to make printing of items inherited from Item_sum uniform.
*/
virtual const char *func_name() const= 0;
virtual Item *result_item(Field *field) virtual Item *result_item(Field *field)
{ return new Item_field(field); } { return new Item_field(field); }
table_map used_tables() const { return used_tables_cache; } table_map used_tables() const { return used_tables_cache; }
...@@ -664,6 +648,7 @@ class Item_avg_field :public Item_result_field ...@@ -664,6 +648,7 @@ class Item_avg_field :public Item_result_field
} }
void fix_length_and_dec() {} void fix_length_and_dec() {}
enum Item_result result_type () const { return hybrid_type; } enum Item_result result_type () const { return hybrid_type; }
const char *func_name() const { DBUG_ASSERT(0); return "avg_field"; }
}; };
...@@ -732,6 +717,7 @@ class Item_variance_field :public Item_result_field ...@@ -732,6 +717,7 @@ class Item_variance_field :public Item_result_field
} }
void fix_length_and_dec() {} void fix_length_and_dec() {}
enum Item_result result_type () const { return hybrid_type; } enum Item_result result_type () const { return hybrid_type; }
const char *func_name() const { DBUG_ASSERT(0); return "variance_field"; }
}; };
...@@ -807,6 +793,7 @@ class Item_std_field :public Item_variance_field ...@@ -807,6 +793,7 @@ class Item_std_field :public Item_variance_field
my_decimal *val_decimal(my_decimal *); my_decimal *val_decimal(my_decimal *);
enum Item_result result_type () const { return REAL_RESULT; } enum Item_result result_type () const { return REAL_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE;} enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE;}
const char *func_name() const { DBUG_ASSERT(0); return "std_field"; }
}; };
/* /*
...@@ -832,14 +819,13 @@ class Item_sum_std :public Item_sum_variance ...@@ -832,14 +819,13 @@ class Item_sum_std :public Item_sum_variance
}; };
// This class is a string or number function depending on num_func // This class is a string or number function depending on num_func
class Arg_comparator;
class Item_cache;
class Item_sum_hybrid :public Item_sum class Item_sum_hybrid :public Item_sum
{ {
protected: protected:
String value,tmp_value; Item_cache *value;
double sum; Arg_comparator *cmp;
longlong sum_int;
my_decimal sum_dec;
Item_result hybrid_type; Item_result hybrid_type;
enum_field_types hybrid_field_type; enum_field_types hybrid_field_type;
int cmp_sign; int cmp_sign;
...@@ -847,12 +833,17 @@ class Item_sum_hybrid :public Item_sum ...@@ -847,12 +833,17 @@ class Item_sum_hybrid :public Item_sum
public: public:
Item_sum_hybrid(Item *item_par,int sign) Item_sum_hybrid(Item *item_par,int sign)
:Item_sum(item_par), sum(0.0), sum_int(0), :Item_sum(item_par), value(0), cmp(0),
hybrid_type(INT_RESULT), hybrid_field_type(MYSQL_TYPE_LONGLONG), hybrid_type(INT_RESULT), hybrid_field_type(MYSQL_TYPE_LONGLONG),
cmp_sign(sign), was_values(TRUE) cmp_sign(sign), was_values(TRUE)
{ collation.set(&my_charset_bin); } { collation.set(&my_charset_bin); }
Item_sum_hybrid(THD *thd, Item_sum_hybrid *item); Item_sum_hybrid(THD *thd, Item_sum_hybrid *item)
:Item_sum(thd, item), value(item->value), hybrid_type(item->hybrid_type),
hybrid_field_type(item->hybrid_field_type), cmp_sign(item->cmp_sign),
was_values(item->was_values)
{ }
bool fix_fields(THD *, Item **); bool fix_fields(THD *, Item **);
void setup(Item *item, Item *value_arg);
void clear(); void clear();
double val_real(); double val_real();
longlong val_int(); longlong val_int();
......
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