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
Lord of the Ladybirds ::5
Lord of the Ladybirds ::5
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)
WITHIN GROUP (ORDER BY star_rating)
OVER (PARTITION BY name) AS pc FROM 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
}
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)
{
......@@ -523,18 +535,18 @@ bool Inet6::make_from_item(Item *item)
}
StringBufferInet6 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();
if (str->charset() != &my_charset_bin)
{
bool rc= character_string_to_ipv6(str->ptr(), str->length(),
str->charset());
if (rc)
if (rc && warn)
current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name.ptr(),
ErrConvString(str).ptr());
......@@ -542,9 +554,10 @@ bool Inet6::make_from_character_or_binary_string(const String *str)
}
if (str->length() != sizeof(m_buffer))
{
current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name.ptr(),
ErrConvString(str).ptr());
if (warn)
current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name.ptr(),
ErrConvString(str).ptr());
return true;
}
DBUG_ASSERT(str->ptr() != m_buffer);
......@@ -1036,6 +1049,8 @@ class Item_typecast_inet6: public Item_func
bool fix_length_and_dec() override
{
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;
}
String *val_str(String *to) override
......
......@@ -137,7 +137,7 @@ class Inet6
{
protected:
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 character_string_to_ipv6(const char *str, size_t str_length,
CHARSET_INFO *cs)
......@@ -152,7 +152,7 @@ class Inet6
}
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)
{
if (length != sizeof(m_buffer))
......@@ -182,11 +182,17 @@ class Inet6
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:
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
{
......@@ -264,8 +270,8 @@ class Inet6_null: public Inet6, public Null_flag
:Inet6_null(str.ptr(), str.length())
{ }
// Initialize from an Item
Inet6_null(Item *item)
:Null_flag(make_from_item(item))
Inet6_null(Item *item, bool warn= true)
:Null_flag(make_from_item(item, warn))
{ }
public:
const Inet6& to_inet6() const
......@@ -714,6 +720,14 @@ class Type_handler_inet6: public Type_handler
{
attr->Type_std_attributes::operator=(Type_std_attributes_inet6());
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;
}
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