Commit 726e8390 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.
parent 16853758
...@@ -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;
}; };
......
This diff is collapsed.
...@@ -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