Commit 839ad5e1 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-22758 Assertion `!item->null_value' failed in Type_handler_inet6::make_sort_key_part

When some expression of an INET6 data type involves conversion to INET6 from
other data types, e.g. in:

- CAST:

    SELECT CAST(non_inet6_expr AS INET6)

- CASE and hybrid functions:

    SELECT CASE WHEN expr THEN inet6_expr ELSE non_inet6_expr END

- UNION:

    SELECT inet6_expr UNION SELECT non_inet6_expr

the result column must be fixed as NULL-able even if the non-inet6 expression itself
is not NULL-able, because at the execution time the conversion can fail.

Details:
- Forcing NULL-ability if conversion from some data type to INET6 is involved
  (for non-constant or for expensive expressions).
- Non-expensive constant expressions are tested for NULL-ability at fix_fields() time,
  so things like `CAST('::' AS INET6)` are still detected as NOT NULL.
- Adding "bool warn" parameter into a few methods, to avoid redundant warnings
  at fix_fields() time when calculating NULL-ability of constant values.
parent 5b18ade0
...@@ -2058,3 +2058,54 @@ Lady of the Flies ::5 ...@@ -2058,3 +2058,54 @@ Lady of the Flies ::5
Lord of the Ladybirds ::5 Lord of the Ladybirds ::5
Lord of the Ladybirds ::5 Lord of the Ladybirds ::5
DROP TABLE t1; DROP TABLE t1;
#
# MDEV-22758 Assertion `!item->null_value' failed in Type_handler_inet6::make_sort_key_part
#
CREATE TABLE t1 (a VARCHAR(8) NOT NULL, b INET6 NOT NULL);
INSERT INTO t1 VALUES ('foo','::'),('bar','1::1');
SELECT * FROM t1 ORDER BY CASE WHEN a THEN b ELSE a END;
a b
foo ::
bar 1::1
Warnings:
Warning 1292 Truncated incorrect DOUBLE value: 'foo'
Warning 1292 Incorrect inet6 value: 'foo'
Warning 1292 Truncated incorrect DOUBLE value: 'bar'
Warning 1292 Incorrect inet6 value: 'bar'
DROP TABLE t1;
CREATE OR REPLACE TABLE t1 (a VARCHAR(8) NOT NULL);
INSERT INTO t1 VALUES ('foo'),('bar');
SELECT * FROM t1 ORDER BY CAST(a AS INET6);
a
foo
bar
Warnings:
Warning 1292 Incorrect inet6 value: 'foo'
Warning 1292 Incorrect inet6 value: 'bar'
DROP TABLE t1;
CREATE TABLE t1 (a INET6 NOT NULL, b VARCHAR(32) NOT NULL);
CREATE TABLE t2 AS SELECT CAST(a AS INET6) AS ca, CAST(b AS INET6) AS cb FROM t1;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`ca` inet6 NOT NULL,
`cb` inet6 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t2;
CREATE TABLE t2 AS SELECT COALESCE(a,a), COALESCE(a,b) FROM t1;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`COALESCE(a,a)` inet6 NOT NULL,
`COALESCE(a,b)` inet6 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t2;
CREATE TABLE t2 AS SELECT a AS ca,a AS cb FROM t1 UNION SELECT a,b FROM t1;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`ca` inet6 NOT NULL DEFAULT '::',
`cb` inet6 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t2;
DROP TABLE t1;
...@@ -1516,3 +1516,33 @@ SELECT name, PERCENTILE_DISC(1) ...@@ -1516,3 +1516,33 @@ SELECT name, PERCENTILE_DISC(1)
WITHIN GROUP (ORDER BY star_rating) WITHIN GROUP (ORDER BY star_rating)
OVER (PARTITION BY name) AS pc FROM t1; OVER (PARTITION BY name) AS pc FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # MDEV-22758 Assertion `!item->null_value' failed in Type_handler_inet6::make_sort_key_part
--echo #
CREATE TABLE t1 (a VARCHAR(8) NOT NULL, b INET6 NOT NULL);
INSERT INTO t1 VALUES ('foo','::'),('bar','1::1');
SELECT * FROM t1 ORDER BY CASE WHEN a THEN b ELSE a END;
DROP TABLE t1;
CREATE OR REPLACE TABLE t1 (a VARCHAR(8) NOT NULL);
INSERT INTO t1 VALUES ('foo'),('bar');
SELECT * FROM t1 ORDER BY CAST(a AS INET6);
DROP TABLE t1;
CREATE TABLE t1 (a INET6 NOT NULL, b VARCHAR(32) NOT NULL);
CREATE TABLE t2 AS SELECT CAST(a AS INET6) AS ca, CAST(b AS INET6) AS cb FROM t1;
SHOW CREATE TABLE t2;
DROP TABLE t2;
CREATE TABLE t2 AS SELECT COALESCE(a,a), COALESCE(a,b) FROM t1;
SHOW CREATE TABLE t2;
DROP TABLE t2;
CREATE TABLE t2 AS SELECT a AS ca,a AS cb FROM t1 UNION SELECT a,b FROM t1;
SHOW CREATE TABLE t2;
DROP TABLE t2;
DROP TABLE t1;
...@@ -508,7 +508,19 @@ size_t Inet6::to_string(char *dst, size_t dstsize) const ...@@ -508,7 +508,19 @@ size_t Inet6::to_string(char *dst, size_t dstsize) const
} }
bool Inet6::make_from_item(Item *item) bool Inet6::fix_fields_maybe_null_on_conversion_to_inet6(Item *item)
{
if (item->maybe_null)
return true;
if (item->type_handler() == &type_handler_inet6)
return false;
if (!item->const_item() || item->is_expensive())
return true;
return Inet6_null(item, false).is_null();
}
bool Inet6::make_from_item(Item *item, bool warn)
{ {
if (item->type_handler() == &type_handler_inet6) if (item->type_handler() == &type_handler_inet6)
{ {
...@@ -523,18 +535,18 @@ bool Inet6::make_from_item(Item *item) ...@@ -523,18 +535,18 @@ bool Inet6::make_from_item(Item *item)
} }
StringBufferInet6 tmp; StringBufferInet6 tmp;
String *str= item->val_str(&tmp); String *str= item->val_str(&tmp);
return str ? make_from_character_or_binary_string(str) : true; return str ? make_from_character_or_binary_string(str, warn) : true;
} }
bool Inet6::make_from_character_or_binary_string(const String *str) bool Inet6::make_from_character_or_binary_string(const String *str, bool warn)
{ {
static Name name= type_handler_inet6.name(); static Name name= type_handler_inet6.name();
if (str->charset() != &my_charset_bin) if (str->charset() != &my_charset_bin)
{ {
bool rc= character_string_to_ipv6(str->ptr(), str->length(), bool rc= character_string_to_ipv6(str->ptr(), str->length(),
str->charset()); str->charset());
if (rc) if (rc && warn)
current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name.ptr(), name.ptr(),
ErrConvString(str).ptr()); ErrConvString(str).ptr());
...@@ -542,9 +554,10 @@ bool Inet6::make_from_character_or_binary_string(const String *str) ...@@ -542,9 +554,10 @@ bool Inet6::make_from_character_or_binary_string(const String *str)
} }
if (str->length() != sizeof(m_buffer)) if (str->length() != sizeof(m_buffer))
{ {
current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, if (warn)
name.ptr(), current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
ErrConvString(str).ptr()); name.ptr(),
ErrConvString(str).ptr());
return true; return true;
} }
DBUG_ASSERT(str->ptr() != m_buffer); DBUG_ASSERT(str->ptr() != m_buffer);
...@@ -1036,6 +1049,8 @@ class Item_typecast_inet6: public Item_func ...@@ -1036,6 +1049,8 @@ class Item_typecast_inet6: public Item_func
bool fix_length_and_dec() override bool fix_length_and_dec() override
{ {
Type_std_attributes::operator=(Type_std_attributes_inet6()); Type_std_attributes::operator=(Type_std_attributes_inet6());
if (Inet6::fix_fields_maybe_null_on_conversion_to_inet6(args[0]))
maybe_null= true;
return false; return false;
} }
String *val_str(String *to) override String *val_str(String *to) override
......
...@@ -137,7 +137,7 @@ class Inet6 ...@@ -137,7 +137,7 @@ class Inet6
{ {
protected: protected:
char m_buffer[IN6_ADDR_SIZE]; char m_buffer[IN6_ADDR_SIZE];
bool make_from_item(Item *item); bool make_from_item(Item *item, bool warn);
bool ascii_to_ipv6(const char *str, size_t str_length); bool ascii_to_ipv6(const char *str, size_t str_length);
bool character_string_to_ipv6(const char *str, size_t str_length, bool character_string_to_ipv6(const char *str, size_t str_length,
CHARSET_INFO *cs) CHARSET_INFO *cs)
...@@ -152,7 +152,7 @@ class Inet6 ...@@ -152,7 +152,7 @@ class Inet6
} }
return ascii_to_ipv6(str, str_length); return ascii_to_ipv6(str, str_length);
} }
bool make_from_character_or_binary_string(const String *str); bool make_from_character_or_binary_string(const String *str, bool warn);
bool binary_to_ipv6(const char *str, size_t length) bool binary_to_ipv6(const char *str, size_t length)
{ {
if (length != sizeof(m_buffer)) if (length != sizeof(m_buffer))
...@@ -182,11 +182,17 @@ class Inet6 ...@@ -182,11 +182,17 @@ class Inet6
return true; return true;
} }
/*
Check at Item's fix_fields() time if "item" can return a nullable value
on conversion to INET6, or conversion produces a NOT NULL INET6 value.
*/
static bool fix_fields_maybe_null_on_conversion_to_inet6(Item *item);
public: public:
Inet6(Item *item, bool *error) Inet6(Item *item, bool *error, bool warn= true)
{ {
*error= make_from_item(item); *error= make_from_item(item, warn);
} }
void to_binary(char *str, size_t str_size) const void to_binary(char *str, size_t str_size) const
{ {
...@@ -264,8 +270,8 @@ class Inet6_null: public Inet6, public Null_flag ...@@ -264,8 +270,8 @@ class Inet6_null: public Inet6, public Null_flag
:Inet6_null(str.ptr(), str.length()) :Inet6_null(str.ptr(), str.length())
{ } { }
// Initialize from an Item // Initialize from an Item
Inet6_null(Item *item) Inet6_null(Item *item, bool warn= true)
:Null_flag(make_from_item(item)) :Null_flag(make_from_item(item, warn))
{ } { }
public: public:
const Inet6& to_inet6() const const Inet6& to_inet6() const
...@@ -714,6 +720,14 @@ class Type_handler_inet6: public Type_handler ...@@ -714,6 +720,14 @@ class Type_handler_inet6: public Type_handler
{ {
attr->Type_std_attributes::operator=(Type_std_attributes_inet6()); attr->Type_std_attributes::operator=(Type_std_attributes_inet6());
h->set_handler(this); h->set_handler(this);
for (uint i= 0; i < nitems; i++)
{
if (Inet6::fix_fields_maybe_null_on_conversion_to_inet6(items[i]))
{
attr->set_maybe_null(true);
break;
}
}
return false; return false;
} }
bool Item_func_min_max_fix_attributes(THD *thd, bool Item_func_min_max_fix_attributes(THD *thd,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment