diff options
author | unknown <konstantin@mysql.com> | 2005-07-13 17:38:55 +0400 |
---|---|---|
committer | unknown <konstantin@mysql.com> | 2005-07-13 17:38:55 +0400 |
commit | ece17ba3cf18546af2ccad110cf6c6754ae90667 (patch) | |
tree | 744c6be51ce004c24a4e80e1fb469009a0941e61 | |
parent | bc0eef3eccafcdb1bf17f2d46794e3b2be392e1a (diff) | |
download | mariadb-git-ece17ba3cf18546af2ccad110cf6c6754ae90667.tar.gz |
- a fix for Bug#11458 "Prepared statement with subselects return random
data": remove the fix for another bug (8807) that
added OUTER_REF_TABLE_BIT to all subqueries that used a placeholder
to prevent their evaluation at prepare. As this bit hanged in
Item_subselect::used_tables_cache for ever, a constant subquery with
a placeholder was never evaluated as such, which caused wrong
choice of the execution plan for the statement.
- to fix Bug#8807 backport a better fix from 5.0
- post-review fixes.
mysql-test/r/ps.result:
Bug#11458: test results fixed
mysql-test/t/ps.test:
- add a test case for Bug#11458 "Prepared statement with subselects return
random data"
sql/item.cc:
- remove unnecessary Item_param::fix_fields
- fix Item_param::set_null to set item_type accordingly (safety:
Item_param should behave like a basic constant).
sql/item.h:
Remove Item_param::fix_fields
sql/item_subselect.h:
Remove no more existing friend.
sql/mysql_priv.h:
Add UNCACHEABLE_PREPARE to mark all subqueries as uncacheable if
in statement prepare (backport from 5.0).
sql/sql_lex.h:
Comment fixed.
sql/sql_parse.cc:
If in statement prepare, mark all subqueries as uncacheable (backport
from 5.0)
sql/sql_prepare.cc:
Switch off the uncacheable flag from all subqueries after statement
prepare is done (backport from 5.0)
-rw-r--r-- | mysql-test/r/ps.result | 57 | ||||
-rw-r--r-- | mysql-test/t/ps.test | 68 | ||||
-rw-r--r-- | sql/item.cc | 29 | ||||
-rw-r--r-- | sql/item.h | 1 | ||||
-rw-r--r-- | sql/item_subselect.h | 1 | ||||
-rw-r--r-- | sql/mysql_priv.h | 2 | ||||
-rw-r--r-- | sql/sql_lex.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 8 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 13 |
9 files changed, 146 insertions, 34 deletions
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index ca38f1c75cb..b1b06dc4019 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -563,3 +563,60 @@ execute stmt; execute stmt; deallocate prepare stmt; drop table t1; +create table t1 ( +id int(11) unsigned not null primary key auto_increment, +partner_id varchar(35) not null, +t1_status_id int(10) unsigned +); +insert into t1 values ("1", "partner1", "10"), ("2", "partner2", "10"), +("3", "partner3", "10"), ("4", "partner4", "10"); +create table t2 ( +id int(11) unsigned not null default '0', +t1_line_id int(11) unsigned not null default '0', +article_id varchar(20), +sequence int(11) not null default '0', +primary key (id,t1_line_id) +); +insert into t2 values ("1", "1", "sup", "0"), ("2", "1", "sup", "1"), +("2", "2", "sup", "2"), ("2", "3", "sup", "3"), +("2", "4", "imp", "4"), ("3", "1", "sup", "0"), +("4", "1", "sup", "0"); +create table t3 ( +id int(11) not null default '0', +preceeding_id int(11) not null default '0', +primary key (id,preceeding_id) +); +create table t4 ( +user_id varchar(50) not null, +article_id varchar(20) not null, +primary key (user_id,article_id) +); +insert into t4 values("nicke", "imp"); +prepare stmt from +'select distinct t1.partner_id +from t1 left join t3 on t1.id = t3.id + left join t1 pp on pp.id = t3.preceeding_id +where + exists ( + select * + from t2 as pl_inner + where pl_inner.id = t1.id + and pl_inner.sequence <= ( + select min(sequence) from t2 pl_seqnr + where pl_seqnr.id = t1.id + ) + and exists ( + select * from t4 + where t4.article_id = pl_inner.article_id + and t4.user_id = ? + ) + ) + and t1.id = ? +group by t1.id +having count(pp.id) = 0'; +set @user_id = 'nicke'; +set @id = '2'; +execute stmt using @user_id, @id; +partner_id +execute stmt using @user_id, @id; +partner_id diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index bb1052c7337..24276acf933 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -581,3 +581,71 @@ execute stmt; execute stmt; deallocate prepare stmt; drop table t1; + +# +# Bug#11458 "Prepared statement with subselects return random data": +# drop PARAM_TABLE_BIT from the list of tables used by a subquery +# +create table t1 ( + id int(11) unsigned not null primary key auto_increment, + partner_id varchar(35) not null, + t1_status_id int(10) unsigned +); + +insert into t1 values ("1", "partner1", "10"), ("2", "partner2", "10"), + ("3", "partner3", "10"), ("4", "partner4", "10"); + +create table t2 ( + id int(11) unsigned not null default '0', + t1_line_id int(11) unsigned not null default '0', + article_id varchar(20), + sequence int(11) not null default '0', + primary key (id,t1_line_id) +); + +insert into t2 values ("1", "1", "sup", "0"), ("2", "1", "sup", "1"), + ("2", "2", "sup", "2"), ("2", "3", "sup", "3"), + ("2", "4", "imp", "4"), ("3", "1", "sup", "0"), + ("4", "1", "sup", "0"); + +create table t3 ( + id int(11) not null default '0', + preceeding_id int(11) not null default '0', + primary key (id,preceeding_id) +); + +create table t4 ( + user_id varchar(50) not null, + article_id varchar(20) not null, + primary key (user_id,article_id) +); + +insert into t4 values("nicke", "imp"); + +prepare stmt from +'select distinct t1.partner_id +from t1 left join t3 on t1.id = t3.id + left join t1 pp on pp.id = t3.preceeding_id +where + exists ( + select * + from t2 as pl_inner + where pl_inner.id = t1.id + and pl_inner.sequence <= ( + select min(sequence) from t2 pl_seqnr + where pl_seqnr.id = t1.id + ) + and exists ( + select * from t4 + where t4.article_id = pl_inner.article_id + and t4.user_id = ? + ) + ) + and t1.id = ? +group by t1.id +having count(pp.id) = 0'; +set @user_id = 'nicke'; +set @id = '2'; +execute stmt using @user_id, @id; +execute stmt using @user_id, @id; + diff --git a/sql/item.cc b/sql/item.cc index 3bdaf856f2a..19b88c115b9 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -889,6 +889,7 @@ void Item_param::set_null() max_length= 0; decimals= 0; state= NULL_VALUE; + item_type= Item::NULL_ITEM; DBUG_VOID_RETURN; } @@ -1339,34 +1340,6 @@ bool Item_param::convert_str_value(THD *thd) return rc; } -bool Item_param::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) -{ - DBUG_ASSERT(fixed == 0); - SELECT_LEX *cursel= (SELECT_LEX *) thd->lex->current_select; - - /* - Parameters in a subselect should mark the subselect as not constant - during prepare - */ - if (state == NO_VALUE) - { - /* - SELECT_LEX_UNIT::item set only for subqueries, so test of it presence - can be barrier to stop before derived table SELECT or very outer SELECT - */ - for(; - cursel->master_unit()->item; - cursel= cursel->outer_select()) - { - Item_subselect *subselect_item= cursel->master_unit()->item; - subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; - subselect_item->const_item_cache= 0; - } - } - fixed= 1; - return 0; -} - bool Item_param::basic_const_item() const { diff --git a/sql/item.h b/sql/item.h index 8de2adeb730..a8b0bfe9512 100644 --- a/sql/item.h +++ b/sql/item.h @@ -564,7 +564,6 @@ public: bool get_time(TIME *tm); bool get_date(TIME *tm, uint fuzzydate); int save_in_field(Field *field, bool no_conversions); - bool fix_fields(THD *, struct st_table_list *, Item **); void set_null(); void set_int(longlong i, uint32 max_length_arg); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 20ba838e61c..dec32398a80 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -119,7 +119,6 @@ public: friend class Item_in_optimizer; friend bool Item_field::fix_fields(THD *, TABLE_LIST *, Item **); friend bool Item_ref::fix_fields(THD *, TABLE_LIST *, Item **); - friend bool Item_param::fix_fields(THD *, TABLE_LIST *, Item **); }; /* single value subselect */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index cc58e34d582..953bcf5585c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -251,6 +251,8 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; #define UNCACHEABLE_SIDEEFFECT 4 // forcing to save JOIN for explain #define UNCACHEABLE_EXPLAIN 8 +/* Don't evaluate subqueries in prepare even if they're not correlated */ +#define UNCACHEABLE_PREPARE 16 #ifdef EXTRA_DEBUG /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 927982e444f..07b5c9d8edf 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -257,6 +257,7 @@ public: UNCACHEABLE_RAND UNCACHEABLE_SIDEEFFECT UNCACHEABLE_EXPLAIN + UNCACHEABLE_PREPARE */ uint8 uncacheable; enum sub_select_type linkage; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d7fd3239df5..dc55a842263 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4123,6 +4123,14 @@ mysql_new_select(LEX *lex, bool move_down) select_lex->select_number= ++lex->thd->select_number; select_lex->init_query(); select_lex->init_select(); + /* + Don't evaluate this subquery during statement prepare even if + it's a constant one. The flag is switched off in the end of + mysql_stmt_prepare. + */ + if (lex->thd->current_arena->is_stmt_prepare()) + select_lex->uncacheable|= UNCACHEABLE_PREPARE; + if (move_down) { lex->subqueries= TRUE; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 9e2612c5661..f7e2bd0467e 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1657,13 +1657,18 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, { stmt->setup_set_params(); SELECT_LEX *sl= stmt->lex->all_selects_list; - /* - Save WHERE clause pointers, because they may be changed during query - optimisation. - */ for (; sl; sl= sl->next_select_in_list()) { + /* + Save WHERE clause pointers, because they may be changed + during query optimisation. + */ sl->prep_where= sl->where; + /* + Switch off a temporary flag that prevents evaluation of + subqueries in statement prepare. + */ + sl->uncacheable&= ~UNCACHEABLE_PREPARE; } stmt->state= Item_arena::PREPARED; } |