summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2019-10-11 14:50:11 +0400
committerAlexander Barkov <bar@mariadb.com>2019-10-11 14:50:11 +0400
commitb5fae7f743d2b2c71ee23b0daf51c2be294733eb (patch)
treea2609d62812fbd397f7c6ab165f33032f0f45dd1
parentd04f2de80a96b3ff9290ee27507195bfb01fc77f (diff)
downloadmariadb-git-b5fae7f743d2b2c71ee23b0daf51c2be294733eb.tar.gz
MDEV-20795 CAST(inet6 AS BINARY) returns wrong result
-rw-r--r--plugin/type_inet/mysql-test/type_inet/type_inet6.result105
-rw-r--r--plugin/type_inet/mysql-test/type_inet/type_inet6.test65
-rw-r--r--plugin/type_inet/sql_type_inet.cc48
-rw-r--r--plugin/type_inet/sql_type_inet.h6
-rw-r--r--sql/item_func.h50
-rw-r--r--sql/item_timefunc.cc86
-rw-r--r--sql/item_timefunc.h16
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 3e5905cecec..14a43acb342 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 03b83214edc..99d0921ebfa 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 0c50cfe034c..97e433cb053 100644
--- a/plugin/type_inet/sql_type_inet.cc
+++ b/plugin/type_inet/sql_type_inet.cc
@@ -1216,6 +1216,54 @@ public:
};
+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<const Item_char_typecast*>(item));
+ return static_cast<Item_char_typecast*>(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 cfc44f66af6..57e34f3c2d5 100644
--- a/plugin/type_inet/sql_type_inet.h
+++ b/plugin/type_inet/sql_type_inet.h
@@ -930,11 +930,7 @@ public:
}
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 0f10e4b94e2..5b0093167d4 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -467,9 +467,46 @@ public:
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 @@ public:
{
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 @@ public:
{
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 86e81fa5bf7..da67e1ca371 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 @@ end:
}
+String *Item_char_typecast::val_str_binary_from_native(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ DBUG_ASSERT(cast_cs == &my_charset_bin);
+ NativeBuffer<STRING_BUFFER_USUAL_SIZE> 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<const Item_char_typecast*>(item));
+ return static_cast<Item_char_typecast*>(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 f994d463158..97e9c2ac59b 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 @@ public:
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);