Commit 65808255 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-12506 Split Item_func_min_max::fix_length_and_dec() into methods in...

MDEV-12506 Split Item_func_min_max::fix_length_and_dec() into methods in Type_handler + MDEV-12497 + MDEV-12504

This patch does the following:

1. Adds a new method Type_handler_hybrid_field_type::aggregate_for_min_max()

   - For non-traditional data types it uses
     type_handler_data->m_type_aggregator_for_result.find_handler()
     This allows pluggable data types to define in the future their
     own behavior of the result data type detection for LEAST/GREATEST.
     Also, this disallows expressions of the GEOMETRY data type
     (and its variants such as POINT) to be mixed in with
     numeric and temporal data types in LEAST/GREATEST.

   - For traditional data types it reproduces the old behavior of
     the result data type detection (but not attributes, see below).

2. Adds a new virtual method Type_handler::Item_func_min_max_fix_attributes()
   and reuses as much as possible the code that calculates data type attributes
   for CASE-alike functions (e.g. CASE..THEN, COALESCE, IF).

   As the old code responsible for attributes calculation in the old
   implementation of Item_func_min_max::fix_length_and_dec()
   was not fully correct, this automatically fixes the following bugs:

   - MDEV-12497 Wrong data type for LEAST(latin1_expr, utf8_expr)
     The old fix_length_and_dec() calculated max_length before
     character set aggregation. Now max_length is calculated after, in
     Item_func::count_string_length() called from
     Item_func::aggregate_attributes_string() called from
     Type_handler_string_result::Item_hybrid_func_fix_attributes() called from
     Type_handler::Item_func_min_max_fix_attributes() called from
     Item_func_min_max::fix_length_and_dec().

   - MDEV-12504 Wrong data type for LEAST(date_expr,time_expr)
     The old fix_length_and_dec() simply used the maximum of max_length
     among all arguments to set its own max_length and did not take
     into account that a mixture of DATE and TIME becomes DATETIME.
     Now this is correctly handled by:
     Type_handler_datetime_common::Item_hybrid_func_fix_attributes() called from
     Type_handler::Item_func_min_max_fix_attributes() called from
     Item_func_min_max::fix_length_and_dec().

3. Removes the old implementation of Item_func_min_max::fix_length_and_dec()
   and replaces it to calls of the new methods.

4. Cleanup: moves the code related to unsigned_flag processing
   from Type_handler_hybrid_field_type::aggregate_for_result()
   to   Type_handler_int_result::Item_hybrid_func_fix_attributes().
   This is done:
   - to avoid code duplication in
     Type_handler_hybrid_field_type::aggregate_for_min_max()
   - to get rid of one more call for field_type(), which is unfriendly
     to the conceipt of pluggable data types.
