mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
- 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)
This commit is contained in:
@ -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;
|
|||||||
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;
|
||||||
|
|
||||||
|
29
sql/item.cc
29
sql/item.cc
@ -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)
|
|||||||
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:
|
|||||||
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:
|
|||||||
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;
|
|||||||
#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:
|
|||||||
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)
|
|||||||
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,
|
|||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user