maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #10694
MDEV-12775 Reuse data type aggregation code for hybrid functions and UNION
Hello Sanja,
Please review a patch for MDEV-12775, also fixing a number of bugs:
- 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
Thanks!
commit 66ac73266b39443797940f09751a372d489ed8e8
Author: Alexander Barkov <bar@xxxxxxxxxxx>
Date: Wed May 10 15:29:48 2017 +0400
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)
diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result
index f691db2..946ba16 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 3ff332e..cc249a7 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 b83ee20..95a872f 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 87f33d1..7f07b97 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 626990f..57c7a97 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 3637604..6e8be4a 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 afebd27..1b437f6 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 0f0fc59..6094c7e 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 b833f4f..d1bec4a 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 43aed60..ed62c5c 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 9949def..e0aa93d 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 04ab715..ce8b2bc 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 2094502..b8f6655 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -9809,199 +9809,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 4ef2375..9a3775c 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -787,7 +787,13 @@ class Item: public Value_source,
{
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 @@ class Item: public Value_source,
{ 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 @@ class Item_args
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; }
@@ -5837,12 +5852,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
{
@@ -5860,7 +5892,6 @@ class Item_type_holder: public Item,
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()->
@@ -5871,6 +5902,10 @@ class Item_type_holder: public Item,
{
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 c3048a0..d552f23 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 18612db..ca9830c 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -398,6 +398,10 @@ class Item_hybrid_func: public Item_func,
{ 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 85ce07b..4ac407b 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -640,6 +640,14 @@ class st_select_lex_unit: public st_select_lex_node {
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 @@ class st_select_lex_unit: public st_select_lex_node {
/* 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 f632b47..47e511a 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 8abc8e6..92ffd43 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -60,6 +60,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;
@@ -467,6 +468,7 @@ class Type_all_attributes: public Type_std_attributes
: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;
/*
@@ -476,7 +478,9 @@ class Type_all_attributes: public Type_std_attributes
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;
};
@@ -769,7 +773,10 @@ class Type_handler
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,
@@ -964,7 +971,10 @@ class Type_handler_row: public Type_handler
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);
@@ -1168,7 +1178,10 @@ class Type_handler_real_result: public Type_handler_numeric
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;
@@ -1234,7 +1247,10 @@ class Type_handler_decimal_result: public Type_handler_numeric
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;
@@ -1292,7 +1308,10 @@ class Type_handler_int_result: public Type_handler_numeric
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;
@@ -1437,7 +1456,10 @@ class Type_handler_string_result: public Type_handler
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;
@@ -1726,7 +1748,10 @@ class Type_handler_time_common: public Type_handler_temporal_result
}
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;
@@ -1793,7 +1818,10 @@ class Type_handler_date_common: public Type_handler_temporal_with_date
}
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;
};
@@ -1850,7 +1878,10 @@ class Type_handler_datetime_common: public Type_handler_temporal_with_date
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;
};
@@ -1912,7 +1943,10 @@ class Type_handler_timestamp_common: public Type_handler_temporal_with_date
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;
};
@@ -2094,7 +2128,10 @@ class Type_handler_blob_common: public Type_handler_longstr
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;
};
@@ -2197,7 +2234,10 @@ class Type_handler_geometry: public Type_handler_string_result
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;
@@ -2217,16 +2257,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,
@@ -2236,16 +2288,13 @@ class Type_handler_enum: public Type_handler_string_result
};
-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 bf25c0b..0eb9b7f 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 maybe_null;
+public:
+ Type_holder()
+ :m_typelib(NULL),
+ maybe_null(false)
+ { }
+
+ void set_maybe_null(bool maybe_null_arg) { maybe_null= maybe_null_arg; }
+ bool get_maybe_null() const { return 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++)
+ 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 6387cea..f8ce24f 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;