From ca79343359b2da6b03c3f35df3d52f2ce3607eab Mon Sep 17 00:00:00 2001 From: "gkodinov@lsmy3.wdf.sap.corp" <> Date: Fri, 28 Apr 2006 11:23:31 +0200 Subject: [PATCH] BUG#18492: mysqld reports ER_ILLEGAL_REFERENCE in --ps-protocol In the code that converts IN predicates to EXISTS predicates it is changing the select list elements to constant 1. Example : SELECT ... FROM ... WHERE a IN (SELECT c FROM ...) is transformed to : SELECT ... FROM ... WHERE EXISTS (SELECT 1 FROM ... HAVING a = c) However there can be no FROM clause in the IN subquery and it may not be a simple select : SELECT ... FROM ... WHERE a IN (SELECT f(..) AS c UNION SELECT ...) This query is transformed to : SELECT ... FROM ... WHERE EXISTS (SELECT 1 FROM (SELECT f(..) AS c UNION SELECT ...) x HAVING a = c) In the above query c in the HAVING clause is made to be an Item_null_helper (a subclass of Item_ref) pointing to the real Item_field (which is not referenced anywhere else in the query anymore). This is done because Item_ref_null_helper collects information whether there are NULL values in the result. This is OK for directly executed statements, because the Item_field pointed by the Item_null_helper is already fixed when the transformation is done. But when executed as a prepared statement all the Item instances are "un-fixed" before the recompilation of the prepared statement. So when the Item_null_helper gets fixed it discovers that the Item_field it points to is not fixed and issues an error. The remedy is to keep the original select list references when there are no tables in the FROM clause. So the above becomes : SELECT ... FROM ... WHERE EXISTS (SELECT c FROM (SELECT f(..) AS c UNION SELECT ...) x HAVING a = c) In this way c is referenced directly in the select list as well as by reference in the HAVING clause. So it gets correctly fixed even with prepared statements. And since the Item_null_helper subclass of Item_ref_null_helper is not used anywhere else it's taken out. --- mysql-test/r/ps_11bugs.result | 14 ++++++++++++++ mysql-test/r/subselect.result | 2 +- mysql-test/t/ps_11bugs.test | 14 ++++++++++++++ sql/item.cc | 8 -------- sql/item.h | 13 ------------- sql/item_subselect.cc | 16 +++++++++------- 6 files changed, 38 insertions(+), 29 deletions(-) diff --git a/mysql-test/r/ps_11bugs.result b/mysql-test/r/ps_11bugs.result index c0d7fe502af..c849c25d646 100644 --- a/mysql-test/r/ps_11bugs.result +++ b/mysql-test/r/ps_11bugs.result @@ -116,3 +116,17 @@ execute st_1676 using @arg0, @arg1, @arg2; cola colb cold aaaa yyyy R drop table t1, t2; +create table t1 (a int primary key); +insert into t1 values (1); +explain select * from t1 where 3 in (select (1+1) union select 1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used +3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL +select * from t1 where 3 in (select (1+1) union select 1); +a +prepare st_18492 from 'select * from t1 where 3 in (select (1+1) union select 1)'; +execute st_18492; +a +drop table t1; diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 500aa4b1728..7925715a8b7 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -734,7 +734,7 @@ id select_type table type possible_keys key key_len ref rows Extra 3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL Warnings: -Note 1003 select test.t2.id AS `id` from test.t2 where (test.t2.id,(select 1 AS `Not_used` having ((test.t2.id) = (1)) union select 1 AS `Not_used` having ((test.t2.id) = (3)))) +Note 1003 select test.t2.id AS `id` from test.t2 where (test.t2.id,(select 1 AS `1` having ((test.t2.id) = (1)) union select 3 AS `3` having ((test.t2.id) = (3)))) SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 3); id SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); diff --git a/mysql-test/t/ps_11bugs.test b/mysql-test/t/ps_11bugs.test index e214afeaaf3..ff1c87f3bd8 100644 --- a/mysql-test/t/ps_11bugs.test +++ b/mysql-test/t/ps_11bugs.test @@ -130,3 +130,17 @@ drop table t1, t2; # end of bug#1676 # End of 4.1 tests + +# bug#18492: mysqld reports ER_ILLEGAL_REFERENCE in --ps-protocol + +create table t1 (a int primary key); +insert into t1 values (1); + +explain select * from t1 where 3 in (select (1+1) union select 1); + +select * from t1 where 3 in (select (1+1) union select 1); + +prepare st_18492 from 'select * from t1 where 3 in (select (1+1) union select 1)'; +execute st_18492; + +drop table t1; diff --git a/sql/item.cc b/sql/item.cc index 5964ed388c6..7c9f6ec77fb 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2716,14 +2716,6 @@ void Item_ref_null_helper::print(String *str) } -void Item_null_helper::print(String *str) -{ - str->append("(", 14); - store->print(str); - str->append(')'); -} - - bool Item_default_value::eq(const Item *item, bool binary_cmp) const { return item->type() == DEFAULT_VALUE_ITEM && diff --git a/sql/item.h b/sql/item.h index 1d01ce0d3f3..66da7ad4e74 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1045,19 +1045,6 @@ public: } }; -class Item_null_helper :public Item_ref_null_helper -{ - Item *store; -public: - Item_null_helper(Item_in_subselect* master, Item *item, - const char *table_name_par, const char *field_name_par) - :Item_ref_null_helper(master, &item, table_name_par, field_name_par), - store(item) - { ref= &store; } - void print(String *str); -}; - - /* The following class is used to optimize comparing of date and bigint columns We need to save the original item ('ref') to be able to call diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index c8405a9f8f4..8241a8e0402 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -822,13 +822,13 @@ Item_in_subselect::single_value_transformer(JOIN *join, } else { - select_lex->item_list.empty(); - select_lex->item_list.push_back(new Item_int("Not_used", - (longlong) 1, 21)); - select_lex->ref_pointer_array[0]= select_lex->item_list.head(); if (select_lex->table_list.elements) { Item *having= item, *orig_item= item; + select_lex->item_list.empty(); + select_lex->item_list.push_back(new Item_int("Not_used", + (longlong) 1, 21)); + select_lex->ref_pointer_array[0]= select_lex->item_list.head(); item= func->create(expr, item); if (!abort_on_null && orig_item->maybe_null) { @@ -875,9 +875,11 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->having= join->having= func->create(expr, - new Item_null_helper(this, item, - (char *)"", - (char *)"")); + new Item_ref_null_helper(this, + select_lex->ref_pointer_array, + (char *)"", + (char *)"")); + select_lex->having_fix_field= 1; if (join->having->fix_fields(thd, join->tables_list, 0))