Commit 70fdfcb6 authored by Staale Smedseng's avatar Staale Smedseng

Merge from 5.0-bugteam

parents 8d3aceb0 fce11a8b
...@@ -1960,6 +1960,26 @@ select * from t2; ...@@ -1960,6 +1960,26 @@ select * from t2;
s1 s1
drop table t1; drop table t1;
drop temporary table t2; drop temporary table t2;
#------------------------------------------------------------------------
# Bug#39953 Triggers are not working properly with multi table updates
#------------------------------------------------------------------------
DROP TABLE IF EXISTS t1;
DROP TRIGGER IF EXISTS t_insert;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1 (a int, date_insert timestamp, PRIMARY KEY (a));
INSERT INTO t1 (a) VALUES (2),(5);
CREATE TABLE t2 (a int, b int, PRIMARY KEY (a));
CREATE TRIGGER t_insert AFTER INSERT ON t2 FOR EACH ROW BEGIN UPDATE t1,t2 SET
date_insert=NOW() WHERE t1.a=t2.b AND t2.a=NEW.a; END |
INSERT INTO t2 (a,b) VALUES (1,2);
DROP TRIGGER t_insert;
CREATE TRIGGER t_insert AFTER INSERT ON t2 FOR EACH ROW BEGIN UPDATE t1,t2 SET
date_insert=NOW(),b=b+1 WHERE t1.a=t2.b AND t2.a=NEW.a; END |
INSERT INTO t2 (a,b) VALUES (3,5);
ERROR HY000: Can't update table 't2' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
DROP TABLE t1;
DROP TRIGGER t_insert;
DROP TABLE t2;
End of 5.0 tests End of 5.0 tests
drop table if exists table_25411_a; drop table if exists table_25411_a;
drop table if exists table_25411_b; drop table if exists table_25411_b;
......
...@@ -2218,6 +2218,39 @@ select * from t1; ...@@ -2218,6 +2218,39 @@ select * from t1;
select * from t2; select * from t2;
drop table t1; drop table t1;
drop temporary table t2; drop temporary table t2;
--echo #------------------------------------------------------------------------
--echo # Bug#39953 Triggers are not working properly with multi table updates
--echo #------------------------------------------------------------------------
--disable_warnings
DROP TABLE IF EXISTS t1;
DROP TRIGGER IF EXISTS t_insert;
DROP TABLE IF EXISTS t2;
--enable_warnings
CREATE TABLE t1 (a int, date_insert timestamp, PRIMARY KEY (a));
INSERT INTO t1 (a) VALUES (2),(5);
CREATE TABLE t2 (a int, b int, PRIMARY KEY (a));
DELIMITER |;
CREATE TRIGGER t_insert AFTER INSERT ON t2 FOR EACH ROW BEGIN UPDATE t1,t2 SET
date_insert=NOW() WHERE t1.a=t2.b AND t2.a=NEW.a; END |
DELIMITER ;|
INSERT INTO t2 (a,b) VALUES (1,2);
DROP TRIGGER t_insert;
DELIMITER |;
CREATE TRIGGER t_insert AFTER INSERT ON t2 FOR EACH ROW BEGIN UPDATE t1,t2 SET
date_insert=NOW(),b=b+1 WHERE t1.a=t2.b AND t2.a=NEW.a; END |
DELIMITER ;|
--error ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
INSERT INTO t2 (a,b) VALUES (3,5);
DROP TABLE t1;
DROP TRIGGER t_insert;
DROP TABLE t2;
--echo End of 5.0 tests --echo End of 5.0 tests
# #
......
...@@ -2600,27 +2600,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ...@@ -2600,27 +2600,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
{ // Using table locks { // Using table locks
TABLE *best_table= 0; TABLE *best_table= 0;
int best_distance= INT_MIN; int best_distance= INT_MIN;
bool check_if_used= thd->prelocked_mode &&
((int) table_list->lock_type >=
(int) TL_WRITE_ALLOW_WRITE);
for (table=thd->open_tables; table ; table=table->next) for (table=thd->open_tables; table ; table=table->next)
{ {
if (table->s->table_cache_key.length == key_length && if (table->s->table_cache_key.length == key_length &&
!memcmp(table->s->table_cache_key.str, key, key_length)) !memcmp(table->s->table_cache_key.str, key, key_length))
{ {
if (check_if_used && table->query_id &&
table->query_id != thd->query_id)
{
/*
If we are in stored function or trigger we should ensure that
we won't change table that is already used by calling statement.
So if we are opening table for writing, we should check that it
is not already open by some calling stamement.
*/
my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0),
table->s->table_name.str);
DBUG_RETURN(0);
}
/* /*
When looking for a usable TABLE, ignore MERGE children, as they When looking for a usable TABLE, ignore MERGE children, as they
belong to their parent and cannot be used explicitly. belong to their parent and cannot be used explicitly.
...@@ -2649,13 +2633,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ...@@ -2649,13 +2633,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
{ {
best_distance= distance; best_distance= distance;
best_table= table; best_table= table;
if (best_distance == 0 && !check_if_used) if (best_distance == 0)
{ {
/* /*
If we have found perfect match and we don't need to check that We have found a perfect match and can finish iterating
table is not used by one of calling statements (assuming that through open tables list. Check for table use conflict
we are inside of function or trigger) we can finish iterating between calling statement and SP/trigger is done in
through open tables list. lock_tables().
*/ */
break; break;
} }
...@@ -4888,9 +4872,9 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, ...@@ -4888,9 +4872,9 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
lock_flags Flags passed to mysql_lock_table lock_flags Flags passed to mysql_lock_table
NOTE NOTE
This function don't do anything like SP/SF/views/triggers analysis done This function doesn't do anything like SP/SF/views/triggers analysis done
in open_tables(). It is intended for opening of only one concrete table. in open_table()/lock_tables(). It is intended for opening of only one
And used only in special contexts. concrete table. And used only in special contexts.
RETURN VALUES RETURN VALUES
table Opened table table Opened table
...@@ -4908,6 +4892,9 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, ...@@ -4908,6 +4892,9 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
bool refresh; bool refresh;
DBUG_ENTER("open_ltable"); DBUG_ENTER("open_ltable");
/* should not be used in a prelocked_mode context, see NOTE above */
DBUG_ASSERT(!thd->prelocked_mode);
thd_proc_info(thd, "Opening table"); thd_proc_info(thd, "Opening table");
thd->current_tablenr= 0; thd->current_tablenr= 0;
/* open_ltable can be used only for BASIC TABLEs */ /* open_ltable can be used only for BASIC TABLEs */
...@@ -5374,8 +5361,29 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) ...@@ -5374,8 +5361,29 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
table && table != first_not_own; table && table != first_not_own;
table= table->next_global) table= table->next_global)
{ {
if (!table->placeholder() && if (table->placeholder())
check_lock_and_start_stmt(thd, table->table, table->lock_type)) continue;
/*
In a stored function or trigger we should ensure that we won't change
a table that is already used by the calling statement.
*/
if (thd->prelocked_mode &&
table->lock_type >= TL_WRITE_ALLOW_WRITE)
{
for (TABLE* opentab= thd->open_tables; opentab; opentab= opentab->next)
{
if (table->table->s == opentab->s && opentab->query_id &&
table->table->query_id != opentab->query_id)
{
my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0),
table->table->s->table_name.str);
DBUG_RETURN(-1);
}
}
}
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
{ {
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
......
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