Commit 61a68ed6 authored by unknown's avatar unknown

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.


mysql-test/r/trigger.result:
  Added test case for bug #11973 "SELECT .. INTO var_name; in trigger cause
  crash on update".
mysql-test/t/trigger.test:
  Added test case for bug #11973 "SELECT .. INTO var_name; in trigger cause
  crash on update".
sql/item_subselect.cc:
  subselect_engine:
    Moved implementation of set_thd() method to item_subselect.cc,
    since now it also sets "thd" for subselect_engine::result.
sql/item_subselect.h:
  subselect_engine:
    Moved implementation of set_thd() method to item_subselect.cc,
    since now it also sets "thd" for subselect_engine::result.
sql/sql_class.h:
  select_result:
    Added set_thd() method for updating select_result::thd value (we need this
    in cases when statement to which this select_result belongs will be used
    in different threads, as it happens for statements in trigger body).
  multi_delete/multi_update:
    Got rid of redundant "thd" member (we already have it in select_result).
sql/sql_delete.cc:
  multi_delete:
    Got rid of redundant "thd" member (we already have it in select_result).
sql/sql_lex.h:
  st_select_lex_unit:
    Added set_thd() method for updating st_select_lex_unit::thd value
    (we need this in cases when statement to which this unit belongs will
     be used in different threads, as it happens for statements in trigger body).
    We don't update thd of select_result pointed by st_select_lex_unit::result
    in this method, since it is either have too short lifetime (i.e. created for
    each execution) or is accessible via Item_subquery or LEX::result and thus
    already taken care of.
sql/sql_parse.cc:
  multi_delete:
    Got rid of redundant "thd" member (we already have it in select_result).
sql/sql_prepare.cc:
  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.
sql/sql_update.cc:
  multi_update:
    Got rid of redundant "thd" member (we already have it in select_result).
parent 7b969ddd
...@@ -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