1
0
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:
Alexander Barkov
2017-05-10 15:29:48 +04:00
parent 7c44b8afb7
commit 705fc43eaa
21 changed files with 755 additions and 325 deletions

View File

@ -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:
/*