Commit 390de205 authored by Varun Gupta's avatar Varun Gupta

MDEV-24519: Server crashes in Charset::set_charset upon SELECT

The query causing the issue here has implicit grouping for we
have to produce one row with special values for the aggregates
(depending on each aggregate function), and NULL values for all
non-aggregate fields.

The subselect item where implicit grouping was being done,
null_value for the subselect item was not being set for
the case when the implicit grouping produces NULL values
for the items in the select list of the subquery.
This which was leading to the crash.

The fix would be to set the null_value when all the values
for the row column have NULL values.

Further changes are

1) etting null_value for Item_singlerow_subselect only
   after val_* functions have been called.
2) Introduced a parameter null_value_inside to Item_cache that
   would store be set to TRUE if any of the arguments of the
   Item_cache are null.

Reviewed And co-authored by Monty
parent 1f3f9031
...@@ -2694,4 +2694,31 @@ INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4), (5,5); ...@@ -2694,4 +2694,31 @@ INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4), (5,5);
SELECT a, b FROM t1 WHERE a IN (SELECT A.a FROM t1 A GROUP BY s.id); SELECT a, b FROM t1 WHERE a IN (SELECT A.a FROM t1 A GROUP BY s.id);
ERROR 42S22: Unknown column 's.id' in 'group statement' ERROR 42S22: Unknown column 's.id' in 'group statement'
DROP TABLE t1; DROP TABLE t1;
#
# MDEV-24519: Server crashes in Charset::set_charset upon SELECT
#
CREATE TABLE t1 (a VARBINARY(8));
INSERT INTO t1 VALUES ('foo'),('bar');
CREATE TABLE t2 (b VARBINARY(8));
EXPLAIN
SELECT a FROM t1 WHERE (a, a) IN (SELECT 'qux', 'qux') AND a = (SELECT MIN(b) FROM t2);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used
SELECT a FROM t1 WHERE (a, a) IN (SELECT 'qux', 'qux') AND a = (SELECT MIN(b) FROM t2);
a
DROP TABLE t1,t2;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (b VARBINARY(8));
EXPLAIN
SELECT a FROM t1 WHERE (a, a) IN (SELECT 1, 2) AND a = (SELECT MIN(b) FROM t2);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using where
3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used
SELECT a FROM t1 WHERE (a, a) IN (SELECT 1, 2) AND a = (SELECT MIN(b) FROM t2);
a
DROP TABLE t1,t2;
# End of 10.2 tests # End of 10.2 tests
...@@ -2212,4 +2212,28 @@ INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4), (5,5); ...@@ -2212,4 +2212,28 @@ INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4), (5,5);
SELECT a, b FROM t1 WHERE a IN (SELECT A.a FROM t1 A GROUP BY s.id); SELECT a, b FROM t1 WHERE a IN (SELECT A.a FROM t1 A GROUP BY s.id);
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # MDEV-24519: Server crashes in Charset::set_charset upon SELECT
--echo #
CREATE TABLE t1 (a VARBINARY(8));
INSERT INTO t1 VALUES ('foo'),('bar');
CREATE TABLE t2 (b VARBINARY(8));
EXPLAIN
SELECT a FROM t1 WHERE (a, a) IN (SELECT 'qux', 'qux') AND a = (SELECT MIN(b) FROM t2);
SELECT a FROM t1 WHERE (a, a) IN (SELECT 'qux', 'qux') AND a = (SELECT MIN(b) FROM t2);
DROP TABLE t1,t2;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (b VARBINARY(8));
EXPLAIN
SELECT a FROM t1 WHERE (a, a) IN (SELECT 1, 2) AND a = (SELECT MIN(b) FROM t2);
SELECT a FROM t1 WHERE (a, a) IN (SELECT 1, 2) AND a = (SELECT MIN(b) FROM t2);
DROP TABLE t1,t2;
--echo # End of 10.2 tests --echo # End of 10.2 tests
...@@ -9690,7 +9690,7 @@ bool Item_cache_int::cache_value() ...@@ -9690,7 +9690,7 @@ bool Item_cache_int::cache_value()
return FALSE; return FALSE;
value_cached= TRUE; value_cached= TRUE;
value= example->val_int_result(); value= example->val_int_result();
null_value= example->null_value; null_value_inside= null_value= example->null_value;
unsigned_flag= example->unsigned_flag; unsigned_flag= example->unsigned_flag;
return TRUE; return TRUE;
} }
...@@ -9860,7 +9860,7 @@ bool Item_cache_temporal::cache_value() ...@@ -9860,7 +9860,7 @@ bool Item_cache_temporal::cache_value()
return true; return true;
value= pack_time(&ltime); value= pack_time(&ltime);
} }
null_value= example->null_value; null_value_inside= null_value= example->null_value;
return true; return true;
} }
...@@ -9955,7 +9955,7 @@ bool Item_cache_real::cache_value() ...@@ -9955,7 +9955,7 @@ bool Item_cache_real::cache_value()
return FALSE; return FALSE;
value_cached= TRUE; value_cached= TRUE;
value= example->val_result(); value= example->val_result();
null_value= example->null_value; null_value_inside= null_value= example->null_value;
return TRUE; return TRUE;
} }
...@@ -10017,7 +10017,8 @@ bool Item_cache_decimal::cache_value() ...@@ -10017,7 +10017,8 @@ bool Item_cache_decimal::cache_value()
return FALSE; return FALSE;
value_cached= TRUE; value_cached= TRUE;
my_decimal *val= example->val_decimal_result(&decimal_value); my_decimal *val= example->val_decimal_result(&decimal_value);
if (!(null_value= example->null_value) && val != &decimal_value) if (!(null_value_inside= null_value= example->null_value) &&
val != &decimal_value)
my_decimal2decimal(val, &decimal_value); my_decimal2decimal(val, &decimal_value);
return TRUE; return TRUE;
} }
...@@ -10083,11 +10084,14 @@ Item *Item_cache_decimal::convert_to_basic_const_item(THD *thd) ...@@ -10083,11 +10084,14 @@ Item *Item_cache_decimal::convert_to_basic_const_item(THD *thd)
bool Item_cache_str::cache_value() bool Item_cache_str::cache_value()
{ {
if (!example) if (!example)
{
DBUG_ASSERT(value_cached == FALSE);
return FALSE; return FALSE;
}
value_cached= TRUE; value_cached= TRUE;
value_buff.set(buffer, sizeof(buffer), example->collation.collation); value_buff.set(buffer, sizeof(buffer), example->collation.collation);
value= example->str_result(&value_buff); value= example->str_result(&value_buff);
if ((null_value= example->null_value)) if ((null_value= null_value_inside= example->null_value))
value= 0; value= 0;
else if (value != &value_buff) else if (value != &value_buff)
{ {
...@@ -10186,6 +10190,8 @@ Item *Item_cache_str::convert_to_basic_const_item(THD *thd) ...@@ -10186,6 +10190,8 @@ Item *Item_cache_str::convert_to_basic_const_item(THD *thd)
bool Item_cache_row::setup(THD *thd, Item *item) bool Item_cache_row::setup(THD *thd, Item *item)
{ {
example= item; example= item;
null_value= true;
if (!values && allocate(thd, item->cols())) if (!values && allocate(thd, item->cols()))
return 1; return 1;
for (uint i= 0; i < item_count; i++) for (uint i= 0; i < item_count; i++)
...@@ -10218,12 +10224,19 @@ bool Item_cache_row::cache_value() ...@@ -10218,12 +10224,19 @@ bool Item_cache_row::cache_value()
if (!example) if (!example)
return FALSE; return FALSE;
value_cached= TRUE; value_cached= TRUE;
null_value= 0; null_value= TRUE;
null_value_inside= false;
example->bring_value(); example->bring_value();
/*
For Item_cache_row null_value is set to TRUE only when ALL the values
inside the cache are NULL
*/
for (uint i= 0; i < item_count; i++) for (uint i= 0; i < item_count; i++)
{ {
values[i]->cache_value(); values[i]->cache_value();
null_value|= values[i]->null_value; null_value&= values[i]->null_value;
null_value_inside|= values[i]->null_value;
} }
return TRUE; return TRUE;
} }
......
...@@ -5658,6 +5658,14 @@ class Item_cache: public Item_basic_constant, ...@@ -5658,6 +5658,14 @@ class Item_cache: public Item_basic_constant,
*/ */
bool value_cached; bool value_cached;
public: public:
/*
This is set if at least one of the values of a sub query is NULL
Item_cache_row returns this with null_inside().
For not row items, it's set to the value of null_value
It is set after cache_value() is called.
*/
bool null_value_inside;
Item_cache(THD *thd): Item_cache(THD *thd):
Item_basic_constant(thd), Item_basic_constant(thd),
Type_handler_hybrid_field_type(MYSQL_TYPE_STRING), Type_handler_hybrid_field_type(MYSQL_TYPE_STRING),
...@@ -5667,6 +5675,7 @@ class Item_cache: public Item_basic_constant, ...@@ -5667,6 +5675,7 @@ class Item_cache: public Item_basic_constant,
fixed= 1; fixed= 1;
maybe_null= 1; maybe_null= 1;
null_value= 1; null_value= 1;
null_value_inside= true;
} }
protected: protected:
Item_cache(THD *thd, enum_field_types field_type_arg): Item_cache(THD *thd, enum_field_types field_type_arg):
...@@ -5678,6 +5687,7 @@ class Item_cache: public Item_basic_constant, ...@@ -5678,6 +5687,7 @@ class Item_cache: public Item_basic_constant,
fixed= 1; fixed= 1;
maybe_null= 1; maybe_null= 1;
null_value= 1; null_value= 1;
null_value_inside= true;
} }
public: public:
......
...@@ -1598,7 +1598,7 @@ longlong Item_in_optimizer::val_int() ...@@ -1598,7 +1598,7 @@ longlong Item_in_optimizer::val_int()
DBUG_RETURN(res); DBUG_RETURN(res);
} }
if (cache->null_value) if (cache->null_value_inside)
{ {
DBUG_PRINT("info", ("Left NULL...")); DBUG_PRINT("info", ("Left NULL..."));
/* /*
......
...@@ -851,7 +851,7 @@ bool Item_subselect::expr_cache_is_needed(THD *thd) ...@@ -851,7 +851,7 @@ bool Item_subselect::expr_cache_is_needed(THD *thd)
inline bool Item_in_subselect::left_expr_has_null() inline bool Item_in_subselect::left_expr_has_null()
{ {
return (*(optimizer->get_cache()))->null_value; return (*(optimizer->get_cache()))->null_value_inside;
} }
...@@ -1319,7 +1319,17 @@ bool Item_singlerow_subselect::null_inside() ...@@ -1319,7 +1319,17 @@ bool Item_singlerow_subselect::null_inside()
void Item_singlerow_subselect::bring_value() void Item_singlerow_subselect::bring_value()
{ {
if (!exec() && assigned()) if (!exec() && assigned())
null_value= 0; {
null_value= true;
for (uint i= 0; i < max_columns ; i++)
{
if (!row[i]->null_value)
{
null_value= false;
return;
}
}
}
else else
reset(); reset();
} }
...@@ -1345,7 +1355,11 @@ longlong Item_singlerow_subselect::val_int() ...@@ -1345,7 +1355,11 @@ longlong Item_singlerow_subselect::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (forced_const) if (forced_const)
return value->val_int(); {
longlong val= value->val_int();
null_value= value->null_value;
return val;
}
if (!exec() && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= FALSE; null_value= FALSE;
...@@ -1354,6 +1368,7 @@ longlong Item_singlerow_subselect::val_int() ...@@ -1354,6 +1368,7 @@ longlong Item_singlerow_subselect::val_int()
else else
{ {
reset(); reset();
DBUG_ASSERT(null_value);
return 0; return 0;
} }
} }
...@@ -1362,7 +1377,11 @@ String *Item_singlerow_subselect::val_str(String *str) ...@@ -1362,7 +1377,11 @@ String *Item_singlerow_subselect::val_str(String *str)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (forced_const) if (forced_const)
return value->val_str(str); {
String *res= value->val_str(str);
null_value= value->null_value;
return res;
}
if (!exec() && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= FALSE; null_value= FALSE;
...@@ -1371,6 +1390,7 @@ String *Item_singlerow_subselect::val_str(String *str) ...@@ -1371,6 +1390,7 @@ String *Item_singlerow_subselect::val_str(String *str)
else else
{ {
reset(); reset();
DBUG_ASSERT(null_value);
return 0; return 0;
} }
} }
...@@ -1380,7 +1400,11 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) ...@@ -1380,7 +1400,11 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (forced_const) if (forced_const)
return value->val_decimal(decimal_value); {
my_decimal *val= value->val_decimal(decimal_value);
null_value= value->null_value;
return val;
}
if (!exec() && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= FALSE; null_value= FALSE;
...@@ -1389,6 +1413,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) ...@@ -1389,6 +1413,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
else else
{ {
reset(); reset();
DBUG_ASSERT(null_value);
return 0; return 0;
} }
} }
...@@ -1398,7 +1423,11 @@ bool Item_singlerow_subselect::val_bool() ...@@ -1398,7 +1423,11 @@ bool Item_singlerow_subselect::val_bool()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (forced_const) if (forced_const)
return value->val_bool(); {
bool val= value->val_bool();
null_value= value->null_value;
return val;
}
if (!exec() && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= FALSE; null_value= FALSE;
...@@ -1407,6 +1436,7 @@ bool Item_singlerow_subselect::val_bool() ...@@ -1407,6 +1436,7 @@ bool Item_singlerow_subselect::val_bool()
else else
{ {
reset(); reset();
DBUG_ASSERT(null_value);
return 0; return 0;
} }
} }
...@@ -1416,7 +1446,11 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) ...@@ -1416,7 +1446,11 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (forced_const) if (forced_const)
return value->get_date(ltime, fuzzydate); {
bool val= value->get_date(ltime, fuzzydate);
null_value= value->null_value;
return val;
}
if (!exec() && !value->null_value) if (!exec() && !value->null_value)
{ {
null_value= FALSE; null_value= FALSE;
...@@ -1425,6 +1459,7 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) ...@@ -1425,6 +1459,7 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
else else
{ {
reset(); reset();
DBUG_ASSERT(null_value);
return 1; return 1;
} }
} }
......
...@@ -48,7 +48,11 @@ class Cached_item; ...@@ -48,7 +48,11 @@ class Cached_item;
class Item_subselect :public Item_result_field, class Item_subselect :public Item_result_field,
protected Used_tables_and_const_cache protected Used_tables_and_const_cache
{ {
bool value_assigned; /* value already assigned to subselect */ /*
Set to TRUE if the value is assigned for the subselect
FALSE: subquery not executed or the subquery returns an empty result
*/
bool value_assigned;
bool own_engine; /* the engine was not taken from other Item_subselect */ bool own_engine; /* the engine was not taken from other Item_subselect */
protected: protected:
/* thread handler, will be assigned in fix_fields only */ /* thread handler, will be assigned in fix_fields only */
......
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