diff options
author | Alexander Barkov <bar@mariadb.org> | 2017-05-10 15:29:48 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-05-15 23:45:31 +0400 |
commit | 705fc43eaafccd7a41e541f3149a917850f4e2fb (patch) | |
tree | 979e2c40485d6c3f614c33ea2934b2be3aee71ff | |
parent | 7c44b8afb781c533898dfb7321dd5cbb88c32ce0 (diff) | |
download | mariadb-git-705fc43eaafccd7a41e541f3149a917850f4e2fb.tar.gz |
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)
-rw-r--r-- | mysql-test/r/cte_recursive.result | 2 | ||||
-rw-r--r-- | mysql-test/r/metadata.result | 6 | ||||
-rw-r--r-- | mysql-test/r/subselect.result | 2 | ||||
-rw-r--r-- | mysql-test/r/subselect_no_exists_to_in.result | 2 | ||||
-rw-r--r-- | mysql-test/r/subselect_no_mat.result | 2 | ||||
-rw-r--r-- | mysql-test/r/subselect_no_opts.result | 2 | ||||
-rw-r--r-- | mysql-test/r/subselect_no_scache.result | 2 | ||||
-rw-r--r-- | mysql-test/r/subselect_no_semijoin.result | 2 | ||||
-rw-r--r-- | mysql-test/r/temp_table.result | 16 | ||||
-rw-r--r-- | mysql-test/r/type_float.result | 6 | ||||
-rw-r--r-- | mysql-test/r/union.result | 215 | ||||
-rw-r--r-- | mysql-test/t/union.test | 127 | ||||
-rw-r--r-- | sql/item.cc | 193 | ||||
-rw-r--r-- | sql/item.h | 66 | ||||
-rw-r--r-- | sql/item_func.cc | 4 | ||||
-rw-r--r-- | sql/item_func.h | 4 | ||||
-rw-r--r-- | sql/sql_lex.h | 11 | ||||
-rw-r--r-- | sql/sql_type.cc | 104 | ||||
-rw-r--r-- | sql/sql_type.h | 91 | ||||
-rw-r--r-- | sql/sql_union.cc | 217 | ||||
-rw-r--r-- | storage/tokudb/mysql-test/tokudb/r/type_float.result | 6 |
21 files changed, 755 insertions, 325 deletions
diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result index f691db27239..946ba16ac5c 100644 --- a/mysql-test/r/cte_recursive.result +++ b/mysql-test/r/cte_recursive.result @@ -130,7 +130,7 @@ select t2.a+1 from t1,t2 where t1.a=t2.a select * from t1; show columns from v1; Field Type Null Key Default Extra -a bigint(20) YES NULL +a bigint(12) YES NULL # WITH RECURSIVE : types of t1 columns are determined by anchor parts create view v2 as with recursive diff --git a/mysql-test/r/metadata.result b/mysql-test/r/metadata.result index 3ff332e03cc..cc249a7eab7 100644 --- a/mysql-test/r/metadata.result +++ b/mysql-test/r/metadata.result @@ -147,7 +147,7 @@ id data data 2 female no select t1.id from t1 union select t2.id from t2; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def id id 1 4 1 Y 32768 0 63 +def id id 246 4 1 Y 32768 0 63 id 1 2 @@ -168,12 +168,12 @@ def aaa @arg00 @arg00 8 20 1 Y 32768 0 63 1 select 1 union select 1; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def 1 1 3 11 1 N 32769 0 63 +def 1 1 3 1 1 N 32769 0 63 1 1 select * from (select 1 union select 1) aaa; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def aaa 1 1 3 11 1 N 32769 0 63 +def aaa 1 1 3 1 1 N 32769 0 63 1 1 drop table t1; diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index b83ee20e784..95a872f1498 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1260,7 +1260,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` int(3) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_exists_to_in.result b/mysql-test/r/subselect_no_exists_to_in.result index 87f33d1dbf4..7f07b974bb2 100644 --- a/mysql-test/r/subselect_no_exists_to_in.result +++ b/mysql-test/r/subselect_no_exists_to_in.result @@ -1264,7 +1264,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` int(3) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index 626990f2c9e..57c7a979d61 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -1267,7 +1267,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` int(3) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index 3637604646f..6e8be4a02a7 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -1263,7 +1263,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` int(3) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result index afebd27c192..1b437f6919d 100644 --- a/mysql-test/r/subselect_no_scache.result +++ b/mysql-test/r/subselect_no_scache.result @@ -1266,7 +1266,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` int(3) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index 0f0fc59341e..6094c7e029d 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -1263,7 +1263,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(20) NOT NULL + `a` int(3) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); diff --git a/mysql-test/r/temp_table.result b/mysql-test/r/temp_table.result index b833f4f9768..d1bec4a2af5 100644 --- a/mysql-test/r/temp_table.result +++ b/mysql-test/r/temp_table.result @@ -181,20 +181,20 @@ CREATE TABLE t2 ( c FLOAT(30,18) ); INSERT INTO t2 VALUES( 123456 ); SELECT AVG( c ) FROM t1 UNION SELECT 1; AVG( c ) -12139 -1 +12139.000000000000000000 +1.000000000000000000 SELECT 1 UNION SELECT AVG( c ) FROM t1; 1 -1 -12139 +1.000000000000000000 +12139.000000000000000000 SELECT 1 UNION SELECT * FROM t2 UNION SELECT 1; 1 -1 -123456 +1.000000000000000000 +123456.000000000000000000 SELECT c/1 FROM t1 UNION SELECT 1; c/1 -12139 -1 +12139.000000000000000000 +1.000000000000000000 DROP TABLE t1, t2; create temporary table t1 (a int); insert into t1 values (4711); diff --git a/mysql-test/r/type_float.result b/mysql-test/r/type_float.result index 2d138542950..9a92ff21e9f 100644 --- a/mysql-test/r/type_float.result +++ b/mysql-test/r/type_float.result @@ -232,12 +232,12 @@ insert into t2 values ("1.23456780"); create table t3 select * from t2 union select * from t1; select * from t3; d -1.2345678 -100000000 +1.234567800 +100000000.000000000 show create table t3; Table Create Table t3 CREATE TABLE `t3` ( - `d` double DEFAULT NULL + `d` double(18,9) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1, t2, t3; create table t1 select 105213674794682365.00 + 0.0 x; diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 9949defebf7..e0aa93d5c97 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -852,7 +852,7 @@ select * from t1; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `1` int(11) NOT NULL DEFAULT 0 + `1` int(2) NOT NULL DEFAULT 0 ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 select _latin1"test" union select _latin2"testt" ; @@ -1608,7 +1608,7 @@ NULL binary(0) YES NULL CREATE TABLE t5 SELECT NULL UNION SELECT NULL; DESC t5; Field Type Null Key Default Extra -NULL binary(0) YES NULL +NULL null YES NULL CREATE TABLE t6 SELECT * FROM (SELECT * FROM (SELECT NULL)a) b UNION SELECT a FROM t1; DESC t6; @@ -2195,16 +2195,223 @@ CREATE OR REPLACE TABLE t1 AS SELECT 1 UNION SELECT 1; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `1` int(11) NOT NULL DEFAULT 0 + `1` int(1) NOT NULL DEFAULT 0 ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t1; CREATE OR REPLACE TABLE t1 AS SELECT * FROM (SELECT 1 UNION SELECT 1) AS t0; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `1` int(11) NOT NULL DEFAULT 0 + `1` int(1) NOT NULL DEFAULT 0 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +# +# MDEV-9495 Wrong field type for a UNION of a signed and an unsigned INT expression +# +CREATE TABLE t1 (a INT, b INT UNSIGNED); +INSERT INTO t1 VALUES (0x7FFFFFFF,0xFFFFFFFF); +CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t2 ORDER BY a; +a +2147483647 +4294967295 +DROP TABLE t2; +CREATE TABLE t2 AS SELECT COALESCE(a,b), COALESCE(b,a) FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `COALESCE(a,b)` decimal(10,0) DEFAULT NULL, + `COALESCE(b,a)` decimal(10,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t2; +COALESCE(a,b) COALESCE(b,a) +2147483647 4294967295 +DROP TABLE t2; +DROP TABLE t1; +# +# MDEV-9497 UNION and COALESCE produce different field types for DECIMAL+INT +# +CREATE TABLE t1 AS SELECT COALESCE(10.1,CAST(10 AS UNSIGNED)) AS a; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` decimal(3,1) NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE OR REPLACE TABLE t1 AS SELECT 10.1 AS a UNION SELECT CAST(10 AS UNSIGNED); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` decimal(3,1) NOT NULL DEFAULT 0.0 ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t1; # +# MDEV-12594 UNION between fixed length double columns does not always preserve scale +# +CREATE TABLE t1 (a FLOAT(20,4), b FLOAT(20,3), c FLOAT(20,4)); +INSERT INTO t1 VALUES (1111,2222,3333); +CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT a FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` float(20,4) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +CREATE OR REPLACE TABLE t2 SELECT a FROM t1 UNION SELECT c FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` float(20,4) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +CREATE OR REPLACE TABLE t2 SELECT b FROM t1 UNION SELECT b FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `b` float(20,3) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +CREATE OR REPLACE TABLE t2 SELECT c FROM t1 UNION SELECT c FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `c` float(20,4) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +CREATE OR REPLACE TABLE t2 SELECT c FROM t1 UNION SELECT a FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `c` float(20,4) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +CREATE OR REPLACE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` float(21,4) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +CREATE OR REPLACE TABLE t2 AS SELECT b FROM t1 UNION SELECT a FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `b` float(21,4) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +DROP TABLE t1; +# Corner case +CREATE TABLE t1 (a FLOAT(255,4), b FLOAT(255,3)); +INSERT INTO t1 VALUES (1111,2222); +CREATE OR REPLACE TABLE t2 AS SELECT b FROM t1 UNION SELECT a FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `b` float(255,4) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +DROP TABLE t1; +# +# MDEV-12595 UNION converts INT to BIGINT +# +CREATE TABLE t1 AS SELECT +1, +-1, +COALESCE(1,1), +COALESCE(-1,-1), +COALESCE(1,-1), +COALESCE(-1,1); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `1` int(1) NOT NULL, + `-1` int(2) NOT NULL, + `COALESCE(1,1)` int(1) NOT NULL, + `COALESCE(-1,-1)` int(2) NOT NULL, + `COALESCE(1,-1)` int(2) NOT NULL, + `COALESCE(-1,1)` int(2) NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 AS SELECT 1 AS c1,1 AS c2,-1 AS c3,-1 AS c4 UNION SELECT 1,-1,1,-1; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(1) NOT NULL DEFAULT 0, + `c2` int(2) NOT NULL DEFAULT 0, + `c3` int(2) NOT NULL DEFAULT 0, + `c4` int(2) NOT NULL DEFAULT 0 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +# +# MDEV-12599 UNION is not symmetric when mixing INT and CHAR +# +CREATE OR REPLACE TABLE t1 AS SELECT 1 AS c1, 'a' AS c2 UNION SELECT 'a', 1; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` varchar(1) NOT NULL DEFAULT '', + `c2` varchar(1) NOT NULL DEFAULT '' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE OR REPLACE TABLE t1 AS SELECT 11112222 AS c1, 'a' AS c2 UNION SELECT 'a', 11112222; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` varchar(8) NOT NULL DEFAULT '', + `c2` varchar(8) NOT NULL DEFAULT '' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE OR REPLACE TABLE t1 AS SELECT 111122223333 AS c1, 'a' AS c2 UNION SELECT 'a', 111122223333; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` varchar(12) NOT NULL DEFAULT '', + `c2` varchar(12) NOT NULL DEFAULT '' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE OR REPLACE TABLE t1 AS SELECT 1111222233334444 AS c1, 'a' AS c2 UNION SELECT 'a', 1111222233334444; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` varchar(16) NOT NULL DEFAULT '', + `c2` varchar(16) NOT NULL DEFAULT '' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 (a INT(3), b VARCHAR(1)); +CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` varchar(11) DEFAULT NULL, + `b` varchar(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +DROP TABLE t1; +CREATE TABLE t1 (a BIGINT(3), b VARCHAR(1)); +CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` varchar(20) DEFAULT NULL, + `b` varchar(20) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +DROP TABLE t1; +CREATE TABLE t1 (a BIGINT(12), b VARCHAR(1)); +CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` varchar(20) DEFAULT NULL, + `b` varchar(20) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +DROP TABLE t1; +# # End of 10.3 tests # diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 04ab71588be..ce8b2bc9c2a 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -1545,5 +1545,132 @@ SHOW CREATE TABLE t1; DROP TABLE t1; --echo # +--echo # MDEV-9495 Wrong field type for a UNION of a signed and an unsigned INT expression +--echo # +CREATE TABLE t1 (a INT, b INT UNSIGNED); +INSERT INTO t1 VALUES (0x7FFFFFFF,0xFFFFFFFF); +CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1; +SHOW CREATE TABLE t2; +SELECT * FROM t2 ORDER BY a; +DROP TABLE t2; +CREATE TABLE t2 AS SELECT COALESCE(a,b), COALESCE(b,a) FROM t1; +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; +DROP TABLE t1; + +--echo # +--echo # MDEV-9497 UNION and COALESCE produce different field types for DECIMAL+INT +--echo # +CREATE TABLE t1 AS SELECT COALESCE(10.1,CAST(10 AS UNSIGNED)) AS a; +SHOW CREATE TABLE t1; +DROP TABLE t1; +CREATE OR REPLACE TABLE t1 AS SELECT 10.1 AS a UNION SELECT CAST(10 AS UNSIGNED); +SHOW CREATE TABLE t1; +DROP TABLE t1; + +--echo # +--echo # MDEV-12594 UNION between fixed length double columns does not always preserve scale +--echo # +CREATE TABLE t1 (a FLOAT(20,4), b FLOAT(20,3), c FLOAT(20,4)); +INSERT INTO t1 VALUES (1111,2222,3333); + +CREATE TABLE t2 AS SELECT a FROM t1 UNION SELECT a FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE OR REPLACE TABLE t2 SELECT a FROM t1 UNION SELECT c FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE OR REPLACE TABLE t2 SELECT b FROM t1 UNION SELECT b FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE OR REPLACE TABLE t2 SELECT c FROM t1 UNION SELECT c FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE OR REPLACE TABLE t2 SELECT c FROM t1 UNION SELECT a FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE OR REPLACE TABLE t2 AS SELECT a FROM t1 UNION SELECT b FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE OR REPLACE TABLE t2 AS SELECT b FROM t1 UNION SELECT a FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +DROP TABLE t1; + +--echo # Corner case +CREATE TABLE t1 (a FLOAT(255,4), b FLOAT(255,3)); +INSERT INTO t1 VALUES (1111,2222); +CREATE OR REPLACE TABLE t2 AS SELECT b FROM t1 UNION SELECT a FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; +DROP TABLE t1; + + +--echo # +--echo # MDEV-12595 UNION converts INT to BIGINT +--echo # +CREATE TABLE t1 AS SELECT + 1, + -1, + COALESCE(1,1), + COALESCE(-1,-1), + COALESCE(1,-1), + COALESCE(-1,1); +SHOW CREATE TABLE t1; +DROP TABLE t1; +CREATE TABLE t1 AS SELECT 1 AS c1,1 AS c2,-1 AS c3,-1 AS c4 UNION SELECT 1,-1,1,-1; +SHOW CREATE TABLE t1; +DROP TABLE t1; + + +--echo # +--echo # MDEV-12599 UNION is not symmetric when mixing INT and CHAR +--echo # + +CREATE OR REPLACE TABLE t1 AS SELECT 1 AS c1, 'a' AS c2 UNION SELECT 'a', 1; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE OR REPLACE TABLE t1 AS SELECT 11112222 AS c1, 'a' AS c2 UNION SELECT 'a', 11112222; +SHOW CREATE TABLE t1; +DROP TABLE t1; + + +CREATE OR REPLACE TABLE t1 AS SELECT 111122223333 AS c1, 'a' AS c2 UNION SELECT 'a', 111122223333; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE OR REPLACE TABLE t1 AS SELECT 1111222233334444 AS c1, 'a' AS c2 UNION SELECT 'a', 1111222233334444; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 (a INT(3), b VARCHAR(1)); +CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; +DROP TABLE t1; + +CREATE TABLE t1 (a BIGINT(3), b VARCHAR(1)); +CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; +DROP TABLE t1; + +CREATE TABLE t1 (a BIGINT(12), b VARCHAR(1)); +CREATE TABLE t2 AS SELECT a,b FROM t1 UNION SELECT b,a FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t2; +DROP TABLE t1; + + +--echo # --echo # End of 10.3 tests --echo # diff --git a/sql/item.cc b/sql/item.cc index e5ea5537acc..a9c7604d154 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -9772,199 +9772,6 @@ 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()), - Type_geometry_attributes(item), - enum_set_typelib(0) -{ - DBUG_ASSERT(item->fixed); - maybe_null= item->maybe_null; - get_full_info(item); - DBUG_ASSERT(!decimals || result_type() != INT_RESULT); - prev_decimal_int_part= item->decimal_int_part(); -} - - -/** - Find field type which can carry current Item_type_holder type and - type of given Item. - - @param thd thread handler - @param item given item to join its parameters with this item ones - - @retval - TRUE error - types are incompatible - @retval - FALSE OK -*/ - -bool Item_type_holder::join_types(THD *thd, Item *item) -{ - uint max_length_orig= max_length; - uint decimals_orig= decimals; - DBUG_ENTER("Item_type_holder::join_types"); - DBUG_PRINT("info:", ("was type %s len %d, dec %d name %s", - real_type_handler()->name().ptr(), max_length, decimals, - (name.str ? name.str : "<NULL>"))); - DBUG_PRINT("info:", ("in type %s len %d, dec %d", - item->real_type_handler()->name().ptr(), - item->max_length, item->decimals)); - const Type_handler *item_type_handler= item->real_type_handler(); - if (aggregate_for_result(item_type_handler)) - { - my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), - Item_type_holder::real_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 (result_type() == INT_RESULT) - decimals= 0; - else - decimals= MY_MAX(decimals, item->decimals); - - Type_geometry_attributes::join(item); - - if (result_type() == DECIMAL_RESULT) - { - decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE); - int item_int_part= item->decimal_int_part(); - int item_prec = MY_MAX(prev_decimal_int_part, item_int_part) + decimals; - int precision= MY_MIN(item_prec, DECIMAL_MAX_PRECISION); - unsigned_flag&= item->unsigned_flag; - max_length= my_decimal_precision_to_length_no_truncation(precision, - decimals, - unsigned_flag); - } - - switch (result_type()) - { - case STRING_RESULT: - { - const char *old_cs, *old_derivation; - uint32 old_max_chars= max_length / collation.collation->mbmaxlen; - old_cs= collation.collation->name; - old_derivation= collation.derivation_name(); - if (collation.aggregate(item->collation, MY_COLL_ALLOW_CONV)) - { - my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), - old_cs, old_derivation, - item->collation.collation->name, - item->collation.derivation_name(), - "UNION"); - DBUG_RETURN(TRUE); - } - /* - To figure out max_length, we have to take into account possible - expansion of the size of the values because of character set - conversions. - */ - if (collation.collation != &my_charset_bin) - { - max_length= MY_MAX(old_max_chars * collation.collation->mbmaxlen, - item->max_display_length() / - item->collation.collation->mbmaxlen * - collation.collation->mbmaxlen); - } - else - set_if_bigger(max_length, item->max_display_length()); - break; - } - case REAL_RESULT: - { - if (decimals != NOT_FIXED_DEC) - { - /* - For FLOAT(M,D)/DOUBLE(M,D) do not change precision - if both fields have the same M and D - */ - if (item->max_length != max_length_orig || - item->decimals != decimals_orig) - { - int delta1= max_length_orig - decimals_orig; - int delta2= item->max_length - item->decimals; - max_length= MY_MAX(delta1, delta2) + decimals; - if (Item_type_holder::real_type_handler() == &type_handler_float && - max_length > FLT_DIG + 2) - { - max_length= MAX_FLOAT_STR_LENGTH; - decimals= NOT_FIXED_DEC; - } - else if (Item_type_holder::real_type_handler() == &type_handler_double && - max_length > DBL_DIG + 2) - { - max_length= MAX_DOUBLE_STR_LENGTH; - decimals= NOT_FIXED_DEC; - } - } - } - else - max_length= (Item_type_holder::field_type() == MYSQL_TYPE_FLOAT) ? - FLT_DIG+6 : DBL_DIG+7; - break; - } - default: - max_length= MY_MAX(max_length, item->max_display_length()); - }; - maybe_null|= item->maybe_null; - get_full_info(item); - /* - Adjust data type for union, e.g.: - - convert type_handler_null to type_handler_string - - convert type_handler_olddecimal to type_handler_newdecimal - - adjust varchar/blob according to max_length - */ - set_handler(Item_type_holder:: - real_type_handler()->type_handler_for_union(this)); - - /* Remember decimal integer part to be used in DECIMAL_RESULT handleng */ - prev_decimal_int_part= decimal_int_part(); - DBUG_PRINT("info", ("become type: %s len: %u dec: %u", - real_type_handler()->name().ptr(), - max_length, (uint) decimals)); - DBUG_RETURN(FALSE); -} - - -/** - Get full information from Item about enum/set fields to be able to create - them later. - - @param item Item for information collection -*/ -void Item_type_holder::get_full_info(Item *item) -{ - if (Item_type_holder::real_type_handler() == &type_handler_enum || - Item_type_holder::real_type_handler() == &type_handler_set) - { - TYPELIB *item_typelib= item->get_typelib(); - /* - We can have enum/set type after merging only if we have one enum|set - field (or MIN|MAX(enum|set field)) and number of NULL fields - */ - DBUG_ASSERT(item->real_type_handler() == &type_handler_null || - (enum_set_typelib && !item_typelib) || - (!enum_set_typelib && item_typelib)); - if (!enum_set_typelib) - { - enum_set_typelib= ((Field_enum*)((Item_field *) item->real_item())->field)->typelib; - } - } -} - - double Item_type_holder::val_real() { DBUG_ASSERT(0); // should never be called diff --git a/sql/item.h b/sql/item.h index 0ff7deb479f..2de5214c99e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -787,7 +787,13 @@ public: { return type_handler()->max_display_length(this); } - virtual TYPELIB *get_typelib() const { return NULL; } + TYPELIB *get_typelib() const { return NULL; } + void set_maybe_null(bool maybe_null_arg) { maybe_null= maybe_null_arg; } + void set_typelib(TYPELIB *typelib) + { + // Non-field Items (e.g. hybrid functions) never have ENUM/SET types yet. + DBUG_ASSERT(0); + } Item_cache* get_cache(THD *thd) const { return type_handler()->Item_get_cache(thd, this); @@ -1740,6 +1746,10 @@ public: { return Field::GEOM_GEOMETRY; }; uint uint_geometry_type() const { return get_geometry_type(); } + void set_geometry_type(uint type) + { + DBUG_ASSERT(0); + } String *check_well_formed_result(String *str, bool send_error= 0); bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); bool too_big_for_varchar() const @@ -1842,27 +1852,28 @@ class Type_geometry_attributes { uint m_geometry_type; static const uint m_geometry_type_unknown= Field::GEOM_GEOMETRYCOLLECTION + 1; - void copy(const Item *item) + void copy(const Type_handler *handler, const Type_all_attributes *gattr) { // Ignore implicit NULLs - m_geometry_type= item->type_handler() == &type_handler_geometry ? - item->uint_geometry_type() : + m_geometry_type= handler == &type_handler_geometry ? + gattr->uint_geometry_type() : m_geometry_type_unknown; } public: Type_geometry_attributes() :m_geometry_type(m_geometry_type_unknown) { } - Type_geometry_attributes(const Item *item) + Type_geometry_attributes(const Type_handler *handler, + const Type_all_attributes *gattr) :m_geometry_type(m_geometry_type_unknown) { - copy(item); + copy(handler, gattr); } void join(const Item *item) { // Ignore implicit NULLs if (m_geometry_type == m_geometry_type_unknown) - copy(item); + copy(item->type_handler(), item); else if (item->type_handler() == &type_handler_geometry) { m_geometry_type= @@ -1902,7 +1913,6 @@ class Item_args protected: Item **args, *tmp_arg[2]; uint arg_count; - bool alloc_arguments(THD *thd, uint count); void set_arguments(THD *thd, List<Item> &list); bool walk_args(Item_processor processor, bool walk_subquery, void *arg) { @@ -1961,6 +1971,11 @@ public: set_arguments(thd, list); } Item_args(THD *thd, const Item_args *other); + bool alloc_arguments(THD *thd, uint count); + void add_argument(Item *item) + { + args[arg_count++]= item; + } inline Item **arguments() const { return args; } inline uint argument_count() const { return arg_count; } inline void remove_arguments() { arg_count=0; } @@ -3079,6 +3094,9 @@ public: Field::geometry_type get_geometry_type() const { return Type_geometry_attributes::get_geometry_type(); }; + void set_geometry_type(uint type) + { Type_geometry_attributes::set_geometry_type(type); } + Item_param(THD *thd, const LEX_CSTRING *name_arg, uint pos_in_query_arg, uint len_in_query_arg); @@ -5841,12 +5859,29 @@ class Item_type_holder: public Item, { protected: TYPELIB *enum_set_typelib; - void get_full_info(Item *item); - - /* It is used to count decimal precision in join_types */ - int prev_decimal_int_part; public: - Item_type_holder(THD*, Item*); + Item_type_holder(THD *thd, Item *item) + :Item(thd, item), + Type_handler_hybrid_field_type(item->real_type_handler()), + enum_set_typelib(0) + { + DBUG_ASSERT(item->fixed); + maybe_null= item->maybe_null; + } + Item_type_holder(THD *thd, + const LEX_CSTRING *name_arg, + const Type_handler *handler, + const Type_all_attributes *attr, + bool maybe_null_arg) + :Item(thd), + Type_handler_hybrid_field_type(handler), + Type_geometry_attributes(handler, attr), + enum_set_typelib(attr->get_typelib()) + { + name= *name_arg; + Type_std_attributes::set(*attr); + maybe_null= maybe_null_arg; + } const Type_handler *type_handler() const { @@ -5864,7 +5899,6 @@ public: longlong val_int(); my_decimal *val_decimal(my_decimal *); String *val_str(String*); - bool join_types(THD *thd, Item *); Field *create_tmp_field(bool group, TABLE *table) { return Item_type_holder::real_type_handler()-> @@ -5875,6 +5909,10 @@ public: { return Type_geometry_attributes::get_geometry_type(); } + void set_geometry_type(uint type) + { + Type_geometry_attributes::set_geometry_type(type); + } Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } }; diff --git a/sql/item_func.cc b/sql/item_func.cc index 4bfb463d825..cb5dd172634 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -546,7 +546,9 @@ my_decimal *Item_func::val_decimal(my_decimal *decimal_value) bool Item_hybrid_func::fix_attributes(Item **items, uint nitems) { bool rc= Item_hybrid_func::type_handler()-> - Item_hybrid_func_fix_attributes(current_thd, this, items, nitems); + Item_hybrid_func_fix_attributes(current_thd, + func_name(), this, this, + items, nitems); DBUG_ASSERT(!rc || current_thd->is_error()); return rc; } diff --git a/sql/item_func.h b/sql/item_func.h index 3ff3047b335..860a4fdcc7d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -398,6 +398,10 @@ public: { return Type_handler_hybrid_field_type::type_handler(); } Field::geometry_type get_geometry_type() const { return Type_geometry_attributes::get_geometry_type(); }; + void set_geometry_type(uint type) + { + Type_geometry_attributes::set_geometry_type(type); + } }; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 85ce07be5a8..4ac407b260d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -640,6 +640,14 @@ protected: ulonglong found_rows_for_union; bool saved_error; + bool prepare_join(THD *thd, SELECT_LEX *sl, select_result *result, + ulong additional_options, + bool is_union_select); + bool join_union_item_types(THD *thd, List<Item> &types, uint count); + bool join_union_type_handlers(THD *thd, + class Type_holder *holders, uint count); + bool join_union_type_attributes(THD *thd, + class Type_holder *holders, uint count); public: // Ensures that at least all members used during cleanup() are initialized. st_select_lex_unit() @@ -749,9 +757,6 @@ public: /* UNION methods */ bool prepare(THD *thd, select_result *result, ulong additional_options); - bool prepare_join(THD *thd, SELECT_LEX *sl, select_result *result, - ulong additional_options, - bool is_union_select); bool optimize(); bool exec(); bool exec_recursive(); diff --git a/sql/sql_type.cc b/sql/sql_type.cc index d17f9f5927e..1b2798f9467 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -482,29 +482,18 @@ const Type_handler *Type_handler_row::type_handler_for_comparison() const /***************************************************************************/ -const Type_handler *Type_handler_enum::type_handler_for_item_field() const +const Type_handler *Type_handler_typelib::type_handler_for_item_field() const { return &type_handler_string; } -const Type_handler *Type_handler_enum::cast_to_int_type_handler() const +const Type_handler *Type_handler_typelib::cast_to_int_type_handler() const { return &type_handler_longlong; } -const Type_handler *Type_handler_set::type_handler_for_item_field() const -{ - return &type_handler_string; -} - - -const Type_handler *Type_handler_set::cast_to_int_type_handler() const -{ - return &type_handler_longlong; -} - /***************************************************************************/ bool @@ -2080,7 +2069,10 @@ Type_handler_temporal_result::Item_get_cache(THD *thd, const Item *item) const /*************************************************************************/ bool Type_handler_int_result:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { uint unsigned_flag= items[0]->unsigned_flag; @@ -2089,7 +2081,7 @@ bool Type_handler_int_result:: if (unsigned_flag != items[i]->unsigned_flag) { // Convert a mixture of signed and unsigned int to decimal - func->set_handler(&type_handler_newdecimal); + handler->set_handler(&type_handler_newdecimal); func->aggregate_attributes_decimal(items, nitems); return false; } @@ -2100,7 +2092,10 @@ bool Type_handler_int_result:: bool Type_handler_real_result:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { func->aggregate_attributes_real(items, nitems); @@ -2109,7 +2104,10 @@ bool Type_handler_real_result:: bool Type_handler_decimal_result:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { func->aggregate_attributes_decimal(items, nitems); @@ -2118,26 +2116,59 @@ bool Type_handler_decimal_result:: bool Type_handler_string_result:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + return func->aggregate_attributes_string(func_name, items, nitems); +} + + + +/* + We can have enum/set type after merging only if we have one enum|set + field (or MIN|MAX(enum|set field)) and number of NULL fields +*/ +bool Type_handler_typelib:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { - return func->aggregate_attributes_string(func->func_name(), items, nitems); + TYPELIB *typelib= NULL; + for (uint i= 0; i < nitems; i++) + { + if ((typelib= items[i]->get_typelib())) + break; + } + DBUG_ASSERT(typelib); // There must be at least one typelib + func->set_typelib(typelib); + return func->aggregate_attributes_string(func_name, items, nitems); } bool Type_handler_blob_common:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { - if (func->aggregate_attributes_string(func->func_name(), items, nitems)) + if (func->aggregate_attributes_string(func_name, items, nitems)) return true; - func->set_handler(blob_type_handler(func->max_length)); + handler->set_handler(blob_type_handler(func->max_length)); return false; } bool Type_handler_date_common:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { func->fix_attributes_date(); @@ -2146,7 +2177,10 @@ bool Type_handler_date_common:: bool Type_handler_time_common:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { func->aggregate_attributes_temporal(MIN_TIME_WIDTH, items, nitems); @@ -2155,7 +2189,10 @@ bool Type_handler_time_common:: bool Type_handler_datetime_common:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems); @@ -2164,7 +2201,10 @@ bool Type_handler_datetime_common:: bool Type_handler_timestamp_common:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems); @@ -2173,11 +2213,14 @@ bool Type_handler_timestamp_common:: #ifdef HAVE_SPATIAL bool Type_handler_geometry:: - Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, Item **items, uint nitems) const { DBUG_ASSERT(nitems > 0); - Type_geometry_attributes gattr(items[0]); + Type_geometry_attributes gattr(items[0]->type_handler(), items[0]); for (uint i= 1; i < nitems; i++) gattr.join(items[i]); func->set_geometry_type(gattr.get_geometry_type()); @@ -2185,7 +2228,7 @@ bool Type_handler_geometry:: func->unsigned_flag= false; func->decimals= 0; func->max_length= (uint32) UINT_MAX32; - func->maybe_null= true; + func->set_maybe_null(true); return false; } #endif @@ -2202,7 +2245,8 @@ bool Type_handler:: 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); + return Item_hybrid_func_fix_attributes(thd, func->func_name(), + func, func, items, nitems); } diff --git a/sql/sql_type.h b/sql/sql_type.h index 5c134e7ba25..f41437ffe52 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -61,6 +61,7 @@ class Item_func_div; class Item_func_mod; class cmp_item; class in_vector; +class Type_handler_hybrid_field_type; class Sort_param; class Arg_comparator; struct st_value; @@ -468,6 +469,7 @@ public: :Type_std_attributes(other) { } virtual ~Type_all_attributes() {} + virtual void set_maybe_null(bool maybe_null_arg)= 0; // Returns total number of decimal digits virtual uint decimal_precision() const= 0; /* @@ -477,7 +479,9 @@ public: datatype indepented method. */ virtual uint uint_geometry_type() const= 0; - virtual TYPELIB *get_typelib() const { return NULL; } + virtual void set_geometry_type(uint type)= 0; + virtual TYPELIB *get_typelib() const= 0; + virtual void set_typelib(TYPELIB *typelib)= 0; }; @@ -774,7 +778,10 @@ public: const Item *cmp) const= 0; virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0; virtual bool set_comparator_func(Arg_comparator *cmp) const= 0; - virtual bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + virtual bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, Item **items, uint nitems) const= 0; virtual bool Item_func_min_max_fix_attributes(THD *thd, @@ -973,7 +980,10 @@ public: Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, Item **items, uint nitems) const { DBUG_ASSERT(0); @@ -1181,7 +1191,10 @@ public: Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, Item **items, uint nitems) const; bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, Item **items, uint nitems) const; @@ -1251,7 +1264,10 @@ public: Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, 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; @@ -1313,7 +1329,10 @@ public: Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, 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; @@ -1466,7 +1485,10 @@ public: Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; Item_cache *Item_get_cache(THD *thd, const Item *item) const; bool set_comparator_func(Arg_comparator *cmp) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, 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; @@ -1755,7 +1777,10 @@ public: } int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; String *print_item_value(THD *thd, Item *item, String *str) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, Item **items, uint nitems) const; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; bool set_comparator_func(Arg_comparator *cmp) const; @@ -1822,7 +1847,10 @@ public: } uint Item_decimal_precision(const Item *item) const; String *print_item_value(THD *thd, Item *item, String *str) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, Item **items, uint nitems) const; }; @@ -1879,7 +1907,10 @@ public: return Item_send_datetime(item, protocol, buf); } String *print_item_value(THD *thd, Item *item, String *str) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, Item **items, uint nitems) const; }; @@ -1941,7 +1972,10 @@ public: return Item_send_datetime(item, protocol, buf); } String *print_item_value(THD *thd, Item *item, String *str) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, Item **items, uint nitems) const; }; @@ -2123,7 +2157,10 @@ public: return false; // Materialization does not work with BLOB columns } bool is_param_long_data_type() const { return true; } - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, Item **items, uint nitems) const; }; @@ -2230,7 +2267,10 @@ public: bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; - bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *h, + Type_all_attributes *attr, Item **items, uint nitems) 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; @@ -2250,16 +2290,28 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_geometry type_handler_geometry; #endif -class Type_handler_enum: public Type_handler_string_result +class Type_handler_typelib: public Type_handler_string_result +{ +public: + virtual ~Type_handler_typelib() { } + enum_field_types field_type() const { return MYSQL_TYPE_STRING; } + const Type_handler *type_handler_for_item_field() const; + const Type_handler *cast_to_int_type_handler() const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; +}; + + +class Type_handler_enum: public Type_handler_typelib { static const Name m_name_enum; public: virtual ~Type_handler_enum() {} const Name name() const { return m_name_enum; } - enum_field_types field_type() const { return MYSQL_TYPE_STRING; } virtual enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; } - const Type_handler *type_handler_for_item_field() const; - const Type_handler *cast_to_int_type_handler() const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; Field *make_table_field(const LEX_CSTRING *name, @@ -2269,16 +2321,13 @@ public: }; -class Type_handler_set: public Type_handler_string_result +class Type_handler_set: public Type_handler_typelib { static const Name m_name_set; public: virtual ~Type_handler_set() {} const Name name() const { return m_name_set; } - enum_field_types field_type() const { return MYSQL_TYPE_STRING; } virtual enum_field_types real_field_type() const { return MYSQL_TYPE_SET; } - const Type_handler *type_handler_for_item_field() const; - const Type_handler *cast_to_int_type_handler() const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; Field *make_table_field(const LEX_CSTRING *name, diff --git a/sql/sql_union.cc b/sql/sql_union.cc index bf25c0be74e..1c2ff2b012b 100644 --- a/sql/sql_union.cc +++ b/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: /* diff --git a/storage/tokudb/mysql-test/tokudb/r/type_float.result b/storage/tokudb/mysql-test/tokudb/r/type_float.result index 6387cea5384..f8ce24f08c4 100644 --- a/storage/tokudb/mysql-test/tokudb/r/type_float.result +++ b/storage/tokudb/mysql-test/tokudb/r/type_float.result @@ -233,12 +233,12 @@ insert into t2 values ("1.23456780"); create table t3 select * from t2 union select * from t1; select * from t3; d -1.2345678 -100000000 +1.234567800 +100000000.000000000 show create table t3; Table Create Table t3 CREATE TABLE `t3` ( - `d` double DEFAULT NULL + `d` double(18,9) DEFAULT NULL ) ENGINE=ENGINE DEFAULT CHARSET=latin1 drop table t1, t2, t3; create table t1 select 105213674794682365.00 + 0.0 x; |