diff options
author | Alexander Barkov <bar@mariadb.com> | 2018-04-23 09:31:17 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.com> | 2018-04-23 09:31:17 +0400 |
commit | 6426b52ed43876ccc0142fc31f98dd6d42292388 (patch) | |
tree | e3e4d8ec47326db603c746e4d4eb975d9256c6a5 | |
parent | 9f84451d87ba1924539b7d2ab69619b71d0155b9 (diff) | |
download | mariadb-git-6426b52ed43876ccc0142fc31f98dd6d42292388.tar.gz |
MDEV-15957 Unexpected "Data too long" when doing CREATE..SELECT with stored func
Problems:
1. Unlike Item_field::fix_fields(),
Item_sum_sp::fix_length_and_dec() and Item_func_sp::fix_length_and_dec()
did not run the code which resided in adjust_max_effective_column_length(),
therefore they did not extend max_length for the integer return data types
from the user-specified length to the maximum length according to
the data type capacity.
2. The code in adjust_max_effective_column_length() was not correct
for TEXT data, because Field_blob::max_display_length()
multiplies to mbmaxlen. So TEXT variants were unintentionally
promoted to the next longer data type for multi-byte character
sets: TINYTEXT->TEXT, TEXT->MEDIUMTEXT, MEDIUMTEXT->LONGTEXT.
3. Item_sum_sp::create_table_field_from_handler()
Item_func_sp::create_table_field_from_handler()
erroneously called tmp_table_field_from_field_type(),
which converted VARCHAR(>512) to TEXT variants.
So "CREATE..SELECT spfunc()" erroneously converted
VARCHAR to TEXT. This was wrong, because stored
functions have explicitly declared data types,
which should be preserved.
Solution:
- Removing Type_std_attributes(const Field *)
and using instead Type_std_attributes::set() in combination
with field->type_str_attributes() all around the code, e.g.:
Type_std_attributes::set(field->type_std_attributes())
These two ways of copying attributes from a Field
to an Item duplicated each other, and were slightly
different in how to mix max_length and mbmaxlen.
- Removing adjust_max_effective_column_length() and
fixing Field::type_std_attributes() to do all necessary
type-specific calculations , so no further adjustments
is needed.
Field::type_std_attributes() is now called from all affected methods:
Item_field::fix_fields()
Item_sum_sp::fix_length_and_dec()
Item_func_sp::fix_length_and_dec()
This fixes the problem N1.
- Making Field::type_std_attributes() virtual, to make
sure that type-specific adjustments a properly done
by individual Field_xxx classes. Implementing
Field_blob::type_std_attributes() in the way that
no TEXT promotion is done.
This fixes the problem N2.
- Fixing Item_sum_sp::create_table_field_from_handler()
Item_func_sp::create_table_field_from_handler() to
call create_table_field_from_handler() instead of
tmp_table_field_from_field_type() to avoid
VARCHAR->TEXT conversion on "CREATE..SELECT spfunc()".
- Recording mysql-test/suite/compat/oracle/r/sp-param.result
as "CREATE..SELECT spfunc()" now correctly
preserve the data type as specified in the RETURNS clause.
- Adding new tests
-rw-r--r-- | mysql-test/main/custom_aggregate_functions.result | 176 | ||||
-rw-r--r-- | mysql-test/main/custom_aggregate_functions.test | 156 | ||||
-rw-r--r-- | mysql-test/main/sp.result | 104 | ||||
-rw-r--r-- | mysql-test/main/sp.test | 61 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/r/sp-param.result | 16 | ||||
-rw-r--r-- | sql/field.h | 26 | ||||
-rw-r--r-- | sql/item.cc | 31 | ||||
-rw-r--r-- | sql/item.h | 2 | ||||
-rw-r--r-- | sql/item_func.h | 2 | ||||
-rw-r--r-- | sql/item_sum.cc | 2 | ||||
-rw-r--r-- | sql/item_sum.h | 4 | ||||
-rw-r--r-- | sql/sp_head.cc | 5 | ||||
-rw-r--r-- | sql/sql_type.cc | 9 | ||||
-rw-r--r-- | sql/sql_type.h | 1 |
14 files changed, 543 insertions, 52 deletions
diff --git a/mysql-test/main/custom_aggregate_functions.result b/mysql-test/main/custom_aggregate_functions.result index 67be44c43f7..e08c5276ef4 100644 --- a/mysql-test/main/custom_aggregate_functions.result +++ b/mysql-test/main/custom_aggregate_functions.result @@ -947,3 +947,179 @@ select f1('2001-01-01'),cast(f1('2001-01-01') as time); f1('2001-01-01') cast(f1('2001-01-01') as time) 2001-01-01 00:00:00 drop function f1; +# +# MDEV-15957 Unexpected "Data too long" when doing CREATE..SELECT with stored functions +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +CREATE AGGREGATE FUNCTION f1(x INT) RETURNS INT(3) +BEGIN +DECLARE res INT DEFAULT 0; +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN res-200; +LOOP +FETCH GROUP NEXT ROW; +SET res= res + x; +END LOOP; +RETURN res; +END; +$$ +CREATE TABLE t2 AS SELECT CONCAT(f1(a)) FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `CONCAT(f1(a))` varchar(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1,t2; +DROP FUNCTION f1; +CREATE AGGREGATE FUNCTION f1() RETURNS TINYTEXT CHARACTER SET latin1 +BEGIN +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; +LOOP +FETCH GROUP NEXT ROW; +END LOOP; +RETURN ''; +END; +$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` tinytext DEFAULT NULL, + `c2` tinytext DEFAULT NULL, + `c3` varchar(255) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE AGGREGATE FUNCTION f1() RETURNS TEXT CHARACTER SET latin1 +BEGIN +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; +LOOP +FETCH GROUP NEXT ROW; +END LOOP; +RETURN ''; +END; +$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` text DEFAULT NULL, + `c2` text DEFAULT NULL, + `c3` text DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE AGGREGATE FUNCTION f1() RETURNS MEDIUMTEXT CHARACTER SET latin1 +BEGIN +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; +LOOP +FETCH GROUP NEXT ROW; +END LOOP; +RETURN ''; +END; +$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` mediumtext DEFAULT NULL, + `c2` mediumtext DEFAULT NULL, + `c3` mediumtext DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE AGGREGATE FUNCTION f1() RETURNS LONGTEXT CHARACTER SET latin1 +BEGIN +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; +LOOP +FETCH GROUP NEXT ROW; +END LOOP; +RETURN ''; +END; +$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` longtext DEFAULT NULL, + `c2` longtext DEFAULT NULL, + `c3` longtext DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE AGGREGATE FUNCTION f1() RETURNS TINYTEXT CHARACTER SET utf8 +BEGIN +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; +LOOP +FETCH GROUP NEXT ROW; +END LOOP; +RETURN ''; +END; +$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` tinytext CHARACTER SET utf8 DEFAULT NULL, + `c2` text CHARACTER SET utf8 DEFAULT NULL, + `c3` varchar(255) CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE AGGREGATE FUNCTION f1() RETURNS TEXT CHARACTER SET utf8 +BEGIN +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; +LOOP +FETCH GROUP NEXT ROW; +END LOOP; +RETURN ''; +END; +$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` text CHARACTER SET utf8 DEFAULT NULL, + `c2` mediumtext CHARACTER SET utf8 DEFAULT NULL, + `c3` mediumtext CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE AGGREGATE FUNCTION f1() RETURNS MEDIUMTEXT CHARACTER SET utf8 +BEGIN +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; +LOOP +FETCH GROUP NEXT ROW; +END LOOP; +RETURN ''; +END; +$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` mediumtext CHARACTER SET utf8 DEFAULT NULL, + `c2` longtext CHARACTER SET utf8 DEFAULT NULL, + `c3` longtext CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE AGGREGATE FUNCTION f1() RETURNS LONGTEXT CHARACTER SET utf8 +BEGIN +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; +LOOP +FETCH GROUP NEXT ROW; +END LOOP; +RETURN ''; +END; +$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` longtext CHARACTER SET utf8 DEFAULT NULL, + `c2` longtext CHARACTER SET utf8 DEFAULT NULL, + `c3` longtext CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; diff --git a/mysql-test/main/custom_aggregate_functions.test b/mysql-test/main/custom_aggregate_functions.test index 13eb3bed2af..8ede846c90c 100644 --- a/mysql-test/main/custom_aggregate_functions.test +++ b/mysql-test/main/custom_aggregate_functions.test @@ -783,3 +783,159 @@ end| delimiter ;| select f1('2001-01-01'),cast(f1('2001-01-01') as time); drop function f1; + + +--echo # +--echo # MDEV-15957 Unexpected "Data too long" when doing CREATE..SELECT with stored functions +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +DELIMITER $$; +CREATE AGGREGATE FUNCTION f1(x INT) RETURNS INT(3) +BEGIN + DECLARE res INT DEFAULT 0; + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN res-200; + LOOP + FETCH GROUP NEXT ROW; + SET res= res + x; + END LOOP; + RETURN res; +END; +$$ +DELIMITER ;$$ +CREATE TABLE t2 AS SELECT CONCAT(f1(a)) FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t1,t2; +DROP FUNCTION f1; + + +DELIMITER $$; +CREATE AGGREGATE FUNCTION f1() RETURNS TINYTEXT CHARACTER SET latin1 +BEGIN + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; + LOOP + FETCH GROUP NEXT ROW; + END LOOP; + RETURN ''; +END; +$$ +DELIMITER ;$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +DELIMITER $$; +CREATE AGGREGATE FUNCTION f1() RETURNS TEXT CHARACTER SET latin1 +BEGIN + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; + LOOP + FETCH GROUP NEXT ROW; + END LOOP; + RETURN ''; +END; +$$ +DELIMITER ;$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +DELIMITER $$; +CREATE AGGREGATE FUNCTION f1() RETURNS MEDIUMTEXT CHARACTER SET latin1 +BEGIN + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; + LOOP + FETCH GROUP NEXT ROW; + END LOOP; + RETURN ''; +END; +$$ +DELIMITER ;$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +DELIMITER $$; +CREATE AGGREGATE FUNCTION f1() RETURNS LONGTEXT CHARACTER SET latin1 +BEGIN + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; + LOOP + FETCH GROUP NEXT ROW; + END LOOP; + RETURN ''; +END; +$$ +DELIMITER ;$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + + +DELIMITER $$; +CREATE AGGREGATE FUNCTION f1() RETURNS TINYTEXT CHARACTER SET utf8 +BEGIN + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; + LOOP + FETCH GROUP NEXT ROW; + END LOOP; + RETURN ''; +END; +$$ +DELIMITER ;$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +DELIMITER $$; +CREATE AGGREGATE FUNCTION f1() RETURNS TEXT CHARACTER SET utf8 +BEGIN + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; + LOOP + FETCH GROUP NEXT ROW; + END LOOP; + RETURN ''; +END; +$$ +DELIMITER ;$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +DELIMITER $$; +CREATE AGGREGATE FUNCTION f1() RETURNS MEDIUMTEXT CHARACTER SET utf8 +BEGIN + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; + LOOP + FETCH GROUP NEXT ROW; + END LOOP; + RETURN ''; +END; +$$ +DELIMITER ;$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +DELIMITER $$; +CREATE AGGREGATE FUNCTION f1() RETURNS LONGTEXT CHARACTER SET utf8 +BEGIN + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ''; + LOOP + FETCH GROUP NEXT ROW; + END LOOP; + RETURN ''; +END; +$$ +DELIMITER ;$$ +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + diff --git a/mysql-test/main/sp.result b/mysql-test/main/sp.result index 582ea33d903..f6f34318584 100644 --- a/mysql-test/main/sp.result +++ b/mysql-test/main/sp.result @@ -8571,3 +8571,107 @@ affected rows: 5 DROP PROCEDURE p1; DROP PROCEDURE p2; drop table t1; +# +# MDEV-15957 Unexpected "Data too long" when doing CREATE..SELECT with stored functions +# +CREATE TABLE t1 (a INT(3)); +INSERT INTO t1 VALUES (-999); +CREATE FUNCTION f1(a INT(3)) RETURNS INT(3) RETURN a; +CREATE TABLE t2 AS SELECT CONCAT(a) AS c1, CONCAT(COALESCE(a)) AS c2, CONCAT(f1(a)) AS c3 FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `c1` varchar(11) DEFAULT NULL, + `c2` varchar(11) DEFAULT NULL, + `c3` varchar(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1,t2; +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS TINYTEXT CHARACTER SET latin1 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` tinytext DEFAULT NULL, + `c2` tinytext DEFAULT NULL, + `c3` varchar(255) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS TEXT CHARACTER SET latin1 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` text DEFAULT NULL, + `c2` text DEFAULT NULL, + `c3` text DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS MEDIUMTEXT CHARACTER SET latin1 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` mediumtext DEFAULT NULL, + `c2` mediumtext DEFAULT NULL, + `c3` mediumtext DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS LONGTEXT CHARACTER SET latin1 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` longtext DEFAULT NULL, + `c2` longtext DEFAULT NULL, + `c3` longtext DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS TINYTEXT CHARACTER SET utf8 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` tinytext CHARACTER SET utf8 DEFAULT NULL, + `c2` text CHARACTER SET utf8 DEFAULT NULL, + `c3` varchar(255) CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS TEXT CHARACTER SET utf8 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` text CHARACTER SET utf8 DEFAULT NULL, + `c2` mediumtext CHARACTER SET utf8 DEFAULT NULL, + `c3` mediumtext CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS MEDIUMTEXT CHARACTER SET utf8 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` mediumtext CHARACTER SET utf8 DEFAULT NULL, + `c2` longtext CHARACTER SET utf8 DEFAULT NULL, + `c3` longtext CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS LONGTEXT CHARACTER SET utf8 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` longtext CHARACTER SET utf8 DEFAULT NULL, + `c2` longtext CHARACTER SET utf8 DEFAULT NULL, + `c3` longtext CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +DROP FUNCTION f1; diff --git a/mysql-test/main/sp.test b/mysql-test/main/sp.test index 7a6fc3e711d..80d541ac77c 100644 --- a/mysql-test/main/sp.test +++ b/mysql-test/main/sp.test @@ -10120,3 +10120,64 @@ CALL p2(); DROP PROCEDURE p1; DROP PROCEDURE p2; drop table t1; + +--echo # +--echo # MDEV-15957 Unexpected "Data too long" when doing CREATE..SELECT with stored functions +--echo # + +CREATE TABLE t1 (a INT(3)); +INSERT INTO t1 VALUES (-999); +CREATE FUNCTION f1(a INT(3)) RETURNS INT(3) RETURN a; +CREATE TABLE t2 AS SELECT CONCAT(a) AS c1, CONCAT(COALESCE(a)) AS c2, CONCAT(f1(a)) AS c3 FROM t1; +SHOW CREATE TABLE t2; +DROP TABLE t1,t2; +DROP FUNCTION f1; + + +CREATE FUNCTION f1() RETURNS TINYTEXT CHARACTER SET latin1 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +CREATE FUNCTION f1() RETURNS TEXT CHARACTER SET latin1 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +CREATE FUNCTION f1() RETURNS MEDIUMTEXT CHARACTER SET latin1 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +CREATE FUNCTION f1() RETURNS LONGTEXT CHARACTER SET latin1 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +CREATE FUNCTION f1() RETURNS TINYTEXT CHARACTER SET utf8 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +CREATE FUNCTION f1() RETURNS TEXT CHARACTER SET utf8 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +CREATE FUNCTION f1() RETURNS MEDIUMTEXT CHARACTER SET utf8 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; + +CREATE FUNCTION f1() RETURNS LONGTEXT CHARACTER SET utf8 RETURN ''; +CREATE TABLE t1 AS SELECT f1() AS c1, COALESCE(f1()) AS c2, CONCAT(f1()) AS c3; +SHOW CREATE TABLE t1; +DROP TABLE t1; +DROP FUNCTION f1; diff --git a/mysql-test/suite/compat/oracle/r/sp-param.result b/mysql-test/suite/compat/oracle/r/sp-param.result index 68ecefa5077..aab1811ef67 100644 --- a/mysql-test/suite/compat/oracle/r/sp-param.result +++ b/mysql-test/suite/compat/oracle/r/sp-param.result @@ -14,7 +14,7 @@ CREATE TABLE t1 AS SELECT f1(REPEAT('a',2000)) AS a;; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE "t1" ( - "a" text DEFAULT NULL + "a" varchar(2000) DEFAULT NULL ) DROP TABLE t1; DROP FUNCTION f1; @@ -30,7 +30,7 @@ CREATE TABLE t1 AS SELECT f1(REPEAT('a',2000)) AS a;; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE "t1" ( - "a" text CHARACTER SET utf8 DEFAULT NULL + "a" varchar(2000) CHARACTER SET utf8 DEFAULT NULL ) DROP TABLE t1; DROP FUNCTION f1; @@ -46,7 +46,7 @@ CREATE TABLE t1 AS SELECT f1(REPEAT('a',2000)) AS a;; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE "t1" ( - "a" blob DEFAULT NULL + "a" varbinary(2000) DEFAULT NULL ) DROP TABLE t1; DROP FUNCTION f1; @@ -62,7 +62,7 @@ CREATE TABLE t1 AS SELECT f1(REPEAT('a',4000)) AS a;; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE "t1" ( - "a" text DEFAULT NULL + "a" varchar(4000) DEFAULT NULL ) DROP TABLE t1; DROP FUNCTION f1; @@ -78,7 +78,7 @@ CREATE TABLE t1 AS SELECT f1(REPEAT('a',4000)) AS a;; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE "t1" ( - "a" text DEFAULT NULL + "a" varchar(4000) DEFAULT NULL ) DROP TABLE t1; DROP FUNCTION f1; @@ -94,7 +94,7 @@ CREATE TABLE t1 AS SELECT f1(REPEAT('a',4000)) AS a;; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE "t1" ( - "a" text CHARACTER SET utf8 DEFAULT NULL + "a" varchar(4000) CHARACTER SET utf8 DEFAULT NULL ) DROP TABLE t1; DROP FUNCTION f1; @@ -110,7 +110,7 @@ CREATE TABLE t1 AS SELECT f1(REPEAT('a',4000)) AS a;; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE "t1" ( - "a" blob DEFAULT NULL + "a" varbinary(4000) DEFAULT NULL ) DROP TABLE t1; DROP FUNCTION f1; @@ -126,7 +126,7 @@ CREATE TABLE t1 AS SELECT f1(REPEAT('a',4000)) AS a;; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE "t1" ( - "a" blob DEFAULT NULL + "a" varbinary(4000) DEFAULT NULL ) DROP TABLE t1; DROP FUNCTION f1; diff --git a/sql/field.h b/sql/field.h index cb182877a9f..ed8a11806da 100644 --- a/sql/field.h +++ b/sql/field.h @@ -770,7 +770,7 @@ public: { return DTCollation(charset(), derivation(), repertoire()); } - Type_std_attributes type_std_attributes() const + virtual Type_std_attributes type_std_attributes() const { return Type_std_attributes(field_length, decimals(), MY_TEST(flags & UNSIGNED_FLAG), @@ -2002,6 +2002,24 @@ public: int store_time_dec(const MYSQL_TIME *ltime, uint dec); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); virtual uint numeric_precision() const= 0; + Type_std_attributes type_std_attributes() const + { + /* + For integer data types, the user-specified length does not constrain the + supported range, so e.g. a column of the INT(1) data type supports the + full integer range anyway. + Choose the maximum from the user-specified length and the maximum + possible length determined by the data type capacity: + INT(1) -> 11 + INT(10) -> 11 + INT(40) -> 40 + */ + uint32 length1= max_display_length(); + uint32 length2= field_length; + return Type_std_attributes(MY_MAX(length1, length2), decimals(), + MY_TEST(flags & UNSIGNED_FLAG), + dtcollation()); + } Information_schema_numeric_attributes information_schema_numeric_attributes() const { @@ -3581,6 +3599,12 @@ public: } enum ha_base_keytype key_type() const { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; } + Type_std_attributes type_std_attributes() const + { + return Type_std_attributes(Field_blob::max_display_length(), decimals(), + MY_TEST(flags & UNSIGNED_FLAG), + dtcollation()); + } Information_schema_character_attributes information_schema_character_attributes() const { diff --git a/sql/item.cc b/sql/item.cc index 35c5ab8cc3d..1f2ebbd8be5 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3165,45 +3165,16 @@ Item_field::Item_field(THD *thd, Item_field *item) } -/** - Calculate the max column length not taking into account the - limitations over integer types. - - When storing data into fields the server currently just ignores the - limits specified on integer types, e.g. 1234 can safely be stored in - an int(2) and will not cause an error. - Thus when creating temporary tables and doing transformations - we must adjust the maximum field length to reflect this fact. - We take the un-restricted maximum length and adjust it similarly to - how the declared length is adjusted wrt unsignedness etc. - TODO: this all needs to go when we disable storing 1234 in int(2). - - @param field_par Original field the use to calculate the lengths - @param max_length Item's calculated explicit max length - @return The adjusted max length -*/ - -inline static uint32 -adjust_max_effective_column_length(Field *field_par, uint32 max_length) -{ - uint32 new_max_length= field_par->max_display_length(); - /* Adjust only if the actual precision based one is bigger than specified */ - return new_max_length > max_length ? new_max_length : max_length; -} - - void Item_field::set_field(Field *field_par) { field=result_field=field_par; // for easy coding with fields maybe_null=field->maybe_null(); - Type_std_attributes::set(field_par); + Type_std_attributes::set(field_par->type_std_attributes()); table_name= *field_par->table_name; field_name= field_par->field_name; db_name= field_par->table->s->db.str; alias_name_used= field_par->table->alias_name_used; - max_length= adjust_max_effective_column_length(field_par, max_length); - fixed= 1; if (field->table->s->tmp_table == SYSTEM_TMP_TABLE) any_privileges= 0; diff --git a/sql/item.h b/sql/item.h index df08f5f1574..ad5a6217584 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2822,7 +2822,7 @@ public: const char *table_name_arg): Item(thd), field(par_field), db_name(db_arg), table_name(table_name_arg) { - Type_std_attributes::set(par_field); + Type_std_attributes::set(par_field->type_std_attributes()); } enum Type type() const { return FIELD_ITEM; } double val_real() { return field->val_real(); } diff --git a/sql/item_func.h b/sql/item_func.h index 4b4f94df329..c5fb759c2ac 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -2838,7 +2838,7 @@ public: { return result_type() != STRING_RESULT ? sp_result_field : - tmp_table_field_from_field_type(table); + create_table_field_from_handler(table); } void make_send_field(THD *thd, Send_field *tmp_field); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 2d47aeb179d..5b4cf075c64 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1386,7 +1386,7 @@ Item_sum_sp::fix_length_and_dec() { DBUG_ENTER("Item_sum_sp::fix_length_and_dec"); DBUG_ASSERT(sp_result_field); - Type_std_attributes::set(sp_result_field); + Type_std_attributes::set(sp_result_field->type_std_attributes()); Item_sum::fix_length_and_dec(); DBUG_VOID_RETURN; } diff --git a/sql/item_sum.h b/sql/item_sum.h index 1ed3d870bcc..ea8b2f149df 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1297,6 +1297,10 @@ public: { return SP_AGGREGATE_FUNC; } + Field *create_field_for_create_select(TABLE *table) + { + return create_table_field_from_handler(table); + } void fix_length_and_dec(); bool fix_fields(THD *thd, Item **ref); const char *func_name() const; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f7847bae89d..edaa4de942e 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -905,8 +905,13 @@ sp_head::create_result_field(uint field_max_length, const LEX_CSTRING *field_nam Perhaps we should refactor prepare_create_field() to set Create_field::length to maximum octet length for BLOBs, instead of packed length). + + Note, for integer data types, field_max_length can be bigger + than the user specified length, e.g. a field of the INT(1) data type + is translated to the item with max_length=11. */ DBUG_ASSERT(field_max_length <= m_return_field_def.length || + m_return_field_def.type_handler()->cmp_type() == INT_RESULT || (current_thd->stmt_arena->is_stmt_execute() && m_return_field_def.length == 8 && (m_return_field_def.pack_flag & diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 421ff0ed0f1..cc7100cd38d 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -160,15 +160,6 @@ void Temporal_with_date::make_from_item(THD *thd, Item *item, sql_mode_t flags) } -void Type_std_attributes::set(const Field *field) -{ - decimals= field->decimals(); - unsigned_flag= MY_TEST(field->flags & UNSIGNED_FLAG); - collation.set(field->charset(), field->derivation(), field->repertoire()); - fix_char_length(field->char_length()); -} - - uint Type_std_attributes::count_max_decimals(Item **item, uint nitems) { uint res= 0; diff --git a/sql/sql_type.h b/sql/sql_type.h index 8d1884f3860..c5490c4359d 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -617,7 +617,6 @@ public: { *this= other; } - void set(const Field *field); uint32 max_char_length() const { return max_length / collation.collation->mbmaxlen; } void fix_length_and_charset(uint32 max_char_length_arg, CHARSET_INFO *cs) |