A fix: bug#6931: Date Type column problem when using UNION-Table

       bug#7833:  Wrong datatype of aggregate column is returned
parent 7213ca46
...@@ -733,3 +733,15 @@ one 2 ...@@ -733,3 +733,15 @@ one 2
two 2 two 2
three 1 three 1
drop table t1; drop table t1;
create table t1(f1 datetime);
insert into t1 values (now());
create table t2 select f2 from (select max(now()) f2 from t1) a;
show columns from t2;
Field Type Null Key Default Extra
f2 datetime 0000-00-00 00:00:00
drop table t2;
create table t2 select f2 from (select now() f2 from t1) a;
show columns from t2;
Field Type Null Key Default Extra
f2 datetime 0000-00-00 00:00:00
drop table t2, t1;
...@@ -1137,3 +1137,39 @@ t1 CREATE TABLE `t1` ( ...@@ -1137,3 +1137,39 @@ t1 CREATE TABLE `t1` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1; drop table t1;
drop table t2; drop table t2;
create table t1(a1 int, f1 char(10));
create table t2
select f2,a1 from (select a1, CAST('2004-12-31' AS DATE) f2 from t1) a
union
select f2,a1 from (select a1, CAST('2004-12-31' AS DATE) f2 from t1) a
order by f2, a1;
show columns from t2;
Field Type Null Key Default Extra
f2 date YES NULL
a1 int(11) YES NULL
drop table t1, t2;
create table t1 (f1 int);
create table t2 (f1 int, f2 int ,f3 date);
create table t3 (f1 int, f2 char(10));
create table t4
(
select t2.f3 as sdate
from t1
left outer join t2 on (t1.f1 = t2.f1)
inner join t3 on (t2.f2 = t3.f1)
order by t1.f1, t3.f1, t2.f3
)
union
(
select cast('2004-12-31' as date) as sdate
from t1
left outer join t2 on (t1.f1 = t2.f1)
inner join t3 on (t2.f2 = t3.f1)
group by t1.f1
order by t1.f1, t3.f1, t2.f3
)
order by sdate;
show columns from t4;
Field Type Null Key Default Extra
sdate date YES NULL
drop table t1, t2, t3, t4;
...@@ -473,3 +473,17 @@ INSERT INTO t1 VALUES ...@@ -473,3 +473,17 @@ INSERT INTO t1 VALUES
select val, count(*) from t1 group by val; select val, count(*) from t1 group by val;
drop table t1; drop table t1;
#
# Bug 7833: Wrong datatype of aggregate column is returned
#
create table t1(f1 datetime);
insert into t1 values (now());
create table t2 select f2 from (select max(now()) f2 from t1) a;
show columns from t2;
drop table t2;
create table t2 select f2 from (select now() f2 from t1) a;
show columns from t2;
drop table t2, t1;
...@@ -664,3 +664,38 @@ show create table t1; ...@@ -664,3 +664,38 @@ show create table t1;
drop table t1; drop table t1;
drop table t2; drop table t2;
#
# Bug 6931: Date Type column problem when using UNION-Table.
#
create table t1(a1 int, f1 char(10));
create table t2
select f2,a1 from (select a1, CAST('2004-12-31' AS DATE) f2 from t1) a
union
select f2,a1 from (select a1, CAST('2004-12-31' AS DATE) f2 from t1) a
order by f2, a1;
show columns from t2;
drop table t1, t2;
create table t1 (f1 int);
create table t2 (f1 int, f2 int ,f3 date);
create table t3 (f1 int, f2 char(10));
create table t4
(
select t2.f3 as sdate
from t1
left outer join t2 on (t1.f1 = t2.f1)
inner join t3 on (t2.f2 = t3.f1)
order by t1.f1, t3.f1, t2.f3
)
union
(
select cast('2004-12-31' as date) as sdate
from t1
left outer join t2 on (t1.f1 = t2.f1)
inner join t3 on (t2.f2 = t3.f1)
group by t1.f1
order by t1.f1, t3.f1, t2.f3
)
order by sdate;
show columns from t4;
drop table t1, t2, t3, t4;
...@@ -245,6 +245,7 @@ static Field::field_cast_enum field_cast_date[]= ...@@ -245,6 +245,7 @@ static Field::field_cast_enum field_cast_date[]=
Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP};
static Field::field_cast_enum field_cast_newdate[]= static Field::field_cast_enum field_cast_newdate[]=
{Field::FIELD_CAST_NEWDATE, {Field::FIELD_CAST_NEWDATE,
Field::FIELD_CAST_DATE,
Field::FIELD_CAST_DATETIME, Field::FIELD_CAST_DATETIME,
Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING,
Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP};
...@@ -6024,6 +6025,40 @@ Field *make_field(char *ptr, uint32 field_length, ...@@ -6024,6 +6025,40 @@ Field *make_field(char *ptr, uint32 field_length,
} }
/*
Check if field_type is appropriate field type
to create field for tmp table using
item->tmp_table_field() method
SYNOPSIS
field_types_to_be_kept()
field_type - field type
NOTE
it is used in function get_holder_example_field()
from item.cc
RETURN
1 - can use item->tmp_table_field() method
0 - can not use item->tmp_table_field() method
*/
bool field_types_to_be_kept(enum_field_types field_type)
{
switch (field_type)
{
case FIELD_TYPE_DATE:
case FIELD_TYPE_NEWDATE:
case FIELD_TYPE_TIME:
case FIELD_TYPE_DATETIME:
return 1;
default:
return 0;
}
}
/* Create a field suitable for create of table */ /* Create a field suitable for create of table */
create_field::create_field(Field *old_field,Field *orig_field) create_field::create_field(Field *old_field,Field *orig_field)
......
...@@ -1265,6 +1265,7 @@ int set_field_to_null(Field *field); ...@@ -1265,6 +1265,7 @@ int set_field_to_null(Field *field);
int set_field_to_null_with_conversions(Field *field, bool no_conversions); int set_field_to_null_with_conversions(Field *field, bool no_conversions);
bool test_if_int(const char *str, int length, const char *int_end, bool test_if_int(const char *str, int length, const char *int_end,
CHARSET_INFO *cs); CHARSET_INFO *cs);
bool field_types_to_be_kept(enum_field_types field_type);
/* /*
The following are for the interface with the .frm file The following are for the interface with the .frm file
......
...@@ -2639,7 +2639,53 @@ void Item_cache_row::bring_value() ...@@ -2639,7 +2639,53 @@ void Item_cache_row::bring_value()
} }
Item_type_holder::Item_type_holder(THD *thd, Item *item) /*
Returns field for temporary table dependind on item type
SYNOPSIS
get_holder_example_field()
thd - thread handler
item - pointer to item
table - empty table object
NOTE
It is possible to return field for Item_func
items only if field type of this item is
date or time or datetime type.
also see function field_types_to_be_kept() from
field.cc
RETURN
# - field
0 - no field
*/
Field *get_holder_example_field(THD *thd, Item *item, TABLE *table)
{
DBUG_ASSERT(table);
Item_func *tmp_item= 0;
if (item->type() == Item::FIELD_ITEM)
return (((Item_field*) item)->field);
if (item->type() == Item::FUNC_ITEM)
tmp_item= (Item_func *) item;
else if (item->type() == Item::SUM_FUNC_ITEM)
{
Item_sum *item_sum= (Item_sum *) item;
if (item_sum->keep_field_type())
{
if (item_sum->args[0]->type() == Item::FIELD_ITEM)
return (((Item_field*) item_sum->args[0])->field);
if (item_sum->args[0]->type() == Item::FUNC_ITEM)
tmp_item= (Item_func *) item_sum->args[0];
}
}
return (tmp_item && field_types_to_be_kept(tmp_item->field_type()) ?
tmp_item->tmp_table_field(table) : 0);
}
Item_type_holder::Item_type_holder(THD *thd, Item *item, TABLE *table)
:Item(thd, item), item_type(item->result_type()), :Item(thd, item), item_type(item->result_type()),
orig_type(item_type) orig_type(item_type)
{ {
...@@ -2649,10 +2695,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) ...@@ -2649,10 +2695,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
It is safe assign pointer on field, because it will be used just after It is safe assign pointer on field, because it will be used just after
all JOIN::prepare calls and before any SELECT execution all JOIN::prepare calls and before any SELECT execution
*/ */
if (item->type() == Item::FIELD_ITEM) field_example= get_holder_example_field(thd, item, table);
field_example= ((Item_field*) item)->field;
else
field_example= 0;
max_length= real_length(item); max_length= real_length(item);
maybe_null= item->maybe_null; maybe_null= item->maybe_null;
collation.set(item->collation); collation.set(item->collation);
...@@ -2692,25 +2735,23 @@ inline bool is_attr_compatible(Item *from, Item *to) ...@@ -2692,25 +2735,23 @@ inline bool is_attr_compatible(Item *from, Item *to)
(to->maybe_null || !from->maybe_null) && (to->maybe_null || !from->maybe_null) &&
(to->result_type() != STRING_RESULT || (to->result_type() != STRING_RESULT ||
from->result_type() != STRING_RESULT || from->result_type() != STRING_RESULT ||
my_charset_same(from->collation.collation, (from->collation.collation == to->collation.collation)));
to->collation.collation)));
} }
bool Item_type_holder::join_types(THD *thd, Item *item) bool Item_type_holder::join_types(THD *thd, Item *item, TABLE *table)
{ {
uint32 new_length= real_length(item); uint32 new_length= real_length(item);
bool use_new_field= 0, use_expression_type= 0; bool use_new_field= 0, use_expression_type= 0;
Item_result new_result_type= type_convertor[item_type][item->result_type()]; Item_result new_result_type= type_convertor[item_type][item->result_type()];
bool item_is_a_field= item->type() == Item::FIELD_ITEM; Field *field= get_holder_example_field(thd, item, table);
bool item_is_a_field= field;
/* /*
Check if both items point to fields: in this case we Check if both items point to fields: in this case we
can adjust column types of result table in the union smartly. can adjust column types of result table in the union smartly.
*/ */
if (field_example && item_is_a_field) if (field_example && item_is_a_field)
{ {
Field *field= ((Item_field *)item)->field;
/* Can 'field_example' field store data of the column? */ /* Can 'field_example' field store data of the column? */
if ((use_new_field= if ((use_new_field=
(!field->field_cast_compatible(field_example->field_cast_type()) || (!field->field_cast_compatible(field_example->field_cast_type()) ||
...@@ -2751,7 +2792,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) ...@@ -2751,7 +2792,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
It is safe to assign a pointer to field here, because it will be used It is safe to assign a pointer to field here, because it will be used
before any table is closed. before any table is closed.
*/ */
field_example= ((Item_field*) item)->field; field_example= field;
} }
old_cs= collation.collation->name; old_cs= collation.collation->name;
......
...@@ -1321,14 +1321,14 @@ protected: ...@@ -1321,14 +1321,14 @@ protected:
Item_result orig_type; Item_result orig_type;
Field *field_example; Field *field_example;
public: public:
Item_type_holder(THD*, Item*); Item_type_holder(THD*, Item*, TABLE *);
Item_result result_type () const { return item_type; } Item_result result_type () const { return item_type; }
enum Type type() const { return TYPE_HOLDER; } enum Type type() const { return TYPE_HOLDER; }
double val(); double val();
longlong val_int(); longlong val_int();
String *val_str(String*); String *val_str(String*);
bool join_types(THD *thd, Item *); bool join_types(THD *thd, Item *, TABLE *);
Field *example() { return field_example; } Field *example() { return field_example; }
static uint32 real_length(Item *item); static uint32 real_length(Item *item);
void cleanup() void cleanup()
......
...@@ -148,6 +148,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ...@@ -148,6 +148,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
SELECT_LEX *sl, *first_select; SELECT_LEX *sl, *first_select;
select_result *tmp_result; select_result *tmp_result;
bool is_union; bool is_union;
TABLE *empty_table= 0;
DBUG_ENTER("st_select_lex_unit::prepare"); DBUG_ENTER("st_select_lex_unit::prepare");
describe= test(additional_options & SELECT_DESCRIBE); describe= test(additional_options & SELECT_DESCRIBE);
...@@ -239,13 +240,21 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ...@@ -239,13 +240,21 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
goto err; goto err;
if (sl == first_select) if (sl == first_select)
{ {
/*
We need to create an empty table object. It is used
to create tmp_table fields in Item_type_holder.
The main reason of this is that we can't create
field object without table.
*/
DBUG_ASSERT(!empty_table);
empty_table= (TABLE*) thd->calloc(sizeof(TABLE));
types.empty(); types.empty();
List_iterator_fast<Item> it(sl->item_list); List_iterator_fast<Item> it(sl->item_list);
Item *item_tmp; Item *item_tmp;
while ((item_tmp= it++)) while ((item_tmp= it++))
{ {
/* Error's in 'new' will be detected after loop */ /* Error's in 'new' will be detected after loop */
types.push_back(new Item_type_holder(thd_arg, item_tmp)); types.push_back(new Item_type_holder(thd_arg, item_tmp, empty_table));
} }
if (thd_arg->is_fatal_error) if (thd_arg->is_fatal_error)
...@@ -264,7 +273,8 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ...@@ -264,7 +273,8 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
Item *type, *item_tmp; Item *type, *item_tmp;
while ((type= tp++, item_tmp= it++)) while ((type= tp++, item_tmp= it++))
{ {
if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp)) if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp,
empty_table))
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
} }
......
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