From b5fae7f743d2b2c71ee23b0daf51c2be294733eb Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 11 Oct 2019 14:50:11 +0400 Subject: [PATCH] MDEV-20795 CAST(inet6 AS BINARY) returns wrong result --- .../mysql-test/type_inet/type_inet6.result | 105 ++++++++++++++++++ .../mysql-test/type_inet/type_inet6.test | 65 +++++++++++ plugin/type_inet/sql_type_inet.cc | 48 ++++++++ plugin/type_inet/sql_type_inet.h | 6 +- sql/item_func.h | 50 +++++++++ sql/item_timefunc.cc | 86 +++++++++++++- sql/item_timefunc.h | 16 +-- 7 files changed, 362 insertions(+), 14 deletions(-) diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6.result b/plugin/type_inet/mysql-test/type_inet/type_inet6.result index 3e5905cecec86..14a43acb342ad 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6.result +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6.result @@ -720,6 +720,99 @@ t1 CREATE TABLE `t1` ( `a` varchar(39) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t1; +CREATE TABLE t1 (a INET6); +INSERT INTO t1 VALUES ('ffff::ffff'); +CREATE TABLE t2 AS SELECT +CAST(a AS CHAR), +CAST(a AS CHAR(39)), +CAST(a AS CHAR(530)), +CAST(a AS CHAR(65535)), +CAST(a AS CHAR(66000)), +CAST(a AS CHAR(16777215)), +CAST(a AS CHAR(16777216)) +FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `CAST(a AS CHAR)` varchar(39) DEFAULT NULL, + `CAST(a AS CHAR(39))` varchar(39) DEFAULT NULL, + `CAST(a AS CHAR(530))` text DEFAULT NULL, + `CAST(a AS CHAR(65535))` text DEFAULT NULL, + `CAST(a AS CHAR(66000))` mediumtext DEFAULT NULL, + `CAST(a AS CHAR(16777215))` mediumtext DEFAULT NULL, + `CAST(a AS CHAR(16777216))` longtext DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t2; +CAST(a AS CHAR) ffff::ffff +CAST(a AS CHAR(39)) ffff::ffff +CAST(a AS CHAR(530)) ffff::ffff +CAST(a AS CHAR(65535)) ffff::ffff +CAST(a AS CHAR(66000)) ffff::ffff +CAST(a AS CHAR(16777215)) ffff::ffff +CAST(a AS CHAR(16777216)) ffff::ffff +DROP TABLE t2; +DROP TABLE t1; +CREATE TABLE t1 (a INET6); +INSERT INTO t1 VALUES ('ffff::ffff'); +CREATE TABLE t2 AS SELECT +CAST(a AS BINARY(4)) AS cb4, +CAST(a AS BINARY) AS cb, +CAST(a AS BINARY(16)) AS cb16, +CAST(a AS BINARY(32)) AS cb32, +CAST(a AS BINARY(530)) AS cb530, +CAST(a AS BINARY(65535)) AS cb65535, +CAST(a AS BINARY(66000)) AS cb66000, +CAST(a AS BINARY(16777215)) AS cb16777215, +CAST(a AS BINARY(16777216)) AS cb16777216 +FROM t1 LIMIT 0; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `cb4` binary(4) DEFAULT NULL, + `cb` binary(16) DEFAULT NULL, + `cb16` binary(16) DEFAULT NULL, + `cb32` binary(32) DEFAULT NULL, + `cb530` varbinary(530) DEFAULT NULL, + `cb65535` blob DEFAULT NULL, + `cb66000` mediumblob DEFAULT NULL, + `cb16777215` mediumblob DEFAULT NULL, + `cb16777216` longblob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t2; +CREATE TABLE t2 AS SELECT +CAST(a AS BINARY(4)) AS cb4, +CAST(a AS BINARY) AS cb, +CAST(a AS BINARY(16)) AS cb16, +CAST(a AS BINARY(32)) AS cb32, +CAST(a AS BINARY(530)) AS cb530, +CAST(a AS BINARY(65535)) AS cb65535 +FROM t1; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `cb4` binary(4) DEFAULT NULL, + `cb` binary(16) DEFAULT NULL, + `cb16` binary(16) DEFAULT NULL, + `cb32` binary(32) DEFAULT NULL, + `cb530` varbinary(530) DEFAULT NULL, + `cb65535` blob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT +HEX(cb4), +HEX(cb), +HEX(cb16), +HEX(cb32), +LENGTH(cb530), +LENGTH(cb65535) +FROM t2; +HEX(cb4) FFFF0000 +HEX(cb) FFFF000000000000000000000000FFFF +HEX(cb16) FFFF000000000000000000000000FFFF +HEX(cb32) FFFF000000000000000000000000FFFF00000000000000000000000000000000 +LENGTH(cb530) 530 +LENGTH(cb65535) 65535 +DROP TABLE t2; +DROP TABLE t1; # # Implicit conversion to other types in INSERT # @@ -1516,3 +1609,15 @@ SELECT HEX(a) FROM t1; HEX(a) 20010DB8000000000000FF0000428329 DROP TABLE t1; +# +# MDEV-20795 CAST(inet6 AS BINARY) returns wrong result +# +CREATE OR REPLACE TABLE t1 (a INET6); +INSERT INTO t1 VALUES ('2001:db8::ff00:42:8329'); +SELECT HEX(CAST(a AS BINARY)) FROM t1; +HEX(CAST(a AS BINARY)) +20010DB8000000000000FF0000428329 +SELECT HEX(CAST(a AS BINARY(16))) FROM t1; +HEX(CAST(a AS BINARY(16))) +20010DB8000000000000FF0000428329 +DROP TABLE t1; diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6.test b/plugin/type_inet/mysql-test/type_inet/type_inet6.test index 03b83214edc4c..99d0921ebfa17 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6.test +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6.test @@ -383,6 +383,61 @@ CREATE TABLE t1 AS SELECT CAST(CAST('::' AS INET6) AS CHAR) AS a; SHOW CREATE TABLE t1; DROP TABLE t1; +CREATE TABLE t1 (a INET6); +INSERT INTO t1 VALUES ('ffff::ffff'); +CREATE TABLE t2 AS SELECT + CAST(a AS CHAR), + CAST(a AS CHAR(39)), + CAST(a AS CHAR(530)), + CAST(a AS CHAR(65535)), + CAST(a AS CHAR(66000)), + CAST(a AS CHAR(16777215)), + CAST(a AS CHAR(16777216)) +FROM t1; +SHOW CREATE TABLE t2; +--vertical_results +SELECT * FROM t2; +--horizontal_results +DROP TABLE t2; +DROP TABLE t1; + +CREATE TABLE t1 (a INET6); +INSERT INTO t1 VALUES ('ffff::ffff'); +CREATE TABLE t2 AS SELECT + CAST(a AS BINARY(4)) AS cb4, + CAST(a AS BINARY) AS cb, + CAST(a AS BINARY(16)) AS cb16, + CAST(a AS BINARY(32)) AS cb32, + CAST(a AS BINARY(530)) AS cb530, + CAST(a AS BINARY(65535)) AS cb65535, + CAST(a AS BINARY(66000)) AS cb66000, + CAST(a AS BINARY(16777215)) AS cb16777215, + CAST(a AS BINARY(16777216)) AS cb16777216 +FROM t1 LIMIT 0; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE TABLE t2 AS SELECT + CAST(a AS BINARY(4)) AS cb4, + CAST(a AS BINARY) AS cb, + CAST(a AS BINARY(16)) AS cb16, + CAST(a AS BINARY(32)) AS cb32, + CAST(a AS BINARY(530)) AS cb530, + CAST(a AS BINARY(65535)) AS cb65535 +FROM t1; +SHOW CREATE TABLE t2; +--vertical_results +SELECT + HEX(cb4), + HEX(cb), + HEX(cb16), + HEX(cb32), + LENGTH(cb530), + LENGTH(cb65535) +FROM t2; +--horizontal_results +DROP TABLE t2; +DROP TABLE t1; --echo # --echo # Implicit conversion to other types in INSERT @@ -1049,3 +1104,13 @@ INSERT INTO t1 VALUES ('2001:db8::ff00:42:8329'); ALTER TABLE t1 MODIFY a BINARY(16); SELECT HEX(a) FROM t1; DROP TABLE t1; + +--echo # +--echo # MDEV-20795 CAST(inet6 AS BINARY) returns wrong result +--echo # + +CREATE OR REPLACE TABLE t1 (a INET6); +INSERT INTO t1 VALUES ('2001:db8::ff00:42:8329'); +SELECT HEX(CAST(a AS BINARY)) FROM t1; +SELECT HEX(CAST(a AS BINARY(16))) FROM t1; +DROP TABLE t1; diff --git a/plugin/type_inet/sql_type_inet.cc b/plugin/type_inet/sql_type_inet.cc index 0c50cfe034c38..97e433cb053f4 100644 --- a/plugin/type_inet/sql_type_inet.cc +++ b/plugin/type_inet/sql_type_inet.cc @@ -1216,6 +1216,54 @@ class in_inet6 :public in_vector }; +class Item_char_typecast_func_handler_inet6_to_binary: + public Item_handled_func::Handler_str +{ +public: + const Type_handler *return_type_handler() const override + { + return &type_handler_string; + } + const Type_handler * + type_handler_for_create_select(const Item_handled_func *item) const + { + if (item->max_length > MAX_FIELD_VARCHARLENGTH) + return Type_handler::blob_type_handler(item->max_length); + if (item->max_length > 255) + return &type_handler_varchar; + return &type_handler_string; + } + bool fix_length_and_dec(Item_handled_func *xitem) const override + { + return false; + } + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(dynamic_cast(item)); + return static_cast(item)-> + val_str_binary_from_native(to); + } +}; + + +static const Item_char_typecast_func_handler_inet6_to_binary + item_char_typecast_func_handler_inet6_to_binary; + + +bool Type_handler_inet6:: + Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const +{ + if (item->cast_charset() == &my_charset_bin) + { + item->fix_length_and_dec_native_to_binary(Inet6::binary_length()); + item->set_func_handler(&item_char_typecast_func_handler_inet6_to_binary); + return false; + } + item->fix_length_and_dec_str(); + return false; +} + + bool Type_handler_inet6::character_or_binary_string_to_native(THD *thd, const String *str, diff --git a/plugin/type_inet/sql_type_inet.h b/plugin/type_inet/sql_type_inet.h index cfc44f66af645..57e34f3c2d5c2 100644 --- a/plugin/type_inet/sql_type_inet.h +++ b/plugin/type_inet/sql_type_inet.h @@ -930,11 +930,7 @@ class Type_handler_inet6: public Type_handler } bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) - const override - { - item->fix_length_and_dec_str(); - return false; - } + const override; bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const override { diff --git a/sql/item_func.h b/sql/item_func.h index 0f10e4b94e2cb..5b0093167d48b 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -467,9 +467,46 @@ class Item_handled_func: public Item_func virtual my_decimal *val_decimal(Item_handled_func *, my_decimal *) const= 0; virtual bool get_date(THD *thd, Item_handled_func *, MYSQL_TIME *, date_mode_t fuzzydate) const= 0; virtual const Type_handler *return_type_handler() const= 0; + virtual const Type_handler * + type_handler_for_create_select(const Item_handled_func *item) const + { + return return_type_handler(); + } virtual bool fix_length_and_dec(Item_handled_func *) const= 0; }; + class Handler_str: public Handler + { + public: + String *val_str_ascii(Item_handled_func *item, String *str) const + { + return item->Item::val_str_ascii(str); + } + double val_real(Item_handled_func *item) const + { + DBUG_ASSERT(item->is_fixed()); + StringBuffer<64> tmp; + String *res= item->val_str(&tmp); + return res ? item->double_from_string_with_check(res) : 0.0; + } + longlong val_int(Item_handled_func *item) const + { + DBUG_ASSERT(item->is_fixed()); + StringBuffer<22> tmp; + String *res= item->val_str(&tmp); + return res ? item->longlong_from_string_with_check(res) : 0; + } + my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const + { + return item->val_decimal_from_string(to); + } + bool get_date(THD *thd, Item_handled_func *item, MYSQL_TIME *to, + date_mode_t fuzzydate) const + { + return item->get_date_from_string(thd, to, fuzzydate); + } + }; + /** Abstract class for functions returning TIME, DATE, DATETIME or string values, whose data type depends on parameters and is set at fix_fields time. @@ -496,6 +533,11 @@ class Item_handled_func: public Item_func { return &type_handler_string; } + const Type_handler * + type_handler_for_create_select(const Item_handled_func *item) const + { + return return_type_handler()->type_handler_for_tmp_table(item); + } double val_real(Item_handled_func *item) const { return Temporal_hybrid(item).to_double(); @@ -613,6 +655,14 @@ class Item_handled_func: public Item_func { return m_func_handler->return_type_handler(); } + Field *create_field_for_create_select(MEM_ROOT *root, TABLE *table) + { + DBUG_ASSERT(fixed); + const Type_handler *h= m_func_handler->type_handler_for_create_select(this); + return h->make_and_init_table_field(root, &name, + Record_addr(maybe_null), + *this, table); + } String *val_str(String *to) { return m_func_handler->val_str(this, to); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 86e81fa5bf751..da67e1ca3710b 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2326,7 +2326,7 @@ uint Item_char_typecast::adjusted_length_with_warn(uint length) } -String *Item_char_typecast::val_str(String *str) +String *Item_char_typecast::val_str_generic(String *str) { DBUG_ASSERT(fixed == 1); String *res; @@ -2382,11 +2382,75 @@ String *Item_char_typecast::val_str(String *str) } +String *Item_char_typecast::val_str_binary_from_native(String *str) +{ + DBUG_ASSERT(fixed == 1); + DBUG_ASSERT(cast_cs == &my_charset_bin); + NativeBuffer native; + + if (args[0]->val_native(current_thd, &native)) + { + null_value= 1; + return 0; + } + + if (has_explicit_length()) + { + cast_length= adjusted_length_with_warn(cast_length); + if (cast_length > native.length()) + { + // add trailing 0x00s + DBUG_ASSERT(cast_length <= current_thd->variables.max_allowed_packet); + str->alloc(cast_length); + str->copy(native.ptr(), native.length(), &my_charset_bin); + bzero((char*) str->end(), cast_length - str->length()); + str->length(cast_length); + } + else + str->copy(native.ptr(), cast_length, &my_charset_bin); + } + else + str->copy(native.ptr(), native.length(), &my_charset_bin); + + return ((null_value= (str->length() > + adjusted_length_with_warn(str->length())))) ? 0 : str; +} + + +class Item_char_typecast_func_handler: public Item_handled_func::Handler_str +{ +public: + const Type_handler *return_type_handler() const + { + return &type_handler_varchar; + } + const Type_handler * + type_handler_for_create_select(const Item_handled_func *item) const + { + return return_type_handler()->type_handler_for_tmp_table(item); + } + + bool fix_length_and_dec(Item_handled_func *item) const + { + return false; + } + String *val_str(Item_handled_func *item, String *to) const + { + DBUG_ASSERT(dynamic_cast(item)); + return static_cast(item)->val_str_generic(to); + } +}; + + +static const Item_char_typecast_func_handler item_char_typecast_func_handler; + + void Item_char_typecast::fix_length_and_dec_numeric() { fix_length_and_dec_internal(from_cs= cast_cs->mbminlen == 1 ? cast_cs : &my_charset_latin1); + set_func_handler(&item_char_typecast_func_handler); } @@ -2395,6 +2459,24 @@ void Item_char_typecast::fix_length_and_dec_generic() fix_length_and_dec_internal(from_cs= args[0]->dynamic_result() ? 0 : args[0]->collation.collation); + set_func_handler(&item_char_typecast_func_handler); +} + + +void Item_char_typecast::fix_length_and_dec_str() +{ + fix_length_and_dec_generic(); + m_suppress_warning_to_error_escalation= true; + set_func_handler(&item_char_typecast_func_handler); +} + + +void +Item_char_typecast::fix_length_and_dec_native_to_binary(uint32 octet_length) +{ + collation.set(&my_charset_bin, DERIVATION_IMPLICIT); + max_length= has_explicit_length() ? (uint32) cast_length : octet_length; + maybe_null|= current_thd->is_strict_mode(); } @@ -2438,6 +2520,8 @@ void Item_char_typecast::fix_length_and_dec_internal(CHARSET_INFO *from_cs) (cast_cs == &my_charset_bin ? 1 : args[0]->collation.collation->mbmaxlen)); max_length= char_length * cast_cs->mbmaxlen; + // Add NULL-ability in strict mode. See Item_str_func::fix_fields() + maybe_null= maybe_null || current_thd->is_strict_mode(); } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index f994d463158f4..97e9c2ac59bb2 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1074,14 +1074,16 @@ class Item_extract :public Item_int_func, }; -class Item_char_typecast :public Item_str_func +class Item_char_typecast :public Item_handled_func { uint cast_length; CHARSET_INFO *cast_cs, *from_cs; bool charset_conversion; String tmp_value; bool m_suppress_warning_to_error_escalation; +public: bool has_explicit_length() const { return cast_length != ~0U; } +private: String *reuse(String *src, size_t length); String *copy(String *src, CHARSET_INFO *cs); uint adjusted_length_with_warn(uint length); @@ -1092,20 +1094,18 @@ class Item_char_typecast :public Item_str_func uint get_cast_length() const { return cast_length; } public: Item_char_typecast(THD *thd, Item *a, uint length_arg, CHARSET_INFO *cs_arg): - Item_str_func(thd, a), cast_length(length_arg), cast_cs(cs_arg), + Item_handled_func(thd, a), cast_length(length_arg), cast_cs(cs_arg), m_suppress_warning_to_error_escalation(false) {} enum Functype functype() const { return CHAR_TYPECAST_FUNC; } bool eq(const Item *item, bool binary_cmp) const; const char *func_name() const { return "cast_as_char"; } CHARSET_INFO *cast_charset() const { return cast_cs; } - String *val_str(String *a); + String *val_str_generic(String *a); + String *val_str_binary_from_native(String *a); void fix_length_and_dec_generic(); void fix_length_and_dec_numeric(); - void fix_length_and_dec_str() - { - fix_length_and_dec_generic(); - m_suppress_warning_to_error_escalation= true; - } + void fix_length_and_dec_str(); + void fix_length_and_dec_native_to_binary(uint32 octet_length); bool fix_length_and_dec() { return args[0]->type_handler()->Item_char_typecast_fix_length_and_dec(this);