Commit 1cae2808 authored by jan's avatar jan

Forward port r127 from branches/5.0:

Fixed a bug #16229 MySQL/InnoDB uses full explicit table locks 
in trigger processing. Take a InnoDB table lock only if user has explicitly
requested a table lock. Added some additional comments to store_lock() and 
external_lock(). Added test cases for a bug. Fixed some code style errors.
parent 191ae582
...@@ -1004,6 +1004,7 @@ innobase_query_caching_of_table_permitted( ...@@ -1004,6 +1004,7 @@ innobase_query_caching_of_table_permitted(
mutex_enter(&kernel_mutex); mutex_enter(&kernel_mutex);
trx_print(stderr, trx, 1024); trx_print(stderr, trx, 1024);
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
ut_error;
} }
innobase_release_stat_resources(trx); innobase_release_stat_resources(trx);
...@@ -6354,14 +6355,17 @@ ha_innobase::external_lock( ...@@ -6354,14 +6355,17 @@ ha_innobase::external_lock(
TABLES if AUTOCOMMIT=1. It does not make much sense to acquire TABLES if AUTOCOMMIT=1. It does not make much sense to acquire
an InnoDB table lock if it is released immediately at the end an InnoDB table lock if it is released immediately at the end
of LOCK TABLES, and InnoDB's table locks in that case cause of LOCK TABLES, and InnoDB's table locks in that case cause
VERY easily deadlocks. We do not set InnoDB table locks when VERY easily deadlocks.
MySQL sets them at the start of a stored procedure call
(MySQL does have thd->in_lock_tables TRUE there). */ We do not set InnoDB table locks if user has not explicitly
requested a table lock. Note that thd->in_lock_tables
can be TRUE on some cases e.g. at the start of a stored
procedure call (SQLCOM_CALL). */
if (prebuilt->select_lock_type != LOCK_NONE) { if (prebuilt->select_lock_type != LOCK_NONE) {
if (thd->in_lock_tables && if (thd->in_lock_tables &&
thd->lex->sql_command != SQLCOM_CALL && thd->lex->sql_command == SQLCOM_LOCK_TABLES &&
thd->variables.innodb_table_locks && thd->variables.innodb_table_locks &&
(thd->options & OPTION_NOT_AUTOCOMMIT)) { (thd->options & OPTION_NOT_AUTOCOMMIT)) {
...@@ -6844,7 +6848,7 @@ ha_innobase::store_lock( ...@@ -6844,7 +6848,7 @@ ha_innobase::store_lock(
} else if (lock_type != TL_IGNORE) { } else if (lock_type != TL_IGNORE) {
/* We set possible LOCK_X value in external_lock, not yet /* We set possible LOCK_X value in external_lock, not yet
here even if this would be SELECT ... FOR UPDATE */ here even if this would be SELECT ... FOR UPDATE */
prebuilt->select_lock_type = LOCK_NONE; prebuilt->select_lock_type = LOCK_NONE;
...@@ -6853,7 +6857,7 @@ ha_innobase::store_lock( ...@@ -6853,7 +6857,7 @@ ha_innobase::store_lock(
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) { if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) {
/* Starting from 5.0.7, we weaken also the table locks /* Starting from 5.0.7, we weaken also the table locks
set at the start of a MySQL stored procedure call, just like set at the start of a MySQL stored procedure call, just like
we weaken the locks set at the start of an SQL statement. we weaken the locks set at the start of an SQL statement.
MySQL does set thd->in_lock_tables TRUE there, but in reality MySQL does set thd->in_lock_tables TRUE there, but in reality
...@@ -6876,26 +6880,36 @@ ha_innobase::store_lock( ...@@ -6876,26 +6880,36 @@ ha_innobase::store_lock(
lock_type = TL_READ_NO_INSERT; lock_type = TL_READ_NO_INSERT;
} }
/* If we are not doing a LOCK TABLE or DISCARD/IMPORT /* If we are not doing a LOCK TABLE, DISCARD/IMPORT
TABLESPACE or TRUNCATE TABLE, then allow multiple writers */ TABLESPACE or TRUNCATE TABLE then allow multiple
writers. Note that ALTER TABLE uses a TL_WRITE_ALLOW_READ
< TL_WRITE_CONCURRENT_INSERT.
We especially allow multiple writers if MySQL is at the
start of a stored procedure call (SQLCOM_CALL)
(MySQL does have thd->in_lock_tables TRUE there). */
if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && if ((lock_type >= TL_WRITE_CONCURRENT_INSERT
lock_type <= TL_WRITE) && lock_type <= TL_WRITE)
&& (!thd->in_lock_tables && (!thd->in_lock_tables
|| thd->lex->sql_command == SQLCOM_CALL) || thd->lex->sql_command == SQLCOM_CALL)
&& !thd->tablespace_op && !thd->tablespace_op
&& thd->lex->sql_command != SQLCOM_TRUNCATE && thd->lex->sql_command != SQLCOM_TRUNCATE
&& thd->lex->sql_command != SQLCOM_OPTIMIZE && thd->lex->sql_command != SQLCOM_OPTIMIZE
&& thd->lex->sql_command != SQLCOM_CREATE_TABLE) { && thd->lex->sql_command != SQLCOM_CREATE_TABLE) {
lock_type = TL_WRITE_ALLOW_WRITE; lock_type = TL_WRITE_ALLOW_WRITE;
} }
/* In queries of type INSERT INTO t1 SELECT ... FROM t2 ... /* In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
MySQL would use the lock TL_READ_NO_INSERT on t2, and that MySQL would use the lock TL_READ_NO_INSERT on t2, and that
would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
to t2. Convert the lock to a normal read lock to allow to t2. Convert the lock to a normal read lock to allow
concurrent inserts to t2. */ concurrent inserts to t2.
We especially allow concurrent inserts if MySQL is at the
start of a stored procedure call (SQLCOM_CALL)
(MySQL does have thd->in_lock_tables TRUE there). */
if (lock_type == TL_READ_NO_INSERT if (lock_type == TL_READ_NO_INSERT
&& (!thd->in_lock_tables && (!thd->in_lock_tables
...@@ -6904,10 +6918,10 @@ ha_innobase::store_lock( ...@@ -6904,10 +6918,10 @@ ha_innobase::store_lock(
lock_type = TL_READ; lock_type = TL_READ;
} }
lock.type = lock_type; lock.type = lock_type;
} }
*to++= &lock; *to++= &lock;
return(to); return(to);
} }
......
...@@ -3180,3 +3180,61 @@ a hex(b) ...@@ -3180,3 +3180,61 @@ a hex(b)
7 D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B2 7 D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B2
update t1 set b = 'three' where a = 6; update t1 set b = 'three' where a = 6;
drop table t1; drop table t1;
create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
insert into t1(a) values (1),(2),(3);
commit;
set autocommit = 0;
update t1 set b = 5 where a = 2;
create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end |
set autocommit = 0;
insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100),
(11),(21),(31),(41),(51),(61),(71),(81),(91),(101),
(12),(22),(32),(42),(52),(62),(72),(82),(92),(102),
(13),(23),(33),(43),(53),(63),(73),(83),(93),(103),
(14),(24),(34),(44),(54),(64),(74),(84),(94),(104);
commit;
commit;
drop trigger t1t;
drop table t1;
create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
create table t2(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
create table t3(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
create table t4(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
create table t5(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
insert into t1(a) values (1),(2),(3);
insert into t2(a) values (1),(2),(3);
insert into t3(a) values (1),(2),(3);
insert into t4(a) values (1),(2),(3);
insert into t3(a) values (5),(7),(8);
insert into t4(a) values (5),(7),(8);
insert into t5(a) values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12);
create trigger t1t before insert on t1 for each row begin
INSERT INTO t2 SET a = NEW.a;
end |
create trigger t2t before insert on t2 for each row begin
DELETE FROM t3 WHERE a = NEW.a;
end |
create trigger t3t before delete on t3 for each row begin
UPDATE t4 SET b = b + 1 WHERE a = OLD.a;
end |
create trigger t4t before update on t4 for each row begin
UPDATE t5 SET b = b + 1 where a = NEW.a;
end |
commit;
set autocommit = 0;
update t1 set b = b + 5 where a = 1;
update t2 set b = b + 5 where a = 1;
update t3 set b = b + 5 where a = 1;
update t4 set b = b + 5 where a = 1;
insert into t5(a) values(20);
set autocommit = 0;
insert into t1(a) values(7);
insert into t2(a) values(8);
delete from t2 where a = 3;
update t4 set b = b + 1 where a = 3;
commit;
drop trigger t1t;
drop trigger t2t;
drop trigger t3t;
drop trigger t4t;
drop table t1, t2, t3, t4, t5;
...@@ -2047,3 +2047,93 @@ insert into t1 values(7,_utf8 0xD0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1 ...@@ -2047,3 +2047,93 @@ insert into t1 values(7,_utf8 0xD0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1D0B1
select a,hex(b) from t1 order by b; select a,hex(b) from t1 order by b;
update t1 set b = 'three' where a = 6; update t1 set b = 'three' where a = 6;
drop table t1; drop table t1;
#
# Test case for bug #16229: MySQL/InnoDB uses full explicit table locks in trigger processing
#
connect (a,localhost,root,,);
connect (b,localhost,root,,);
connection a;
create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
insert into t1(a) values (1),(2),(3);
commit;
connection b;
set autocommit = 0;
update t1 set b = 5 where a = 2;
connection a;
delimiter |;
create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end |
delimiter ;|
set autocommit = 0;
connection a;
insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100),
(11),(21),(31),(41),(51),(61),(71),(81),(91),(101),
(12),(22),(32),(42),(52),(62),(72),(82),(92),(102),
(13),(23),(33),(43),(53),(63),(73),(83),(93),(103),
(14),(24),(34),(44),(54),(64),(74),(84),(94),(104);
connection b;
commit;
connection a;
commit;
drop trigger t1t;
drop table t1;
disconnect a;
disconnect b;
#
# Another trigger test
#
connect (a,localhost,root,,);
connect (b,localhost,root,,);
connection a;
create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
create table t2(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
create table t3(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
create table t4(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
create table t5(a int not null, b int, c int, d int, primary key(a)) engine=innodb;
insert into t1(a) values (1),(2),(3);
insert into t2(a) values (1),(2),(3);
insert into t3(a) values (1),(2),(3);
insert into t4(a) values (1),(2),(3);
insert into t3(a) values (5),(7),(8);
insert into t4(a) values (5),(7),(8);
insert into t5(a) values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12);
delimiter |;
create trigger t1t before insert on t1 for each row begin
INSERT INTO t2 SET a = NEW.a;
end |
create trigger t2t before insert on t2 for each row begin
DELETE FROM t3 WHERE a = NEW.a;
end |
create trigger t3t before delete on t3 for each row begin
UPDATE t4 SET b = b + 1 WHERE a = OLD.a;
end |
create trigger t4t before update on t4 for each row begin
UPDATE t5 SET b = b + 1 where a = NEW.a;
end |
delimiter ;|
commit;
set autocommit = 0;
update t1 set b = b + 5 where a = 1;
update t2 set b = b + 5 where a = 1;
update t3 set b = b + 5 where a = 1;
update t4 set b = b + 5 where a = 1;
insert into t5(a) values(20);
connection b;
set autocommit = 0;
insert into t1(a) values(7);
insert into t2(a) values(8);
delete from t2 where a = 3;
update t4 set b = b + 1 where a = 3;
commit;
drop trigger t1t;
drop trigger t2t;
drop trigger t3t;
drop trigger t4t;
drop table t1, t2, t3, t4, t5;
disconnect a;
disconnect b;
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