Commit 8aa044e6 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-11478 Result data type aggregation for pluggable data types

parent f6138883
......@@ -2938,13 +2938,7 @@ sha1('P'),
)
)
) AS r;
r
0
Warnings:
Warning 1292 Truncated incorrect INTEGER value: '511993d3c99719e38a6779073019dacd7178ddb9'
Warning 1292 Truncated incorrect DECIMAL value: '[.DC2.]'
Warning 1292 Truncated incorrect INTEGER value: '511993d3c99719e38a6779073019dacd7178ddb9'
Warning 1292 Truncated incorrect DOUBLE value: '0.000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
ERROR HY000: Illegal parameter data types geometry and decimal for operation 'coalesce'
connection conn1;
SET @@global.max_allowed_packet:= @tmp_max;
disconnect newconn;
......
This diff is collapsed.
......@@ -1554,6 +1554,8 @@ format(rpad('111111111.1',
# But in mysqltest --disable_prepare_warnings affects SELECT queries only
# and can't suppress prepare time warnings for DO.
#
--error ER_CANT_AGGREGATE_2TYPES
SELECT
round(
concat( (
......
......@@ -1716,6 +1716,85 @@ INSERT INTO t1 (a,b) VALUES (Point(1,1),Point(1,1));
SELECT c FROM t1;
DROP TABLE t1;
--echo #
--echo #
--echo #
DELIMITER $$;
CREATE PROCEDURE p2(query TEXT)
BEGIN
DECLARE errcount INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
SET errcount = errcount+1;
#SHOW WARNINGS;
GET DIAGNOSTICS CONDITION 1 @p= MESSAGE_TEXT;
SELECT @p AS `ERROR: `;
END;
SELECT query AS ``;
EXECUTE IMMEDIATE query;
IF errcount = 0
THEN
SHOW CREATE TABLE t2;
DROP TABLE t2;
END IF;
END;
$$
CREATE PROCEDURE p1(query TEXT)
BEGIN
SELECT query AS `-------------------------------------`;
EXECUTE IMMEDIATE query;
CALL p2('CREATE TABLE t2 AS SELECT CASE WHEN TRUE THEN a ELSE b END FROM t1');
CALL p2('CREATE TABLE t2 AS SELECT COALESCE(a,b) FROM t1');
CALL p2('CREATE TABLE t2 AS SELECT IF(TRUE,a,b) FROM t1');
CALL p2('CREATE TABLE t2 AS SELECT IFNULL(a,b) FROM t1');
CALL p2('CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1');
DROP TABLE t1;
END;
$$
DELIMITER ;$$
--disable_query_log
CALL p1('CREATE TABLE t1 (a CHAR(10), b Point)');
CALL p1('CREATE TABLE t1 (a VARCHAR(10), b Point)');
CALL p1('CREATE TABLE t1 (a TINYTEXT, b Point)');
CALL p1('CREATE TABLE t1 (a TEXT, b Point)');
CALL p1('CREATE TABLE t1 (a MEDIUMTEXT, b Point)');
CALL p1('CREATE TABLE t1 (a LONGTEXT, b Point)');
CALL p1('CREATE TABLE t1 (a TINYINT, b Point)');
CALL p1('CREATE TABLE t1 (a SMALLINT, b Point)');
CALL p1('CREATE TABLE t1 (a MEDIUMINT, b Point)');
CALL p1('CREATE TABLE t1 (a INT, b Point)');
CALL p1('CREATE TABLE t1 (a BIGINT, b Point)');
CALL p1('CREATE TABLE t1 (a FLOAT, b Point)');
CALL p1('CREATE TABLE t1 (a DOUBLE, b Point)');
CALL p1('CREATE TABLE t1 (a DECIMAL(10,2), b Point)');
CALL p1('CREATE TABLE t1 (a BIT(8), b Point)');
CALL p1('CREATE TABLE t1 (a TIME, b Point)');
CALL p1('CREATE TABLE t1 (a DATE, b Point)');
CALL p1('CREATE TABLE t1 (a DATETIME, b Point)');
CALL p1('CREATE TABLE t1 (a TIMESTAMP, b Point)');
CALL p1('CREATE TABLE t1 (a YEAR, b Point)');
--echo # This creates BLOB with hybrid functions, but fails on error with UNION (MDEV-11458)
CALL p1('CREATE TABLE t1 (a ENUM(0x61), b Point)');
CALL p1('CREATE TABLE t1 (a SET(0x61), b Point)');
--enable_query_log
--echo # This does not preserve geometry type (MDEV-9405)
CREATE TABLE t1 AS SELECT COALESCE(NULL, Point(1,1));
SHOW CREATE TABLE t1;
DROP TABLE t1;
CREATE TABLE t1 AS SELECT NULL UNION SELECT Point(1,1);
SHOW CREATE TABLE t1;
DROP TABLE t1;
DROP PROCEDURE p1;
DROP PROCEDURE p2;
--echo #
--echo # End of 10.2 tests
--echo #
This diff is collapsed.
......@@ -853,7 +853,6 @@ class Field: public Value_source
virtual Item_result cmp_type () const { return result_type(); }
static bool type_can_have_key_part(enum_field_types);
static enum_field_types field_type_merge(enum_field_types, enum_field_types);
static Item_result result_merge_type(enum_field_types);
virtual bool eq(Field *field)
{
return (ptr == field->ptr && null_ptr == field->null_ptr &&
......
......@@ -9900,23 +9900,8 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
{
DBUG_ASSERT(item->fixed);
maybe_null= item->maybe_null;
collation.set(item->collation);
get_full_info(item);
/**
Field::result_merge_type(real_field_type()) should be equal to
result_type(), with one exception when "this" is a Item_field for
a BIT field:
- Field_bit::result_type() returns INT_RESULT, so does its Item_field.
- Field::result_merge_type(MYSQL_TYPE_BIT) returns STRING_RESULT.
Perhaps we need a new method in Type_handler to cover these type
merging rules for UNION.
*/
DBUG_ASSERT(real_field_type() == MYSQL_TYPE_BIT ||
Item_type_holder::result_type() ==
Field::result_merge_type(Item_type_holder::real_field_type()));
/* fix variable decimals which always is NOT_FIXED_DEC */
if (Field::result_merge_type(real_field_type()) == INT_RESULT)
decimals= 0;
DBUG_ASSERT(!decimals || Item_type_holder::result_type() != INT_RESULT);
prev_decimal_int_part= item->decimal_int_part();
#ifdef HAVE_SPATIAL
if (item->field_type() == MYSQL_TYPE_GEOMETRY)
......@@ -10030,21 +10015,37 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
DBUG_PRINT("info:", ("in type %d len %d, dec %d",
get_real_type(item),
item->max_length, item->decimals));
set_handler_by_real_type(Field::field_type_merge(real_field_type(),
get_real_type(item)));
{
uint item_decimals= item->decimals;
/* fix variable decimals which always is NOT_FIXED_DEC */
if (Field::result_merge_type(real_field_type()) == INT_RESULT)
item_decimals= 0;
decimals= MY_MAX(decimals, item_decimals);
const Type_handler *item_type_handler=
Type_handler::get_handler_by_real_type(get_real_type(item));
if (aggregate_for_result(item_type_handler))
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
Item_type_holder::type_handler()->name().ptr(),
item_type_handler->name().ptr(),
"UNION");
DBUG_RETURN(true);
}
/*
At this point non-zero decimals in combination with integer data types
is possible in some cases:
SELECT * FROM (SELECT NULL) a UNION SELECT 1;
In the constructor Item_type_holder::Item_type_holder() the data type
handler was set to type_handler_null with decimals==NOT_FIXED_DEC.
After the above call for aggregate_for_result() for the literal 1
which is on the right side of the UNION, the data type handler
changes to type_handler_longlong, while decimals is still NOT_FIXED_DEC.
*/
if (Item_type_holder::result_type() == INT_RESULT)
decimals= 0;
else
decimals= MY_MAX(decimals, item->decimals);
if (Item_type_holder::field_type() == FIELD_TYPE_GEOMETRY)
geometry_type=
Field_geom::geometry_type_merge(geometry_type, item->get_geometry_type());
if (Field::result_merge_type(real_field_type()) == DECIMAL_RESULT)
if (Item_type_holder::result_type() == DECIMAL_RESULT)
{
decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE);
int item_int_part= item->decimal_int_part();
......@@ -10056,7 +10057,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
unsigned_flag);
}
switch (Field::result_merge_type(real_field_type()))
switch (Item_type_holder::result_type())
{
case STRING_RESULT:
{
......
......@@ -155,87 +155,6 @@ bool Type_handler_hybrid_field_type::aggregate_for_comparison(Item **items,
}
/**
@brief Aggregates field types from the array of items.
@param[in] items array of items to aggregate the type from
@paran[in] nitems number of items in the array
@param[in] treat_bit_as_number - if BIT should be aggregated to a non-BIT
counterpart as a LONGLONG number or as a VARBINARY string.
Currently behaviour depends on the function:
- LEAST/GREATEST treat BIT as VARBINARY when
aggregating with a non-BIT counterpart.
Note, UNION also works this way.
- CASE, COALESCE, IF, IFNULL treat BIT as LONGLONG when
aggregating with a non-BIT counterpart;
This inconsistency may be changed in the future. See MDEV-8867.
Note, independently from "treat_bit_as_number":
- a single BIT argument gives BIT as a result
- two BIT couterparts give BIT as a result
@details This function aggregates field types from the array of items.
Found type is supposed to be used later as the result field type
of a multi-argument function.
Aggregation itself is performed by the Field::field_type_merge()
function.
@note The term "aggregation" is used here in the sense of inferring the
result type of a function from its argument types.
@return aggregated field type.
*/
enum_field_types agg_field_type(Item **items, uint nitems,
bool treat_bit_as_number)
{
uint i;
if (!nitems || items[0]->result_type() == ROW_RESULT)
{
DBUG_ASSERT(0);
return MYSQL_TYPE_NULL;
}
enum_field_types res= items[0]->field_type();
uint unsigned_count= items[0]->unsigned_flag;
for (i= 1 ; i < nitems ; i++)
{
enum_field_types cur= items[i]->field_type();
if (treat_bit_as_number &&
((res == MYSQL_TYPE_BIT) ^ (cur == MYSQL_TYPE_BIT)))
{
if (res == MYSQL_TYPE_BIT)
res= MYSQL_TYPE_LONGLONG; // BIT + non-BIT
else
cur= MYSQL_TYPE_LONGLONG; // non-BIT + BIT
}
res= Field::field_type_merge(res, cur);
unsigned_count+= items[i]->unsigned_flag;
}
switch (res) {
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_BIT:
if (unsigned_count != 0 && unsigned_count != nitems)
{
/*
If all arguments are of INT-alike type but have different
unsigned_flag, then convert to DECIMAL.
*/
return MYSQL_TYPE_NEWDECIMAL;
}
default:
break;
}
return res;
}
/*
Collects different types for comparison of first item with each other items
......@@ -3105,8 +3024,9 @@ void Item_func_case::fix_length_and_dec()
if (else_expr_num != -1)
agg[nagg++]= args[else_expr_num];
set_handler_by_field_type(agg_field_type(agg, nagg, true));
if (aggregate_for_result(func_name(), agg, nagg, true))
return;
if (fix_attributes(agg, nagg))
return;
......
......@@ -977,8 +977,8 @@ class Item_func_coalesce :public Item_func_hybrid_field_type
bool date_op(MYSQL_TIME *ltime,uint fuzzydate);
void fix_length_and_dec()
{
set_handler_by_field_type(agg_field_type(args, arg_count, true));
fix_attributes(args, arg_count);
if (!aggregate_for_result(func_name(), args, arg_count, true))
fix_attributes(args, arg_count);
}
const char *func_name() const { return "coalesce"; }
table_map not_null_tables() const { return 0; }
......@@ -997,8 +997,8 @@ class Item_func_case_abbreviation2 :public Item_func_hybrid_field_type
protected:
void fix_length_and_dec2(Item **items)
{
set_handler_by_field_type(agg_field_type(items, 2, true));
fix_attributes(items, 2);
if (!aggregate_for_result(func_name(), items, 2, true))
fix_attributes(items, 2);
}
uint decimal_precision2(Item **args) const;
public:
......
......@@ -2695,6 +2695,8 @@ void Item_func_min_max::fix_length_and_dec()
break;
case STRING_RESULT:
if (aggregate_for_result(func_name(), args, arg_count, false))
return;
/*
All arguments are of string-alike types:
CHAR, VARCHAR, TEXT, BINARY, VARBINARY, BLOB, SET, ENUM
......@@ -2702,7 +2704,6 @@ void Item_func_min_max::fix_length_and_dec()
*/
agg_arg_charsets_for_string_result_with_comparison(collation,
args, arg_count);
set_handler_by_field_type(agg_field_type(args, arg_count, false));
break;
case INT_RESULT:
......@@ -2731,7 +2732,8 @@ void Item_func_min_max::fix_length_and_dec()
Treat BIT as LONGLONG when aggregating to non-BIT types.
Possible final type: TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT.
*/
set_handler_by_field_type(agg_field_type(args, arg_count, true));
if (aggregate_for_result(func_name(), args, arg_count, true))
return;
}
break;
......@@ -2754,7 +2756,7 @@ void Item_func_min_max::fix_length_and_dec()
/*
Set type to DOUBLE, as Item_func::create_tmp_field() does not
distinguish between DOUBLE and FLOAT and always creates Field_double.
Perhaps we should eventually change this to use agg_field_type() here,
Perhaps we should eventually change this to use aggregate_for_result()
and fix Item_func::create_tmp_field() to create Field_float when possible.
*/
set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
......
......@@ -2587,8 +2587,6 @@ class Item_func_last_value :public Item_func
Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
LEX_STRING component);
extern bool check_reserved_words(LEX_STRING *name);
extern enum_field_types agg_field_type(Item **items, uint nitems,
bool treat_bit_as_number);
Item *find_date_time_item(Item **args, uint nargs, uint col);
double my_double_round(double value, longlong dec, bool dec_unsigned,
bool truncate);
......
......@@ -7438,3 +7438,5 @@ ER_JSON_PATH_ARRAY
eng "JSON path should end with an array identifier in argument %d to function '%s'"
ER_JSON_ONE_OR_ALL
eng "Argument 2 to function '%s' must be "one" or "all"."
ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
eng "Illegal parameter data types %s and %s for operation '%s'"
......@@ -25,7 +25,6 @@ static Type_handler_short type_handler_short;
static Type_handler_long type_handler_long;
static Type_handler_int24 type_handler_int24;
static Type_handler_year type_handler_year;
static Type_handler_bit type_handler_bit;
static Type_handler_float type_handler_float;
static Type_handler_double type_handler_double;
static Type_handler_time type_handler_time;
......@@ -54,6 +53,39 @@ Type_handler_varchar type_handler_varchar;
Type_handler_longlong type_handler_longlong;
Type_handler_newdecimal type_handler_newdecimal;
Type_handler_datetime type_handler_datetime;
Type_handler_bit type_handler_bit;
Type_aggregator type_aggregator_for_result;
class Static_data_initializer
{
public:
static Static_data_initializer m_singleton;
Static_data_initializer()
{
#ifdef HAVE_SPATIAL
type_aggregator_for_result.add(&type_handler_geometry,
&type_handler_null,
&type_handler_geometry);
type_aggregator_for_result.add(&type_handler_geometry,
&type_handler_geometry,
&type_handler_geometry);
type_aggregator_for_result.add(&type_handler_geometry,
&type_handler_blob,
&type_handler_long_blob);
type_aggregator_for_result.add(&type_handler_geometry,
&type_handler_varchar,
&type_handler_long_blob);
type_aggregator_for_result.add(&type_handler_geometry,
&type_handler_string,
&type_handler_long_blob);
#endif
}
};
Static_data_initializer Static_data_initializer::m_singleton;
void Type_std_attributes::set(const Field *field)
......@@ -149,6 +181,46 @@ Type_handler_hybrid_field_type::Type_handler_hybrid_field_type()
}
/***************************************************************************/
const Name Type_handler_row::m_name_row(C_STRING_WITH_LEN("row"));
const Name Type_handler_null::m_name_null(C_STRING_WITH_LEN("null"));
const Name
Type_handler_string::m_name_char(C_STRING_WITH_LEN("char")),
Type_handler_varchar::m_name_varchar(C_STRING_WITH_LEN("varchar")),
Type_handler_tiny_blob::m_name_tinyblob(C_STRING_WITH_LEN("tinyblob")),
Type_handler_medium_blob::m_name_mediumblob(C_STRING_WITH_LEN("mediumblob")),
Type_handler_long_blob::m_name_longblob(C_STRING_WITH_LEN("longblob")),
Type_handler_blob::m_name_blob(C_STRING_WITH_LEN("blob"));
const Name
Type_handler_enum::m_name_enum(C_STRING_WITH_LEN("enum")),
Type_handler_set::m_name_set(C_STRING_WITH_LEN("set"));
const Name
Type_handler_tiny::m_name_tiny(C_STRING_WITH_LEN("tinyint")),
Type_handler_short::m_name_short(C_STRING_WITH_LEN("shortint")),
Type_handler_long::m_name_int(C_STRING_WITH_LEN("int")),
Type_handler_longlong::m_name_longlong(C_STRING_WITH_LEN("bigint")),
Type_handler_int24::m_name_mediumint(C_STRING_WITH_LEN("mediumint")),
Type_handler_year::m_name_year(C_STRING_WITH_LEN("year")),
Type_handler_bit::m_name_bit(C_STRING_WITH_LEN("bit"));
const Name
Type_handler_float::m_name_float(C_STRING_WITH_LEN("float")),
Type_handler_double::m_name_double(C_STRING_WITH_LEN("double"));
const Name
Type_handler_olddecimal::m_name_decimal(C_STRING_WITH_LEN("decimal")),
Type_handler_newdecimal::m_name_decimal(C_STRING_WITH_LEN("decimal"));
const Name
Type_handler_time_common::m_name_time(C_STRING_WITH_LEN("time")),
Type_handler_date_common::m_name_date(C_STRING_WITH_LEN("date")),
Type_handler_datetime_common::m_name_datetime(C_STRING_WITH_LEN("datetime")),
Type_handler_timestamp_common::m_name_timestamp(C_STRING_WITH_LEN("timestamp"));
/***************************************************************************/
const Type_handler *Type_handler_int_result::type_handler_for_comparison() const
......@@ -192,6 +264,113 @@ const Type_handler *Type_handler_row::type_handler_for_comparison() const
}
/***************************************************************************/
bool
Type_handler_hybrid_field_type::aggregate_for_result(const Type_handler *other)
{
if (m_type_handler->is_traditional_type() && other->is_traditional_type())
{
m_type_handler=
Type_handler::aggregate_for_result_traditional(m_type_handler, other);
return false;
}
other= type_aggregator_for_result.find_handler(m_type_handler, other);
if (!other)
return true;
m_type_handler= other;
return false;
}
/**
@brief Aggregates field types from the array of items.
@param[in] items array of items to aggregate the type from
@param[in] nitems number of items in the array
@param[in] treat_bit_as_number - if BIT should be aggregated to a non-BIT
counterpart as a LONGLONG number or as a VARBINARY string.
Currently behaviour depends on the function:
- LEAST/GREATEST treat BIT as VARBINARY when
aggregating with a non-BIT counterpart.
Note, UNION also works this way.
- CASE, COALESCE, IF, IFNULL treat BIT as LONGLONG when
aggregating with a non-BIT counterpart;
This inconsistency may be changed in the future. See MDEV-8867.
Note, independently from "treat_bit_as_number":
- a single BIT argument gives BIT as a result
- two BIT couterparts give BIT as a result
@details This function aggregates field types from the array of items.
Found type is supposed to be used later as the result field type
of a multi-argument function.
Aggregation itself is performed by Type_handler::aggregate_for_result().
@note The term "aggregation" is used here in the sense of inferring the
result type of a function from its argument types.
@retval false - on success
@retval true - on error
*/
bool
Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
Item **items, uint nitems,
bool treat_bit_as_number)
{
if (!nitems || items[0]->result_type() == ROW_RESULT)
{
DBUG_ASSERT(0);
set_handler(&type_handler_null);
return true;
}
set_handler(items[0]->type_handler());
uint unsigned_count= items[0]->unsigned_flag;
for (uint i= 1 ; i < nitems ; i++)
{
const Type_handler *cur= items[i]->type_handler();
if (treat_bit_as_number &&
((type_handler() == &type_handler_bit) ^ (cur == &type_handler_bit)))
{
if (type_handler() == &type_handler_bit)
set_handler(&type_handler_longlong); // BIT + non-BIT
else
cur= &type_handler_longlong; // non-BIT + BIT
}
if (aggregate_for_result(cur))
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
type_handler()->name().ptr(), cur->name().ptr(), funcname);
return true;
}
unsigned_count+= items[i]->unsigned_flag;
}
switch (field_type()) {
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_BIT:
if (unsigned_count != 0 && unsigned_count != nitems)
{
/*
If all arguments are of INT-alike type but have different
unsigned_flag, then convert to DECIMAL.
*/
set_handler(&type_handler_newdecimal);
}
default:
break;
}
return false;
}
/**
Collect built-in data type handlers for comparison.
This method is very similar to item_cmp_type() defined in item.cc.
......@@ -719,6 +898,8 @@ Field *Type_handler_long_blob::make_conversion_table_field(TABLE *table,
#ifdef HAVE_SPATIAL
const Name Type_handler_geometry::m_name_geometry(C_STRING_WITH_LEN("geometry"));
Field *Type_handler_geometry::make_conversion_table_field(TABLE *table,
uint metadata,
const Field *target)
......
This diff is collapsed.
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