diff options
-rw-r--r-- | mysql-test/r/subselect4.result | 27 | ||||
-rw-r--r-- | mysql-test/t/subselect4.test | 24 | ||||
-rw-r--r-- | sql/item.cc | 27 | ||||
-rw-r--r-- | sql/item.h | 10 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 2 | ||||
-rw-r--r-- | sql/item_subselect.cc | 49 | ||||
-rw-r--r-- | sql/item_subselect.h | 6 |
7 files changed, 129 insertions, 16 deletions
diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result index 25937373e5e..2e24cbcb40c 100644 --- a/mysql-test/r/subselect4.result +++ b/mysql-test/r/subselect4.result @@ -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); ERROR 42S22: Unknown column 's.id' in 'group statement' 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 diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test index fd36023435f..f19a654de64 100644 --- a/mysql-test/t/subselect4.test +++ b/mysql-test/t/subselect4.test @@ -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); 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 diff --git a/sql/item.cc b/sql/item.cc index e633964270b..45f40874ddb 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -9690,7 +9690,7 @@ bool Item_cache_int::cache_value() return FALSE; value_cached= TRUE; value= example->val_int_result(); - null_value= example->null_value; + null_value_inside= null_value= example->null_value; unsigned_flag= example->unsigned_flag; return TRUE; } @@ -9860,7 +9860,7 @@ bool Item_cache_temporal::cache_value() return true; value= pack_time(<ime); } - null_value= example->null_value; + null_value_inside= null_value= example->null_value; return true; } @@ -9955,7 +9955,7 @@ bool Item_cache_real::cache_value() return FALSE; value_cached= TRUE; value= example->val_result(); - null_value= example->null_value; + null_value_inside= null_value= example->null_value; return TRUE; } @@ -10017,7 +10017,8 @@ bool Item_cache_decimal::cache_value() return FALSE; value_cached= TRUE; 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); return TRUE; } @@ -10083,11 +10084,14 @@ Item *Item_cache_decimal::convert_to_basic_const_item(THD *thd) bool Item_cache_str::cache_value() { if (!example) + { + DBUG_ASSERT(value_cached == FALSE); return FALSE; + } value_cached= TRUE; value_buff.set(buffer, sizeof(buffer), example->collation.collation); value= example->str_result(&value_buff); - if ((null_value= example->null_value)) + if ((null_value= null_value_inside= example->null_value)) value= 0; else if (value != &value_buff) { @@ -10186,6 +10190,8 @@ Item *Item_cache_str::convert_to_basic_const_item(THD *thd) bool Item_cache_row::setup(THD *thd, Item *item) { example= item; + null_value= true; + if (!values && allocate(thd, item->cols())) return 1; for (uint i= 0; i < item_count; i++) @@ -10218,12 +10224,19 @@ bool Item_cache_row::cache_value() if (!example) return FALSE; value_cached= TRUE; - null_value= 0; + null_value= TRUE; + null_value_inside= false; 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++) { 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; } diff --git a/sql/item.h b/sql/item.h index 823ffd873b6..cb1e8519b27 100644 --- a/sql/item.h +++ b/sql/item.h @@ -5658,6 +5658,14 @@ protected: */ bool value_cached; 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_basic_constant(thd), Type_handler_hybrid_field_type(MYSQL_TYPE_STRING), @@ -5667,6 +5675,7 @@ public: fixed= 1; maybe_null= 1; null_value= 1; + null_value_inside= true; } protected: Item_cache(THD *thd, enum_field_types field_type_arg): @@ -5678,6 +5687,7 @@ protected: fixed= 1; maybe_null= 1; null_value= 1; + null_value_inside= true; } public: diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d5b89f13f04..7b7604053e3 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1598,7 +1598,7 @@ longlong Item_in_optimizer::val_int() DBUG_RETURN(res); } - if (cache->null_value) + if (cache->null_value_inside) { DBUG_PRINT("info", ("Left NULL...")); /* diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 3424df1eb91..4b8f118ca43 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -851,7 +851,7 @@ bool Item_subselect::expr_cache_is_needed(THD *thd) 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() void Item_singlerow_subselect::bring_value() { 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 reset(); } @@ -1345,7 +1355,11 @@ longlong Item_singlerow_subselect::val_int() { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->val_int(); + { + longlong val= value->val_int(); + null_value= value->null_value; + return val; + } if (!exec() && !value->null_value) { null_value= FALSE; @@ -1354,6 +1368,7 @@ longlong Item_singlerow_subselect::val_int() else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1362,7 +1377,11 @@ String *Item_singlerow_subselect::val_str(String *str) { DBUG_ASSERT(fixed == 1); 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) { null_value= FALSE; @@ -1371,6 +1390,7 @@ String *Item_singlerow_subselect::val_str(String *str) else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1380,7 +1400,11 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); 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) { null_value= FALSE; @@ -1389,6 +1413,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1398,7 +1423,11 @@ bool Item_singlerow_subselect::val_bool() { DBUG_ASSERT(fixed == 1); if (forced_const) - return value->val_bool(); + { + bool val= value->val_bool(); + null_value= value->null_value; + return val; + } if (!exec() && !value->null_value) { null_value= FALSE; @@ -1407,6 +1436,7 @@ bool Item_singlerow_subselect::val_bool() else { reset(); + DBUG_ASSERT(null_value); return 0; } } @@ -1416,7 +1446,11 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) { DBUG_ASSERT(fixed == 1); 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) { null_value= FALSE; @@ -1425,6 +1459,7 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) else { reset(); + DBUG_ASSERT(null_value); return 1; } } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 9116238c640..130f90839e3 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -48,7 +48,11 @@ class Cached_item; class Item_subselect :public Item_result_field, 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 */ protected: /* thread handler, will be assigned in fix_fields only */ |