mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-12775 Reuse data type aggregation code for hybrid functions and UNION
Introducing a new class Type_holder (used internally in sql_union.cc), to reuse exactly the same data type attribute aggregation Type_handler API for hybrid functions and UNION. This fixes a number of bugs in UNION: - MDEV-9495 Wrong field type for a UNION of a signed and an unsigned INT expression - MDEV-9497 UNION and COALESCE produce different field types for DECIMAL+INT - MDEV-12594 UNION between fixed length double columns does not always preserve scale - MDEV-12595 UNION converts INT to BIGINT - MDEV-12599 UNION is not symmetric when mixing INT and CHAR Details: - sql_union.cc: Reusing attribute aggregation for UNION. Adding new methods: * st_select_lex_unit::join_union_type_handlers() * st_select_lex_unit::join_union_type_attributes() * st_select_lex_unit::join_union_item_types() Removing the old join_types()-based code. - Changing Type_handler::Item_hybrid_func_fix_attributes() to accept "name", Type_handler_hybrid_field_type, Type_all_attributes as three separate parameters instead of a single Item_hybrid_func parameter, to make it possible to pass both Item_hybrid_func and Type_holder. - Moving the former special GEOMETRY and ENUM/SET attribute aggregation code from Item_type_holder::join_types() to * Type_handler_typelib::Item_hybrid_func_fix_attributes(). * Type_handler_geometry::Item_hybrid_func_fix_attrubutes(). This makes GEOMETRY/ENUM/SET symmetric with all other data types (from the UNION point of view). Removing Item_type_holder::join_types() and Item_type_holder::get_full_info(). - Adding new methods into Type_all_attributes: * Type_all_attributes::set_geometry_type() and Item_hybrid_func::set_geometry_type(). * Adding Type_all_attributes::get_typelib(). * Adding Type_all_attributes::set_typelib(). - Adding Type_handler_typelib as a common parent for Type_handler_enum and Type_handler_set, to avoid code duplication: they have already had two common methods, and we're adding one more shared method. - Adding Type_all_attributes::set_maybe_null(), as some type handlers may want to set maybe_null (e.g. Type_handler_geometry) during data type attribute aggregation. - Changing Type_geometry_attributes() to accept Type_handler and Type_all_attributes as two separate parameters, instead of a single Item parameter, to make it possible to pass Type_holder. - Adding Item_args::add_argument(). - Moving Item_args::alloc_arguments() from "protected" to "public". - Moving Item_type_holder::Item_type_holder() from item.cc to item.h, as now it's very simple. Btw, this constructor should probably be eventually removed. It's now used only in sql_show.cc, which could be modified to use Item_return_decimal (for symmetry with Item_return_xxx created for all other data types). Or, another option: remove all Item_return_xxx and use Item_type_holder for all data types instead. - storage/tokudb/mysql-test/tokudb/r/type_float.result Recording new results (MDEV-12594). - mysql-test/r/cte_recursive.result Recording new results (MDEV-9497) - mysql-test/r/subselect*.result Recording new results (MDEV-12595) - mysql-test/r/metadata.result Recording new results (MDEV-9495) - mysql-test/r/temp_table.result Recording new results (MDEV-12594) - mysql-test/r/type_float.result Recording new results (MDEV-12594)
This commit is contained in:
217
sql/sql_union.cc
217
sql/sql_union.cc
@ -692,6 +692,179 @@ bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl,
|
||||
}
|
||||
|
||||
|
||||
class Type_holder: public Sql_alloc,
|
||||
public Item_args,
|
||||
public Type_handler_hybrid_field_type,
|
||||
public Type_all_attributes,
|
||||
public Type_geometry_attributes
|
||||
{
|
||||
TYPELIB *m_typelib;
|
||||
bool m_maybe_null;
|
||||
public:
|
||||
Type_holder()
|
||||
:m_typelib(NULL),
|
||||
m_maybe_null(false)
|
||||
{ }
|
||||
|
||||
void set_maybe_null(bool maybe_null_arg) { m_maybe_null= maybe_null_arg; }
|
||||
bool get_maybe_null() const { return m_maybe_null; }
|
||||
|
||||
uint decimal_precision() const
|
||||
{
|
||||
/*
|
||||
Type_holder is not used directly to create fields, so
|
||||
its virtual decimal_precision() is never called.
|
||||
We should eventually extend create_result_table() to accept
|
||||
an array of Type_holders directly, without having to allocate
|
||||
Item_type_holder's and put them into List<Item>.
|
||||
*/
|
||||
DBUG_ASSERT(0);
|
||||
return 0;
|
||||
}
|
||||
void set_geometry_type(uint type)
|
||||
{
|
||||
Type_geometry_attributes::set_geometry_type(type);
|
||||
}
|
||||
uint uint_geometry_type() const
|
||||
{
|
||||
return Type_geometry_attributes::get_geometry_type();
|
||||
}
|
||||
void set_typelib(TYPELIB *typelib)
|
||||
{
|
||||
m_typelib= typelib;
|
||||
}
|
||||
TYPELIB *get_typelib() const
|
||||
{
|
||||
return m_typelib;
|
||||
}
|
||||
|
||||
bool aggregate_attributes(THD *thd)
|
||||
{
|
||||
for (uint i= 0; i < arg_count; i++)
|
||||
m_maybe_null|= args[i]->maybe_null;
|
||||
return
|
||||
type_handler()->Item_hybrid_func_fix_attributes(thd,
|
||||
"UNION", this, this,
|
||||
args, arg_count);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Aggregate data type handlers for the "count" leftmost UNION parts.
|
||||
*/
|
||||
bool st_select_lex_unit::join_union_type_handlers(THD *thd_arg,
|
||||
Type_holder *holders,
|
||||
uint count)
|
||||
{
|
||||
DBUG_ENTER("st_select_lex_unit::join_union_type_handlers");
|
||||
SELECT_LEX *first_sl= first_select(), *sl= first_sl;
|
||||
for (uint i= 0; i < count ; sl= sl->next_select(), i++)
|
||||
{
|
||||
Item *item;
|
||||
List_iterator_fast<Item> it(sl->item_list);
|
||||
for (uint pos= 0; (item= it++); pos++)
|
||||
{
|
||||
const Type_handler *item_type_handler= item->real_type_handler();
|
||||
if (sl == first_sl)
|
||||
holders[pos].set_handler(item_type_handler);
|
||||
else
|
||||
{
|
||||
if (first_sl->item_list.elements != sl->item_list.elements)
|
||||
{
|
||||
my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
|
||||
ER_THD(thd_arg, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),
|
||||
MYF(0));
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
if (holders[pos].aggregate_for_result(item_type_handler))
|
||||
{
|
||||
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
|
||||
holders[pos].type_handler()->name().ptr(),
|
||||
item_type_handler->name().ptr(),
|
||||
"UNION");
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Aggregate data type attributes for the "count" leftmost UNION parts.
|
||||
*/
|
||||
bool st_select_lex_unit::join_union_type_attributes(THD *thd_arg,
|
||||
Type_holder *holders,
|
||||
uint count)
|
||||
{
|
||||
DBUG_ENTER("st_select_lex_unit::join_union_type_attributes");
|
||||
SELECT_LEX *sl, *first_sl= first_select();
|
||||
uint item_pos;
|
||||
for (uint pos= 0; pos < first_sl->item_list.elements; pos++)
|
||||
{
|
||||
if (holders[pos].alloc_arguments(thd_arg, count))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
for (item_pos= 0, sl= first_sl ;
|
||||
item_pos < count;
|
||||
sl= sl->next_select(), item_pos++)
|
||||
{
|
||||
Item *item_tmp;
|
||||
List_iterator_fast<Item> itx(sl->item_list);
|
||||
for (uint holder_pos= 0 ; (item_tmp= itx++); holder_pos++)
|
||||
{
|
||||
DBUG_ASSERT(item_tmp->fixed);
|
||||
holders[holder_pos].add_argument(item_tmp);
|
||||
}
|
||||
}
|
||||
for (uint pos= 0; pos < first_sl->item_list.elements; pos++)
|
||||
{
|
||||
if (holders[pos].aggregate_attributes(thd_arg))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Join data types for the leftmost "count" UNION parts
|
||||
and store corresponding Item_type_holder's into "types".
|
||||
*/
|
||||
bool st_select_lex_unit::join_union_item_types(THD *thd_arg,
|
||||
List<Item> &types,
|
||||
uint count)
|
||||
{
|
||||
DBUG_ENTER("st_select_lex_unit::join_union_select_list_types");
|
||||
SELECT_LEX *first_sl= first_select();
|
||||
Type_holder *holders;
|
||||
|
||||
if (!(holders= new (thd_arg->mem_root)
|
||||
Type_holder[first_sl->item_list.elements]) ||
|
||||
join_union_type_handlers(thd_arg, holders, count) ||
|
||||
join_union_type_attributes(thd_arg, holders, count))
|
||||
DBUG_RETURN(true);
|
||||
|
||||
types.empty();
|
||||
List_iterator_fast<Item> it(first_sl->item_list);
|
||||
Item *item_tmp;
|
||||
for (uint pos= 0; (item_tmp= it++); pos++)
|
||||
{
|
||||
/* Error's in 'new' will be detected after loop */
|
||||
types.push_back(new (thd_arg->mem_root)
|
||||
Item_type_holder(thd_arg,
|
||||
&item_tmp->name,
|
||||
holders[pos].type_handler(),
|
||||
&holders[pos]/*Type_all_attributes*/,
|
||||
holders[pos].get_maybe_null()));
|
||||
}
|
||||
if (thd_arg->is_fatal_error)
|
||||
DBUG_RETURN(true); // out of memory
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
|
||||
bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
|
||||
ulong additional_options)
|
||||
{
|
||||
@ -699,6 +872,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
|
||||
SELECT_LEX *sl, *first_sl= first_select();
|
||||
bool is_recursive= with_element && with_element->is_recursive;
|
||||
bool is_rec_result_table_created= false;
|
||||
uint union_part_count= 0;
|
||||
select_result *tmp_result;
|
||||
bool is_union_select;
|
||||
bool have_except= FALSE, have_intersect= FALSE;
|
||||
@ -811,7 +985,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
|
||||
goto cont;
|
||||
}
|
||||
|
||||
for (;sl; sl= sl->next_select())
|
||||
for (;sl; sl= sl->next_select(), union_part_count++)
|
||||
{
|
||||
if (prepare_join(thd_arg, sl, tmp_result, additional_options,
|
||||
is_union_select))
|
||||
@ -834,43 +1008,10 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
|
||||
if (is_recursive)
|
||||
{
|
||||
if (derived->with->rename_columns_of_derived_unit(thd, this))
|
||||
goto err;
|
||||
goto err;
|
||||
if (check_duplicate_names(thd, sl->item_list, 0))
|
||||
goto err;
|
||||
}
|
||||
types.empty();
|
||||
List_iterator_fast<Item> it(sl->item_list);
|
||||
Item *item_tmp;
|
||||
while ((item_tmp= it++))
|
||||
{
|
||||
/* Error's in 'new' will be detected after loop */
|
||||
types.push_back(new (thd_arg->mem_root)
|
||||
Item_type_holder(thd_arg, item_tmp));
|
||||
}
|
||||
|
||||
if (thd_arg->is_fatal_error)
|
||||
goto err; // out of memory
|
||||
}
|
||||
else
|
||||
{
|
||||
if (types.elements != sl->item_list.elements)
|
||||
{
|
||||
my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
|
||||
ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
|
||||
goto err;
|
||||
}
|
||||
if (!is_rec_result_table_created)
|
||||
{
|
||||
List_iterator_fast<Item> it(sl->item_list);
|
||||
List_iterator_fast<Item> tp(types);
|
||||
Item *type, *item_tmp;
|
||||
while ((type= tp++, item_tmp= it++))
|
||||
{
|
||||
DBUG_ASSERT(item_tmp->fixed);
|
||||
if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_recursive)
|
||||
{
|
||||
@ -883,6 +1024,9 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
|
||||
ulonglong create_options;
|
||||
create_options= (first_sl->options | thd_arg->variables.option_bits |
|
||||
TMP_TABLE_ALL_COLUMNS);
|
||||
// Join data types for all non-recursive parts of a recursive UNION
|
||||
if (join_union_item_types(thd, types, union_part_count + 1))
|
||||
goto err;
|
||||
if (union_result->create_result_table(thd, &types,
|
||||
MY_TEST(union_distinct),
|
||||
create_options, derived->alias,
|
||||
@ -898,6 +1042,9 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
|
||||
}
|
||||
}
|
||||
}
|
||||
// In case of a non-recursive UNION, join data types for all UNION parts.
|
||||
if (!is_recursive && join_union_item_types(thd, types, union_part_count))
|
||||
goto err;
|
||||
|
||||
cont:
|
||||
/*
|
||||
|
Reference in New Issue
Block a user