parent ba670edf
......@@ -608,7 +608,7 @@ create table t1 as select concat(least(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varbinary(2) DEFAULT NULL
`c1` varbinary(1) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(greatest(1,2)));
......@@ -618,7 +618,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varbinary(2) DEFAULT NULL
`c1` varbinary(1) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(case when 11 then 22 else 33 end));
......
......@@ -1017,7 +1017,7 @@ create table t1 as select concat(least(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(2) CHARACTER SET cp1251 DEFAULT NULL
`c1` varchar(1) CHARACTER SET cp1251 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(greatest(1,2)));
......@@ -1027,7 +1027,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(2) CHARACTER SET cp1251 DEFAULT NULL
`c1` varchar(1) CHARACTER SET cp1251 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(case when 11 then 22 else 33 end));
......
......@@ -1314,7 +1314,7 @@ create table t1 as select concat(least(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(2) DEFAULT NULL
`c1` varchar(1) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(greatest(1,2)));
......@@ -1324,7 +1324,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(2) DEFAULT NULL
`c1` varchar(1) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(case when 11 then 22 else 33 end));
......
......@@ -2193,7 +2193,7 @@ create table t1 as select concat(least(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(2) CHARACTER SET ucs2 DEFAULT NULL
`c1` varchar(1) CHARACTER SET ucs2 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(greatest(1,2)));
......@@ -2203,7 +2203,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(2) CHARACTER SET ucs2 DEFAULT NULL
`c1` varchar(1) CHARACTER SET ucs2 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(case when 11 then 22 else 33 end));
......
......@@ -3065,7 +3065,7 @@ create table t1 as select concat(least(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(2) CHARACTER SET utf8 DEFAULT NULL
`c1` varchar(1) CHARACTER SET utf8 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(greatest(1,2)));
......@@ -3075,7 +3075,7 @@ create table t1 as select concat(greatest(1,2)) as c1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(2) CHARACTER SET utf8 DEFAULT NULL
`c1` varchar(1) CHARACTER SET utf8 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select hex(concat(case when 11 then 22 else 33 end));
......
......@@ -1615,8 +1615,8 @@ def coalesce___b 254 1 1 Y 0 39 8
def coalesce_b_b 254 1 1 Y 0 39 8
def if_______b_b 254 1 1 Y 0 39 8
def ifnull___b_b 254 1 1 Y 0 39 8
def least____b_b 254 1 1 Y 0 0 8
def greatest_b_b 254 1 1 Y 0 0 8
def least____b_b 254 1 1 Y 0 39 8
def greatest_b_b 254 1 1 Y 0 39 8
___________a a
case_______a a
case_____a_a a
......@@ -2190,10 +2190,10 @@ def if_______a_b 12 19 19 Y 128 0 63
def if_______b_a 12 19 19 Y 128 0 63
def ifnull___a_b 12 19 19 Y 128 0 63
def ifnull___b_a 12 19 19 Y 128 0 63
def least____a_b 12 10 19 Y 128 0 63
def least____b_a 12 10 19 Y 128 0 63
def greatest_a_b 12 10 19 Y 128 0 63
def greatest_b_a 12 10 19 Y 128 0 63
def least____a_b 12 19 19 Y 128 0 63
def least____b_a 12 19 19 Y 128 0 63
def greatest_a_b 12 19 19 Y 128 0 63
def greatest_b_a 12 19 19 Y 128 0 63
case_____a_b 2010-01-01 00:00:00
case_____b_a 2001-01-01 10:20:30
coalesce_a_b 2010-01-01 00:00:00
......@@ -2317,8 +2317,8 @@ def coalesce___a 7 19 19 N 129 0 63
def coalesce_a_a 7 19 19 N 129 0 63
def if_______a_a 7 19 19 N 129 0 63
def ifnull___a_a 7 19 19 N 129 0 63
def least____a_a 7 19 19 N 161 0 63
def greatest_a_a 7 19 19 N 161 0 63
def least____a_a 7 19 19 N 129 0 63
def greatest_a_a 7 19 19 N 129 0 63
def test t1 t1 b ___________b 11 10 8 Y 128 0 63
def case_______b 11 10 8 Y 128 0 63
def case_____b_b 11 10 8 Y 128 0 63
......@@ -3050,7 +3050,7 @@ DROP TABLE t1;
SET timestamp=UNIX_TIMESTAMP('2010-01-01 01:02:03');
SELECT GREATEST(CURRENT_TIME, CURRENT_DATE), COALESCE(CURRENT_TIME, CURRENT_DATE);
Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
def GREATEST(CURRENT_TIME, CURRENT_DATE) 12 10 19 N 129 0 63
def GREATEST(CURRENT_TIME, CURRENT_DATE) 12 19 19 N 129 0 63
def COALESCE(CURRENT_TIME, CURRENT_DATE) 12 19 19 N 129 0 63
GREATEST(CURRENT_TIME, CURRENT_DATE) COALESCE(CURRENT_TIME, CURRENT_DATE)
2010-01-01 01:02:03 2010-01-01 01:02:03
......@@ -3058,9 +3058,14 @@ CREATE TABLE t1 (a TIMESTAMP);
INSERT INTO t1 VALUES ('2010-01-01 10:20:30');
SELECT GREATEST(a,a) FROM t1;
Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
def GREATEST(a,a) 7 19 19 N 161 0 63
def GREATEST(a,a) 7 19 19 N 129 0 63
GREATEST(a,a)
2010-01-01 10:20:30
SELECT COALESCE(a,a) FROM t1;
Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
def COALESCE(a,a) 7 19 19 N 129 0 63
COALESCE(a,a)
2010-01-01 10:20:30
DROP TABLE t1;
CREATE TABLE t1 (a TIMESTAMP, b DATETIME);
CREATE TABLE t2 AS SELECT LEAST(a,a),LEAST(b,b),LEAST(a,b) FROM t1;
......@@ -3445,3 +3450,55 @@ DROP TABLE t1;
#
# End of 10.1 tests
#
#
# Start of 10.3 tests
#
#
# MDEV-12497 Wrong data type for LEAST(latin1_expr, utf8_expr)
#
CREATE TABLE t1 AS SELECT
LEAST(_latin1'aaa',_utf8 0xC39F) AS c1,
COALESCE(_latin1'aaa',_utf8 0xC39F) AS c2;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(3) CHARACTER SET utf8 NOT NULL,
`c2` varchar(3) CHARACTER SET utf8 NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
SELECT * FROM t1;
c1 c2
aaa aaa
DROP TABLE t1;
#
# MDEV-12504 Wrong data type for LEAST(date_expr,time_expr)
#
CREATE TABLE t1 AS SELECT
LEAST(DATE'2001-01-01', TIME'10:20:30') AS c1,
CONCAT(LEAST(DATE'2001-01-01', TIME'10:20:30')) AS c2;
SELECT * FROM t1;
c1 c2
2001-01-01 00:00:00 2001-01-01 00:00:00
DROP TABLE t1;
#
# MDEV-12505 Wrong data type for GREATEST(bit_column, int_column)
#
CREATE TABLE t1 (a BIT(64),b INT);
INSERT INTO t1 VALUES (0xFFFFFFFFFFFFFFFF,-1);
SELECT a>b, COALESCE(a,b), GREATEST(a,b) FROM t1;
a>b COALESCE(a,b) GREATEST(a,b)
1 18446744073709551615 18446744073709551615
CREATE TABLE t2 AS SELECT COALESCE(a,b),GREATEST(a,b) FROM t1;
SELECT * FROM t2;
COALESCE(a,b) GREATEST(a,b)
18446744073709551615 18446744073709551615
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`COALESCE(a,b)` decimal(64,0) DEFAULT NULL,
`GREATEST(a,b)` decimal(64,0) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t2;
DROP TABLE t1;
#
# End of 10.3 tests
#
......@@ -2792,7 +2792,7 @@ t2 CREATE TABLE `t2` (
CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1
ERROR:
Illegal parameter data types set and geometry for operation 'UNION'
# This does not preserve geometry type (MDEV-9405)
# This does not preserve geometry type (MDEV-12560)
CREATE TABLE t1 AS SELECT COALESCE(NULL, Point(1,1));
SHOW CREATE TABLE t1;
Table Create Table
......@@ -3711,6 +3711,195 @@ CASE a WHEN POINT(1,1) THEN "a" WHEN POINT(1,2) THEN "b" END
DROP PROCEDURE p1;
DROP PROCEDURE p2;
#
# MDEV-12506 Split Item_func_min_max::fix_length_and_dec() into methods in Type_handler
#
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 LEAST(a,b) FROM t1');
DROP TABLE t1;
END;
$$
-------------------------------------
CREATE TABLE t1 (a CHAR(10), b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
Table Create Table
t2 CREATE TABLE `t2` (
`LEAST(a,b)` longblob DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
-------------------------------------
CREATE TABLE t1 (a VARCHAR(10), b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
Table Create Table
t2 CREATE TABLE `t2` (
`LEAST(a,b)` longblob DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
-------------------------------------
CREATE TABLE t1 (a TINYTEXT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
Table Create Table
t2 CREATE TABLE `t2` (
`LEAST(a,b)` longblob DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
-------------------------------------
CREATE TABLE t1 (a TEXT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
Table Create Table
t2 CREATE TABLE `t2` (
`LEAST(a,b)` longblob DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
-------------------------------------
CREATE TABLE t1 (a MEDIUMTEXT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
Table Create Table
t2 CREATE TABLE `t2` (
`LEAST(a,b)` longblob DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
-------------------------------------
CREATE TABLE t1 (a LONGTEXT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
Table Create Table
t2 CREATE TABLE `t2` (
`LEAST(a,b)` longblob DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
-------------------------------------
CREATE TABLE t1 (a TINYINT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types tinyint and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a SMALLINT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types smallint and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a MEDIUMINT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types mediumint and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a INT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types int and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a BIGINT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types bigint and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a FLOAT, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types float and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a DOUBLE, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types double and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a DECIMAL(10,2), b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types decimal and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a BIT(8), b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types bit and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a TIME, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types time and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a DATE, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types date and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a DATETIME, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types datetime and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a TIMESTAMP, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types timestamp and geometry for operation 'least'
-------------------------------------
CREATE TABLE t1 (a YEAR, b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
ERROR:
Illegal parameter data types year and geometry for operation 'least'
# This LEAST(ENUM,GEOMETRY) creates BLOB, but fails on error with UNION (see MDEV-12503)
-------------------------------------
CREATE TABLE t1 (a ENUM(0x61), b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
Table Create Table
t2 CREATE TABLE `t2` (
`LEAST(a,b)` longblob DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
-------------------------------------
CREATE TABLE t1 (a SET(0x61), b Point)
CREATE TABLE t2 AS SELECT LEAST(a,b) FROM t1
Table Create Table
t2 CREATE TABLE `t2` (
`LEAST(a,b)` longblob DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
# This does not preserve geometry type (MDEV-9405)
CREATE TABLE t1 AS SELECT LEAST(NULL, Point(1,1));
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`LEAST(NULL, Point(1,1))` geometry DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
DROP PROCEDURE p1;
DROP PROCEDURE p2;
#
# MDEV-12001 Split Item_func_round::fix_length_and_dec to virtual methods in Type_handler
#
CREATE TABLE t1 (a GEOMETRY);
......
......@@ -155,6 +155,8 @@ CREATE TABLE t1 (a TIMESTAMP);
INSERT INTO t1 VALUES ('2010-01-01 10:20:30');
# Expect TIMESTAMP type (7) in metadata
SELECT GREATEST(a,a) FROM t1;
# Similar to this one
SELECT COALESCE(a,a) FROM t1;
DROP TABLE t1;
--disable_metadata
--enable_ps_protocol
......@@ -459,3 +461,47 @@ DROP TABLE t1;
--echo #
--echo # End of 10.1 tests
--echo #
--echo #
--echo # Start of 10.3 tests
--echo #
--echo #
--echo # MDEV-12497 Wrong data type for LEAST(latin1_expr, utf8_expr)
--echo #
CREATE TABLE t1 AS SELECT
LEAST(_latin1'aaa',_utf8 0xC39F) AS c1,
COALESCE(_latin1'aaa',_utf8 0xC39F) AS c2;
SHOW CREATE TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # MDEV-12504 Wrong data type for LEAST(date_expr,time_expr)
--echo #
CREATE TABLE t1 AS SELECT
LEAST(DATE'2001-01-01', TIME'10:20:30') AS c1,
CONCAT(LEAST(DATE'2001-01-01', TIME'10:20:30')) AS c2;
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # MDEV-12505 Wrong data type for GREATEST(bit_column, int_column)
--echo #
CREATE TABLE t1 (a BIT(64),b INT);
INSERT INTO t1 VALUES (0xFFFFFFFFFFFFFFFF,-1);
SELECT a>b, COALESCE(a,b), GREATEST(a,b) FROM t1;
CREATE TABLE t2 AS SELECT COALESCE(a,b),GREATEST(a,b) FROM t1;
SELECT * FROM t2;
SHOW CREATE TABLE t2;
DROP TABLE t2;
DROP TABLE t1;
--echo #
--echo # End of 10.3 tests
--echo #
......@@ -1789,7 +1789,7 @@ 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)
--echo # This does not preserve geometry type (MDEV-12560)
CREATE TABLE t1 AS SELECT COALESCE(NULL, Point(1,1));
SHOW CREATE TABLE t1;
DROP TABLE t1;
......@@ -1869,6 +1869,77 @@ DROP PROCEDURE p1;
DROP PROCEDURE p2;
--echo #
--echo # MDEV-12506 Split Item_func_min_max::fix_length_and_dec() into methods in Type_handler
--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 LEAST(a,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 LEAST(ENUM,GEOMETRY) creates BLOB, but fails on error with UNION (see MDEV-12503)
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 LEAST(NULL, Point(1,1));
SHOW CREATE TABLE t1;
DROP TABLE t1;
DROP PROCEDURE p1;
DROP PROCEDURE p2;
--echo #
--echo # MDEV-12001 Split Item_func_round::fix_length_and_dec to virtual methods in Type_handler
--echo #
......
......@@ -10187,7 +10187,8 @@ void Item_cache_row::set_null()
Item_type_holder::Item_type_holder(THD *thd, Item *item)
:Item(thd, item),
Type_handler_hybrid_field_type(item->real_type_handler()),
enum_set_typelib(0)
enum_set_typelib(0),
geometry_type(Field::GEOM_GEOMETRY)
{
DBUG_ASSERT(item->fixed);
maybe_null= item->maybe_null;
......
......@@ -2697,119 +2697,12 @@ double Item_func_units::val_real()
}
void Item_func_min_max::fix_length_and_dec()
bool Item_func_min_max::fix_attributes(Item **items, uint nitems)
{
uint unsigned_count= 0;
int max_int_part=0;
decimals=0;
max_length=0;
maybe_null=0;
Item_result tmp_cmp_type= args[0]->cmp_type();
uint string_type_count= 0;
uint temporal_type_count= 0;
enum_field_types temporal_field_type= MYSQL_TYPE_DATETIME;
for (uint i=0 ; i < arg_count ; i++)
{
set_if_bigger(max_length, args[i]->max_length);
set_if_bigger(decimals, args[i]->decimals);
set_if_bigger(max_int_part, args[i]->decimal_int_part());
unsigned_count+= args[i]->unsigned_flag;
if (args[i]->maybe_null)
maybe_null= 1;
tmp_cmp_type= item_cmp_type(tmp_cmp_type, args[i]->cmp_type());
string_type_count+= args[i]->cmp_type() == STRING_RESULT;
if (args[i]->cmp_type() == TIME_RESULT)
{
if (!temporal_type_count)
temporal_field_type= args[i]->field_type();
else
temporal_field_type= Field::field_type_merge(temporal_field_type,
args[i]->field_type());
temporal_type_count++;
}
}
unsigned_flag= unsigned_count == arg_count; // if all args are unsigned
switch (tmp_cmp_type) {
case TIME_RESULT:
// At least one temporal argument was found.
collation.set_numeric();
set_handler_by_field_type(temporal_field_type);
if (is_temporal_type_with_time(temporal_field_type))
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
else
decimals= 0;
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
No numeric and no temporal types were found.
*/
agg_arg_charsets_for_string_result_with_comparison(collation,
args, arg_count);
break;
case INT_RESULT:
/*
All arguments have INT-alike types:
TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT.
*/
collation.set_numeric();
fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part +
decimals,
decimals,
unsigned_flag));
if (unsigned_count != 0 && unsigned_count != arg_count)
{
/*
If all args are of INT-alike type, but have different unsigned_flag,
then change type to DECIMAL.
*/
set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
}
else
{
/*
There are only INT-alike arguments with equal unsigned_flag.
Aggregate types to get the best covering type.
Treat BIT as LONGLONG when aggregating to non-BIT types.
Possible final type: TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT.
*/
if (aggregate_for_result(func_name(), args, arg_count, true))
return;
}
break;
case DECIMAL_RESULT:
// All arguments are of DECIMAL type
collation.set_numeric();
fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part +
decimals,
decimals,
unsigned_flag));
set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
break;
case ROW_RESULT:
DBUG_ASSERT(0);
// Pass through
case REAL_RESULT:
collation.set_numeric();
fix_char_length(float_length(decimals));
/*
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 aggregate_for_result()
and fix Item_func::create_tmp_field() to create Field_float when possible.
*/
set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
break;
}
bool rc= Item_func_min_max::type_handler()->
Item_func_min_max_fix_attributes(current_thd, this, items, nitems);
DBUG_ASSERT(!rc || current_thd->is_error());
return rc;
}
......
......@@ -1434,6 +1434,8 @@ class Item_func_min_max :public Item_hybrid_func
{
String tmp_value;
int cmp_sign;
protected:
bool fix_attributes(Item **item, uint nitems);
public:
Item_func_min_max(THD *thd, List<Item> &list, int cmp_sign_arg):
Item_hybrid_func(thd, list), cmp_sign(cmp_sign_arg)
......@@ -1474,7 +1476,32 @@ class Item_func_min_max :public Item_hybrid_func
return Item_func_min_max::type_handler()->
Item_func_min_max_get_date(this, res, fuzzy_date);
}
void fix_length_and_dec();
void aggregate_attributes_real(Item **items, uint nitems)
{
/*
Aggregating attributes for the double data type for LEAST/GREATEST
is almost the same with aggregating for CASE-alike hybrid functions,
(CASE..THEN, COALESCE, IF, etc).
There is one notable difference though, when a numeric argument is mixed
with a string argument:
- CASE-alike functions return a string data type in such cases
COALESCE(10,'x') -> VARCHAR(2) = '10'
- LEAST/GREATEST returns double:
GREATEST(10,'10e4') -> DOUBLE = 100000
As the string argument can represent a number in the scientific notation,
like in the example above, max_length of the result can be longer than
max_length of the arguments. To handle this properly, max_length is
additionally assigned to the result of float_length(decimals).
*/
Item_func::aggregate_attributes_real(items, nitems);
max_length= float_length(decimals);
}
void fix_length_and_dec()
{
if (aggregate_for_min_max(func_name(), args, arg_count))
return;
fix_attributes(args, arg_count);
}
};
class Item_func_min :public Item_func_min_max
......
......@@ -370,7 +370,6 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
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();
......@@ -388,26 +387,6 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
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;
}
......@@ -479,6 +458,114 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
}
/**
Aggregate data type handler for LEAST/GRATEST.
aggregate_for_min_max() is close to aggregate_for_comparison(),
but tries to preserve the exact type handler for string, int and temporal
data types (instead of converting to super-types).
FLOAT is not preserved and is converted to its super-type (DOUBLE).
This should probably fixed eventually, for symmetry.
*/
bool
Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h)
{
if (!m_type_handler->is_traditional_type() ||
!h->is_traditional_type())
{
/*
If at least one data type is non-traditional,
do aggregation for result immediately.
For now we suppose that these two expressions:
- LEAST(type1, type2)
- COALESCE(type1, type2)
return the same data type (or both expressions return error)
if type1 and/or type2 are non-traditional.
This may change in the future.
*/
h= type_handler_data->
m_type_aggregator_for_result.find_handler(m_type_handler, h);
if (!h)
return true;
m_type_handler= h;
return false;
}
Item_result a= cmp_type();
Item_result b= h->cmp_type();
DBUG_ASSERT(a != ROW_RESULT); // Disallowed by check_cols() in fix_fields()
DBUG_ASSERT(b != ROW_RESULT); // Disallowed by check_cols() in fix_fields()
if (a == STRING_RESULT && b == STRING_RESULT)
m_type_handler=
Type_handler::aggregate_for_result_traditional(m_type_handler, h);
else if (a == INT_RESULT && b == INT_RESULT)
{
// BIT aggregates with non-BIT as BIGINT
if (m_type_handler != h)
{
if (m_type_handler == &type_handler_bit)
m_type_handler= &type_handler_longlong;
else if (h == &type_handler_bit)
h= &type_handler_longlong;
}
m_type_handler=
Type_handler::aggregate_for_result_traditional(m_type_handler, h);
}
else if (a == TIME_RESULT || b == TIME_RESULT)
{
if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
{
/*
We're here if there's only one temporal data type:
either m_type_handler or h.
*/
if (b == TIME_RESULT)
m_type_handler= h; // Temporal types bit non-temporal types
}
else
{
/*
We're here if both m_type_handler and h are temporal data types.
*/
m_type_handler=
Type_handler::aggregate_for_result_traditional(m_type_handler, h);
}
}
else if ((a == INT_RESULT || a == DECIMAL_RESULT) &&
(b == INT_RESULT || b == DECIMAL_RESULT))
{
m_type_handler= &type_handler_newdecimal;
}
else
{
m_type_handler= &type_handler_double;
}
return false;
}
bool
Type_handler_hybrid_field_type::aggregate_for_min_max(const char *funcname,
Item **items, uint nitems)
{
// LEAST/GREATEST require at least two arguments
DBUG_ASSERT(nitems > 1);
set_handler(items[0]->type_handler());
for (uint i= 1; i < nitems; i++)
{
const Type_handler *cur= items[i]->type_handler();
if (aggregate_for_min_max(cur))
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
type_handler()->name().ptr(), cur->name().ptr(), funcname);
return true;
}
}
return false;
}
const Type_handler *
Type_handler::aggregate_for_num_op_traditional(const Type_handler *h0,
const Type_handler *h1)
......@@ -1313,6 +1400,17 @@ bool Type_handler_int_result::
Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
Item **items, uint nitems) const
{
uint unsigned_flag= items[0]->unsigned_flag;
for (uint i= 1; i < nitems; i++)
{
if (unsigned_flag != items[i]->unsigned_flag)
{
// Convert a mixture of signed and unsigned int to decimal
func->set_handler(&type_handler_newdecimal);
func->aggregate_attributes_decimal(items, nitems);
return false;
}
}
func->aggregate_attributes_int(items, nitems);
return false;
}
......@@ -1379,6 +1477,33 @@ bool Type_handler_timestamp_common::
return false;
}
/*************************************************************************/
bool Type_handler::
Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
Item **items, uint nitems) const
{
/*
Aggregating attributes for LEAST/GREATES is exactly the same
with aggregating for CASE-alike functions (e.g. COALESCE)
for the majority of data type handlers.
*/
return Item_hybrid_func_fix_attributes(thd, func, items, nitems);
}
bool Type_handler_real_result::
Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
Item **items, uint nitems) const
{
/*
DOUBLE is an exception and aggregates attributes differently
for LEAST/GREATEST vs CASE-alike functions. See the comment in
Item_func_min_max::aggregate_attributes_real().
*/
func->aggregate_attributes_real(items, nitems);
return false;
}
/*************************************************************************/
......
......@@ -485,6 +485,10 @@ class Type_handler
virtual bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
Item **items,
uint nitems) const= 0;
virtual bool Item_func_min_max_fix_attributes(THD *thd,
Item_func_min_max *func,
Item **items,
uint nitems) const;
virtual bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const= 0;
virtual bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const= 0;
virtual bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const= 0;
......@@ -840,6 +844,8 @@ class Type_handler_real_result: public Type_handler_numeric
bool set_comparator_func(Arg_comparator *cmp) const;
bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func,
Item **items, uint nitems) const;
bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
Item **items, uint nitems) const;
bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
......@@ -1602,6 +1608,7 @@ class Type_handler_set: public Type_handler_string_result
class Type_handler_hybrid_field_type
{
const Type_handler *m_type_handler;
bool aggregate_for_min_max(const Type_handler *other);
public:
Type_handler_hybrid_field_type();
Type_handler_hybrid_field_type(const Type_handler *handler)
......@@ -1657,6 +1664,8 @@ class Type_handler_hybrid_field_type
bool aggregate_for_result(const Type_handler *other);
bool aggregate_for_result(const char *funcname,
Item **item, uint nitems, bool treat_bit_as_number);
bool aggregate_for_min_max(const char *funcname, Item **item, uint nitems);
bool aggregate_for_num_op(const class Type_aggregator *aggregator,
const Type_handler *h0, const Type_handler *h1);
};
......
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