diff --git a/mysql-test/r/type_enum.result b/mysql-test/r/type_enum.result index 2a08a140087..02d71c6bd9f 100644 --- a/mysql-test/r/type_enum.result +++ b/mysql-test/r/type_enum.result @@ -2225,6 +2225,9 @@ a DROP TABLE t1; # +# Start of 10.3 tests +# +# # MDEV-12432 Range optimizer for ENUM and SET does not return "Impossible WHERE" in some case # CREATE TABLE t1 (a ENUM('a','b','c','1'),KEY(a)); @@ -2257,3 +2260,54 @@ EXPLAIN SELECT * FROM t1 WHERE a='1.1'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables DROP TABLE t1; +# +# MDEV-12656 Crash in CREATE..SELECT..UNION with a ENUM column and NULL +# +CREATE TABLE t1 (a ENUM('a')); +# non-UNION + table column +CREATE TABLE t2 AS SELECT (SELECT a FROM t1); +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `(SELECT a FROM t1)` varchar(1) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +# UNION + table column +CREATE TABLE t2 AS SELECT (SELECT a FROM t1 UNION SELECT NULL); +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `(SELECT a FROM t1 UNION SELECT NULL)` varchar(1) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +# UNION + SP variable +CREATE PROCEDURE p1() +BEGIN +DECLARE va ENUM('a'); +CREATE TABLE t2 AS SELECT (SELECT va FROM t1 UNION SELECT NULL); +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +$$ +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `(SELECT va FROM t1 UNION SELECT NULL)` varchar(1) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP PROCEDURE p1; +# UNION + anchored SP variable +CREATE PROCEDURE p1() +BEGIN +DECLARE va TYPE OF t1.a; +CREATE TABLE t2 AS SELECT (SELECT va FROM t1 UNION SELECT NULL); +SHOW CREATE TABLE t2; +DROP TABLE t2; +END; +$$ +CALL p1(); +Table Create Table +t2 CREATE TABLE `t2` ( + `(SELECT va FROM t1 UNION SELECT NULL)` varchar(1) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP PROCEDURE p1; +DROP TABLE t1; diff --git a/mysql-test/t/type_enum.test b/mysql-test/t/type_enum.test index d00cf66ccb0..a79335960bc 100644 --- a/mysql-test/t/type_enum.test +++ b/mysql-test/t/type_enum.test @@ -457,6 +457,10 @@ ALTER TABLE t1 MODIFY a ENUM('2001','2002'); SELECT * FROM t1; DROP TABLE t1; +--echo # +--echo # Start of 10.3 tests +--echo # + --echo # --echo # MDEV-12432 Range optimizer for ENUM and SET does not return "Impossible WHERE" in some case --echo # @@ -473,3 +477,44 @@ EXPLAIN SELECT * FROM t1 WHERE a='1x'; EXPLAIN SELECT * FROM t1 WHERE a='1.0'; EXPLAIN SELECT * FROM t1 WHERE a='1.1'; DROP TABLE t1; + +--echo # +--echo # MDEV-12656 Crash in CREATE..SELECT..UNION with a ENUM column and NULL +--echo # + +CREATE TABLE t1 (a ENUM('a')); +--echo # non-UNION + table column +CREATE TABLE t2 AS SELECT (SELECT a FROM t1); +SHOW CREATE TABLE t2; +DROP TABLE t2; +--echo # UNION + table column +CREATE TABLE t2 AS SELECT (SELECT a FROM t1 UNION SELECT NULL); +SHOW CREATE TABLE t2; +DROP TABLE t2; +--echo # UNION + SP variable +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE va ENUM('a'); + CREATE TABLE t2 AS SELECT (SELECT va FROM t1 UNION SELECT NULL); + SHOW CREATE TABLE t2; + DROP TABLE t2; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +--echo # UNION + anchored SP variable +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE va TYPE OF t1.a; + CREATE TABLE t2 AS SELECT (SELECT va FROM t1 UNION SELECT NULL); + SHOW CREATE TABLE t2; + DROP TABLE t2; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; diff --git a/sql/item.cc b/sql/item.cc index c0c99dc22bb..416f889712a 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -10072,7 +10072,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) if (aggregate_for_result(item_type_handler)) { my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), - Item_type_holder::type_handler()->name().ptr(), + Item_type_holder::real_type_handler()->name().ptr(), item_type_handler->name().ptr(), "UNION"); DBUG_RETURN(true); @@ -10180,7 +10180,14 @@ bool Item_type_holder::join_types(THD *thd, Item *item) }; maybe_null|= item->maybe_null; get_full_info(item); - set_handler(Item_type_holder::type_handler()->type_handler_for_union(this)); + /* + 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(); diff --git a/sql/item.h b/sql/item.h index 1123b4beb93..0e004571395 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2604,11 +2604,7 @@ public: const Type_handler *type_handler() const { const Type_handler *handler= field->type_handler(); - // This special code for ENUM and SET should eventually be removed - if (handler == &type_handler_enum || - handler == &type_handler_set) - return &type_handler_string; - return field->type_handler(); + return handler->type_handler_for_item_field(); } enum Item_result result_type () const { @@ -5792,7 +5788,10 @@ public: Item_type_holder(THD*, Item*); const Type_handler *type_handler() const - { return Type_handler_hybrid_field_type::type_handler(); } + { + const Type_handler *handler= Type_handler_hybrid_field_type::type_handler(); + return handler->type_handler_for_item_field(); + } enum_field_types field_type() const { return Type_handler_hybrid_field_type::field_type(); } enum Item_result result_type () const @@ -5813,7 +5812,7 @@ public: } const Type_handler *real_type_handler() const { - return Item_type_holder::type_handler(); + return Type_handler_hybrid_field_type::type_handler(); } enum Type type() const { return TYPE_HOLDER; } @@ -5825,7 +5824,7 @@ public: bool join_types(THD *thd, Item *); Field *create_tmp_field(bool group, TABLE *table) { - return Item_type_holder::type_handler()-> + return Item_type_holder::real_type_handler()-> make_and_init_table_field(&name, Record_addr(maybe_null), *this, table); } diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 88ad5bd576c..23cfe20e5e6 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -470,12 +470,24 @@ const Type_handler *Type_handler_row::type_handler_for_comparison() const /***************************************************************************/ +const Type_handler *Type_handler_enum::type_handler_for_item_field() const +{ + return &type_handler_string; +} + + const Type_handler *Type_handler_enum::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; diff --git a/sql/sql_type.h b/sql/sql_type.h index a1a2beeb1d1..c61142d0a7b 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -579,6 +579,10 @@ public: */ virtual bool is_param_long_data_type() const { return false; } virtual const Type_handler *type_handler_for_comparison() const= 0; + virtual const Type_handler *type_handler_for_item_field() const + { + return this; + } virtual const Type_handler *type_handler_for_tmp_table(const Item *) const { return this; @@ -2053,6 +2057,7 @@ public: 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; @@ -2071,6 +2076,7 @@ public: 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;