Commit 60511c3d authored by dlenev@mysql.com's avatar dlenev@mysql.com

Clumsy but working fix for bug #11973 "SELECT .. INTO var_name; in trigger

cause crash on update".

Let us update "thd" pointer in LEX, all its units and in LEX::result before
executing statement in trigger body, since triggers are associated with TABLE
object and because of this can be used in different threads.
parent 26e4741d
...@@ -635,3 +635,16 @@ show triggers; ...@@ -635,3 +635,16 @@ show triggers;
Trigger Event Table Statement Timing Created sql_mode Trigger Event Table Statement Timing Created sql_mode
t1_bi INSERT t1 set new.a = '2004-01-00' BEFORE # t1_bi INSERT t1 set new.a = '2004-01-00' BEFORE #
drop table t1; drop table t1;
create table t1 (id int, data int, username varchar(16));
insert into t1 (id, data) values (1, 0);
create trigger t1_whoupdated before update on t1 for each row
begin
declare user varchar(32);
declare i int;
select user() into user;
set NEW.username = user;
select count(*) from ((select 1) union (select 2)) as d1 into i;
end|
update t1 set data = 1;
update t1 set data = 2;
drop table t1;
...@@ -642,3 +642,26 @@ show create table t1; ...@@ -642,3 +642,26 @@ show create table t1;
--replace_column 6 # --replace_column 6 #
show triggers; show triggers;
drop table t1; drop table t1;
# Test for bug #11973 "SELECT .. INTO var_name; in trigger cause
# crash on update"
create table t1 (id int, data int, username varchar(16));
insert into t1 (id, data) values (1, 0);
delimiter |;
create trigger t1_whoupdated before update on t1 for each row
begin
declare user varchar(32);
declare i int;
select user() into user;
set NEW.username = user;
select count(*) from ((select 1) union (select 2)) as d1 into i;
end|
delimiter ;|
update t1 set data = 1;
connect (addconroot, localhost, root,,);
connection addconroot;
update t1 set data = 2;
connection default;
drop table t1;
...@@ -1271,6 +1271,14 @@ void Item_allany_subselect::print(String *str) ...@@ -1271,6 +1271,14 @@ void Item_allany_subselect::print(String *str)
} }
void subselect_engine::set_thd(THD *thd_arg)
{
thd= thd_arg;
if (result)
result->set_thd(thd_arg);
}
subselect_single_select_engine:: subselect_single_select_engine::
subselect_single_select_engine(st_select_lex *select, subselect_single_select_engine(st_select_lex *select,
select_subselect *result, select_subselect *result,
......
...@@ -299,8 +299,11 @@ public: ...@@ -299,8 +299,11 @@ public:
virtual ~subselect_engine() {}; // to satisfy compiler virtual ~subselect_engine() {}; // to satisfy compiler
virtual void cleanup()= 0; virtual void cleanup()= 0;
// set_thd should be called before prepare() /*
void set_thd(THD *thd_arg) { thd= thd_arg; } Also sets "thd" for subselect_engine::result.
Should be called before prepare().
*/
void set_thd(THD *thd_arg);
THD * get_thd() { return thd; } THD * get_thd() { return thd; }
virtual int prepare()= 0; virtual int prepare()= 0;
virtual void fix_length_and_dec(Item_cache** row)= 0; virtual void fix_length_and_dec(Item_cache** row)= 0;
......
...@@ -1560,6 +1560,7 @@ public: ...@@ -1560,6 +1560,7 @@ public:
statement/stored procedure. statement/stored procedure.
*/ */
virtual void cleanup(); virtual void cleanup();
void set_thd(THD *thd_arg) { thd= thd_arg; }
}; };
...@@ -1915,14 +1916,13 @@ class multi_delete :public select_result_interceptor ...@@ -1915,14 +1916,13 @@ class multi_delete :public select_result_interceptor
{ {
TABLE_LIST *delete_tables, *table_being_deleted; TABLE_LIST *delete_tables, *table_being_deleted;
Unique **tempfiles; Unique **tempfiles;
THD *thd;
ha_rows deleted, found; ha_rows deleted, found;
uint num_of_tables; uint num_of_tables;
int error; int error;
bool do_delete, transactional_tables, normal_tables, delete_while_scanning; bool do_delete, transactional_tables, normal_tables, delete_while_scanning;
public: public:
multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables); multi_delete(TABLE_LIST *dt, uint num_of_tables);
~multi_delete(); ~multi_delete();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items); bool send_data(List<Item> &items);
...@@ -1938,7 +1938,6 @@ class multi_update :public select_result_interceptor ...@@ -1938,7 +1938,6 @@ class multi_update :public select_result_interceptor
TABLE_LIST *all_tables; /* query/update command tables */ TABLE_LIST *all_tables; /* query/update command tables */
TABLE_LIST *leaves; /* list of leves of join table tree */ TABLE_LIST *leaves; /* list of leves of join table tree */
TABLE_LIST *update_tables, *table_being_updated; TABLE_LIST *update_tables, *table_being_updated;
THD *thd;
TABLE **tmp_tables, *main_table, *table_to_update; TABLE **tmp_tables, *main_table, *table_to_update;
TMP_TABLE_PARAM *tmp_table_param; TMP_TABLE_PARAM *tmp_table_param;
ha_rows updated, found; ha_rows updated, found;
...@@ -1950,7 +1949,7 @@ class multi_update :public select_result_interceptor ...@@ -1950,7 +1949,7 @@ class multi_update :public select_result_interceptor
bool do_update, trans_safe, transactional_tables, ignore; bool do_update, trans_safe, transactional_tables, ignore;
public: public:
multi_update(THD *thd_arg, TABLE_LIST *ut, TABLE_LIST *leaves_list, multi_update(TABLE_LIST *ut, TABLE_LIST *leaves_list,
List<Item> *fields, List<Item> *values, List<Item> *fields, List<Item> *values,
enum_duplicates handle_duplicates, bool ignore); enum_duplicates handle_duplicates, bool ignore);
~multi_update(); ~multi_update();
......
...@@ -407,9 +407,8 @@ bool mysql_multi_delete_prepare(THD *thd) ...@@ -407,9 +407,8 @@ bool mysql_multi_delete_prepare(THD *thd)
} }
multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt, multi_delete::multi_delete(TABLE_LIST *dt, uint num_of_tables_arg)
uint num_of_tables_arg) : delete_tables(dt), deleted(0), found(0),
: delete_tables(dt), thd(thd_arg), deleted(0), found(0),
num_of_tables(num_of_tables_arg), error(0), num_of_tables(num_of_tables_arg), error(0),
do_delete(0), transactional_tables(0), normal_tables(0) do_delete(0), transactional_tables(0), normal_tables(0)
{ {
......
...@@ -458,6 +458,7 @@ public: ...@@ -458,6 +458,7 @@ public:
inline bool is_prepared() { return prepared; } inline bool is_prepared() { return prepared; }
bool change_result(select_subselect *result, select_subselect *old_result); bool change_result(select_subselect *result, select_subselect *old_result);
void set_limit(st_select_lex *values); void set_limit(st_select_lex *values);
void set_thd(THD *thd_arg) { thd= thd_arg; }
friend void lex_start(THD *thd, uchar *buf, uint length); friend void lex_start(THD *thd, uchar *buf, uint length);
friend int subselect_union_engine::exec(); friend int subselect_union_engine::exec();
......
...@@ -3318,7 +3318,7 @@ end_with_restore_list: ...@@ -3318,7 +3318,7 @@ end_with_restore_list:
if ((res= mysql_multi_delete_prepare(thd))) if ((res= mysql_multi_delete_prepare(thd)))
goto error; goto error;
if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, if (!thd->is_fatal_error && (result= new multi_delete(aux_tables,
lex->table_count))) lex->table_count)))
{ {
res= mysql_select(thd, &select_lex->ref_pointer_array, res= mysql_select(thd, &select_lex->ref_pointer_array,
......
...@@ -1850,6 +1850,13 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) ...@@ -1850,6 +1850,13 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
SELECT_LEX *sl= lex->all_selects_list; SELECT_LEX *sl= lex->all_selects_list;
DBUG_ENTER("reinit_stmt_before_use"); DBUG_ENTER("reinit_stmt_before_use");
/*
We have to update "thd" pointer in LEX, all its units and in LEX::result,
since statements which belong to trigger body are associated with TABLE
object and because of this can be used in different threads.
*/
lex->thd= thd;
if (lex->empty_field_list_on_rset) if (lex->empty_field_list_on_rset)
{ {
lex->empty_field_list_on_rset= 0; lex->empty_field_list_on_rset= 0;
...@@ -1888,6 +1895,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) ...@@ -1888,6 +1895,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
unit->types.empty(); unit->types.empty();
/* for derived tables & PS (which can't be reset by Item_subquery) */ /* for derived tables & PS (which can't be reset by Item_subquery) */
unit->reinit_exec_mechanism(); unit->reinit_exec_mechanism();
unit->set_thd(thd);
} }
} }
...@@ -1926,7 +1934,10 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) ...@@ -1926,7 +1934,10 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
lex->select_lex.leaf_tables= lex->leaf_tables_insert; lex->select_lex.leaf_tables= lex->leaf_tables_insert;
if (lex->result) if (lex->result)
{
lex->result->cleanup(); lex->result->cleanup();
lex->result->set_thd(thd);
}
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
......
...@@ -820,7 +820,7 @@ bool mysql_multi_update(THD *thd, ...@@ -820,7 +820,7 @@ bool mysql_multi_update(THD *thd,
if (mysql_multi_update_prepare(thd)) if (mysql_multi_update_prepare(thd))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
if (!(result= new multi_update(thd, table_list, if (!(result= new multi_update(table_list,
thd->lex->select_lex.leaf_tables, thd->lex->select_lex.leaf_tables,
fields, values, fields, values,
handle_duplicates, ignore))) handle_duplicates, ignore)))
...@@ -846,13 +846,13 @@ bool mysql_multi_update(THD *thd, ...@@ -846,13 +846,13 @@ bool mysql_multi_update(THD *thd,
} }
multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, multi_update::multi_update(TABLE_LIST *table_list,
TABLE_LIST *leaves_list, TABLE_LIST *leaves_list,
List<Item> *field_list, List<Item> *value_list, List<Item> *field_list, List<Item> *value_list,
enum enum_duplicates handle_duplicates_arg, enum enum_duplicates handle_duplicates_arg,
bool ignore_arg) bool ignore_arg)
:all_tables(table_list), leaves(leaves_list), update_tables(0), :all_tables(table_list), leaves(leaves_list), update_tables(0),
thd(thd_arg), tmp_tables(0), updated(0), found(0), fields(field_list), tmp_tables(0), updated(0), found(0), fields(field_list),
values(value_list), table_count(0), copy_field(0), values(value_list), table_count(0), copy_field(0),
handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(0), handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(0),
transactional_tables(1), ignore(ignore_arg) transactional_tables(1), ignore(ignore_arg)
......
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