Commit bef558b7 authored by konstantin@mysql.com's avatar konstantin@mysql.com

- 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.
parent 670428f6
...@@ -563,3 +563,60 @@ execute stmt; ...@@ -563,3 +563,60 @@ execute stmt;
execute stmt; execute stmt;
deallocate prepare stmt; deallocate prepare stmt;
drop table t1; 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
...@@ -581,3 +581,71 @@ execute stmt; ...@@ -581,3 +581,71 @@ execute stmt;
execute stmt; execute stmt;
deallocate prepare stmt; deallocate prepare stmt;
drop table t1; 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;
...@@ -889,6 +889,7 @@ void Item_param::set_null() ...@@ -889,6 +889,7 @@ void Item_param::set_null()
max_length= 0; max_length= 0;
decimals= 0; decimals= 0;
state= NULL_VALUE; state= NULL_VALUE;
item_type= Item::NULL_ITEM;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -1339,34 +1340,6 @@ bool Item_param::convert_str_value(THD *thd) ...@@ -1339,34 +1340,6 @@ bool Item_param::convert_str_value(THD *thd)
return rc; 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 bool Item_param::basic_const_item() const
{ {
......
...@@ -564,7 +564,6 @@ public: ...@@ -564,7 +564,6 @@ public:
bool get_time(TIME *tm); bool get_time(TIME *tm);
bool get_date(TIME *tm, uint fuzzydate); bool get_date(TIME *tm, uint fuzzydate);
int save_in_field(Field *field, bool no_conversions); int save_in_field(Field *field, bool no_conversions);
bool fix_fields(THD *, struct st_table_list *, Item **);
void set_null(); void set_null();
void set_int(longlong i, uint32 max_length_arg); void set_int(longlong i, uint32 max_length_arg);
......
...@@ -119,7 +119,6 @@ public: ...@@ -119,7 +119,6 @@ public:
friend class Item_in_optimizer; friend class Item_in_optimizer;
friend bool Item_field::fix_fields(THD *, TABLE_LIST *, Item **); friend bool Item_field::fix_fields(THD *, TABLE_LIST *, Item **);
friend bool Item_ref::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 */ /* single value subselect */
......
...@@ -251,6 +251,8 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; ...@@ -251,6 +251,8 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
#define UNCACHEABLE_SIDEEFFECT 4 #define UNCACHEABLE_SIDEEFFECT 4
// forcing to save JOIN for explain // forcing to save JOIN for explain
#define UNCACHEABLE_EXPLAIN 8 #define UNCACHEABLE_EXPLAIN 8
/* Don't evaluate subqueries in prepare even if they're not correlated */
#define UNCACHEABLE_PREPARE 16
#ifdef EXTRA_DEBUG #ifdef EXTRA_DEBUG
/* /*
......
...@@ -257,6 +257,7 @@ public: ...@@ -257,6 +257,7 @@ public:
UNCACHEABLE_RAND UNCACHEABLE_RAND
UNCACHEABLE_SIDEEFFECT UNCACHEABLE_SIDEEFFECT
UNCACHEABLE_EXPLAIN UNCACHEABLE_EXPLAIN
UNCACHEABLE_PREPARE
*/ */
uint8 uncacheable; uint8 uncacheable;
enum sub_select_type linkage; enum sub_select_type linkage;
......
...@@ -4123,6 +4123,14 @@ mysql_new_select(LEX *lex, bool move_down) ...@@ -4123,6 +4123,14 @@ mysql_new_select(LEX *lex, bool move_down)
select_lex->select_number= ++lex->thd->select_number; select_lex->select_number= ++lex->thd->select_number;
select_lex->init_query(); select_lex->init_query();
select_lex->init_select(); 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) if (move_down)
{ {
lex->subqueries= TRUE; lex->subqueries= TRUE;
......
...@@ -1657,13 +1657,18 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, ...@@ -1657,13 +1657,18 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
{ {
stmt->setup_set_params(); stmt->setup_set_params();
SELECT_LEX *sl= stmt->lex->all_selects_list; 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()) 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; 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; stmt->state= Item_arena::PREPARED;
} }
......
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