1# 2# Test how do we handle locking in various cases when 3# we read data from InnoDB tables. 4# 5# In fact by performing this test we check two things: 6# 1) That SQL-layer correctly determine type of thr_lock.c 7# lock to be acquired/passed to InnoDB engine. 8# 2) That InnoDB engine correctly interprets this lock 9# type and takes necessary row locks or does not 10# take them if they are not necessary. 11# 12# This test makes sense only in REPEATABLE-READ mode as 13# in SERIALIZABLE mode all statements that read data take 14# shared lock on them to enforce its semantics. 15select @@session.tx_isolation; 16@@session.tx_isolation 17REPEATABLE-READ 18# Prepare playground by creating tables, views, 19# routines and triggers used in tests. 20drop table if exists t0, t1, t2, t3, t4, t5; 21drop view if exists v1, v2; 22drop procedure if exists p1; 23drop procedure if exists p2; 24drop function if exists f1; 25drop function if exists f2; 26drop function if exists f3; 27drop function if exists f4; 28drop function if exists f5; 29drop function if exists f6; 30drop function if exists f7; 31drop function if exists f8; 32drop function if exists f9; 33drop function if exists f10; 34drop function if exists f11; 35drop function if exists f12; 36drop function if exists f13; 37drop function if exists f14; 38drop function if exists f15; 39create table t1 (i int primary key) engine=innodb; 40insert into t1 values (1), (2), (3), (4), (5); 41create table t2 (j int primary key) engine=innodb; 42insert into t2 values (1), (2), (3), (4), (5); 43create table t3 (k int primary key) engine=innodb; 44insert into t3 values (1), (2), (3); 45create table t4 (l int primary key) engine=innodb; 46insert into t4 values (1); 47create table t5 (l int primary key) engine=innodb; 48insert into t5 values (1); 49create view v1 as select i from t1; 50create view v2 as select j from t2 where j in (select i from t1); 51create procedure p1(k int) insert into t2 values (k); 52create function f1() returns int 53begin 54declare j int; 55select i from t1 where i = 1 into j; 56return j; 57end| 58create function f2() returns int 59begin 60declare k int; 61select i from t1 where i = 1 into k; 62insert into t2 values (k + 5); 63return 0; 64end| 65create function f3() returns int 66begin 67return (select i from t1 where i = 3); 68end| 69create function f4() returns int 70begin 71if (select i from t1 where i = 3) then 72return 1; 73else 74return 0; 75end if; 76end| 77create function f5() returns int 78begin 79insert into t2 values ((select i from t1 where i = 1) + 5); 80return 0; 81end| 82create function f6() returns int 83begin 84declare k int; 85select i from v1 where i = 1 into k; 86return k; 87end| 88create function f7() returns int 89begin 90declare k int; 91select j from v2 where j = 1 into k; 92return k; 93end| 94create function f8() returns int 95begin 96declare k int; 97select i from v1 where i = 1 into k; 98insert into t2 values (k+5); 99return k; 100end| 101create function f9() returns int 102begin 103update v2 set j=j+10 where j=1; 104return 1; 105end| 106create function f10() returns int 107begin 108return f1(); 109end| 110create function f11() returns int 111begin 112declare k int; 113set k= f1(); 114insert into t2 values (k+5); 115return k; 116end| 117create function f12(p int) returns int 118begin 119insert into t2 values (p); 120return p; 121end| 122create function f13(p int) returns int 123begin 124return p; 125end| 126create procedure p2(inout p int) 127begin 128select i from t1 where i = 1 into p; 129end| 130create function f14() returns int 131begin 132declare k int; 133call p2(k); 134insert into t2 values (k+5); 135return k; 136end| 137create function f15() returns int 138begin 139declare k int; 140call p2(k); 141return k; 142end| 143create trigger t4_bi before insert on t4 for each row 144begin 145declare k int; 146select i from t1 where i=1 into k; 147set new.l= k+1; 148end| 149create trigger t4_bu before update on t4 for each row 150begin 151if (select i from t1 where i=1) then 152set new.l= 2; 153end if; 154end| 155create trigger t4_bd before delete on t4 for each row 156begin 157if !(select i from v1 where i=1) then 158signal sqlstate '45000'; 159end if; 160end| 161create trigger t5_bi before insert on t5 for each row 162begin 163set new.l= f1()+1; 164end| 165create trigger t5_bu before update on t5 for each row 166begin 167declare j int; 168call p2(j); 169set new.l= j + 1; 170end| 171# 172# Set common variables to be used by scripts called below. 173# 174# 175# 1. Statements that read tables and do not use subqueries. 176# 177# 178# 1.1 Simple SELECT statement. 179# 180# No locks are necessary as this statement won't be written 181# to the binary log and InnoDB supports snapshots. 182Success: 'select * from t1' doesn't take row locks on 't1'. 183# 184# 1.2 Multi-UPDATE statement. 185# 186# Has to take shared locks on rows in the table being read as this 187# statement will be written to the binary log and therefore should 188# be serialized with concurrent statements. 189Success: 'update t2, t1 set j= j - 1 where i = j' takes shared row locks on 't1'. 190# 191# 1.3 Multi-DELETE statement. 192# 193# The above is true for this statement as well. 194Success: 'delete t2 from t1, t2 where i = j' takes shared row locks on 't1'. 195# 196# 1.4 DESCRIBE statement. 197# 198# This statement does not really read data from the 199# target table and thus does not take any lock on it. 200# We check this for completeness of coverage. 201Success: 'describe t1' doesn't take row locks on 't1'. 202# 203# 1.5 SHOW statements. 204# 205# The above is true for SHOW statements as well. 206Success: 'show create table t1' doesn't take row locks on 't1'. 207Success: 'show keys from t1' doesn't take row locks on 't1'. 208# 209# 2. Statements which read tables through subqueries. 210# 211# 212# 2.1 CALL with a subquery. 213# 214# A strong lock is not necessary as this statement is not 215# written to the binary log as a whole (it is written 216# statement-by-statement) and thanks to MVCC we can always get 217# versions of rows prior to the update that has locked them. 218# But in practice InnoDB does locking reads for all statements 219# other than SELECT (unless it is a READ-COMITTED mode or 220# innodb_locks_unsafe_for_binlog is ON). 221Success: 'call p1((select i + 5 from t1 where i = 1))' takes shared row locks on 't1'. 222# 223# 2.2 CREATE TABLE with a subquery. 224# 225# Has to take shared locks on rows in the table being read as 226# this statement is written to the binary log and therefore 227# should be serialized with concurrent statements. 228Success: 'create table t0 engine=innodb select * from t1' takes shared row locks on 't1'. 229drop table t0; 230Success: 'create table t0 engine=innodb select j from t2 where j in (select i from t1)' takes shared row locks on 't1'. 231drop table t0; 232# 233# 2.3 DELETE with a subquery. 234# 235# The above is true for this statement as well. 236Success: 'delete from t2 where j in (select i from t1)' takes shared row locks on 't1'. 237# 238# 2.4 MULTI-DELETE with a subquery. 239# 240# Same is true for this statement as well. 241Success: 'delete t2 from t3, t2 where k = j and j in (select i from t1)' takes shared row locks on 't1'. 242# 243# 2.5 DO with a subquery. 244# 245# In theory should not take row locks as it is not logged. 246# In practice InnoDB takes shared row locks. 247Success: 'do (select i from t1 where i = 1)' takes shared row locks on 't1'. 248# 249# 2.6 INSERT with a subquery. 250# 251# Has to take shared locks on rows in the table being read as 252# this statement is written to the binary log and therefore 253# should be serialized with concurrent statements. 254Success: 'insert into t2 select i+5 from t1' takes shared row locks on 't1'. 255Success: 'insert into t2 values ((select i+5 from t1 where i = 4))' takes shared row locks on 't1'. 256# 257# 2.7 LOAD DATA with a subquery. 258# 259# The above is true for this statement as well. 260Success: 'load data infile '../../std_data/rpl_loaddata.dat' into table t2 (@a, @b) set j= @b + (select i from t1 where i = 1)' takes shared row locks on 't1'. 261# 262# 2.8 REPLACE with a subquery. 263# 264# Same is true for this statement as well. 265Success: 'replace into t2 select i+5 from t1' takes shared row locks on 't1'. 266Success: 'replace into t2 values ((select i+5 from t1 where i = 4))' takes shared row locks on 't1'. 267# 268# 2.9 SELECT with a subquery. 269# 270# Locks are not necessary as this statement is not written 271# to the binary log and thanks to MVCC we can always get 272# versions of rows prior to the update that has locked them. 273# 274# Also serves as a test case for bug #46947 "Embedded SELECT 275# without FOR UPDATE is causing a lock". 276Success: 'select * from t2 where j in (select i from t1)' doesn't take row locks on 't1'. 277# 278# 2.10 SET with a subquery. 279# 280# In theory should not require locking as it is not written 281# to the binary log. In practice InnoDB acquires shared row 282# locks. 283Success: 'set @a:= (select i from t1 where i = 1)' takes shared row locks on 't1'. 284# 285# 2.11 SHOW with a subquery. 286# 287# Similarly to the previous case, in theory should not require locking 288# as it is not written to the binary log. In practice InnoDB 289# acquires shared row locks. 290Success: 'show tables from test where Tables_in_test = 't2' and (select i from t1 where i = 1)' takes shared row locks on 't1'. 291Success: 'show columns from t2 where (select i from t1 where i = 1)' takes shared row locks on 't1'. 292# 293# 2.12 UPDATE with a subquery. 294# 295# Has to take shared locks on rows in the table being read as 296# this statement is written to the binary log and therefore 297# should be serialized with concurrent statements. 298Success: 'update t2 set j= j-10 where j in (select i from t1)' takes shared row locks on 't1'. 299# 300# 2.13 MULTI-UPDATE with a subquery. 301# 302# Same is true for this statement as well. 303Success: 'update t2, t3 set j= j -10 where j=k and j in (select i from t1)' takes shared row locks on 't1'. 304# 305# 3. Statements which read tables through a view. 306# 307# 308# 3.1 SELECT statement which uses some table through a view. 309# 310# Since this statement is not written to the binary log 311# and old version of rows are accessible thanks to MVCC, 312# no locking is necessary. 313Success: 'select * from v1' doesn't take row locks on 't1'. 314Success: 'select * from v2' doesn't take row locks on 't1'. 315Success: 'select * from t2 where j in (select i from v1)' doesn't take row locks on 't1'. 316Success: 'select * from t3 where k in (select j from v2)' doesn't take row locks on 't1'. 317# 318# 3.2 Statements which modify a table and use views. 319# 320# Since such statements are going to be written to the binary 321# log they need to be serialized against concurrent statements 322# and therefore should take shared row locks on data read. 323Success: 'update t2 set j= j-10 where j in (select i from v1)' takes shared row locks on 't1'. 324Success: 'update t3 set k= k-10 where k in (select j from v2)' takes shared row locks on 't1'. 325Success: 'update t2, v1 set j= j-10 where j = i' takes shared row locks on 't1'. 326Success: 'update v2 set j= j-10 where j = 3' takes shared row locks on 't1'. 327# 328# 4. Statements which read tables through stored functions. 329# 330# 331# 4.1 SELECT/SET with a stored function which does not 332# modify data and uses SELECT in its turn. 333# 334# There is no need to take row locks on the table 335# being selected from in SF as the call to such function 336# won't get into the binary log. 337# 338# However in practice innodb takes strong lock on tables 339# being selected from within SF, when SF is called from 340# non SELECT statements like 'set' statement below. 341Success: 'select f1()' doesn't take row locks on 't1'. 342Success: 'set @a:= f1()' takes shared row locks on 't1'. 343# 344# 4.2 INSERT (or other statement which modifies data) with 345# a stored function which does not modify data and uses 346# SELECT. 347# 348# Since such statement is written to the binary log it should 349# be serialized with concurrent statements affecting the data 350# it uses. Therefore it should take row locks on the data 351# it reads. 352Success: 'insert into t2 values (f1() + 5)' takes shared row locks on 't1'. 353# 354# 4.3 SELECT/SET with a stored function which 355# reads and modifies data. 356# 357# Since a call to such function is written to the binary log, 358# it should be serialized with concurrent statements affecting 359# the data it uses. Hence, row locks on the data read 360# should be taken. 361Success: 'select f2()' takes shared row locks on 't1'. 362Success: 'set @a:= f2()' takes shared row locks on 't1'. 363# 364# 4.4. SELECT/SET with a stored function which does not 365# modify data and reads a table through subselect 366# in a control construct. 367# 368# Call to this function won't get to the 369# binary log and thus no locking is needed. 370# 371# However in practice innodb takes strong lock on tables 372# being selected from within SF, when SF is called from 373# non SELECT statements like 'set' statement below. 374Success: 'select f3()' doesn't take row locks on 't1'. 375Success: 'set @a:= f3()' takes shared row locks on 't1'. 376Success: 'select f4()' doesn't take row locks on 't1'. 377Success: 'set @a:= f4()' takes shared row locks on 't1'. 378# 379# 4.5. INSERT (or other statement which modifies data) with 380# a stored function which does not modify data and reads 381# the table through a subselect in one of its control 382# constructs. 383# 384# Since such statement is written to the binary log it should 385# be serialized with concurrent statements affecting data it 386# uses. Therefore it should take row locks on the data 387# it reads. 388Success: 'insert into t2 values (f3() + 5)' takes shared row locks on 't1'. 389Success: 'insert into t2 values (f4() + 6)' takes shared row locks on 't1'. 390# 391# 4.6 SELECT/SET which uses a stored function with 392# DML which reads a table via a subquery. 393# 394# Since call to such function is written to the binary log 395# it should be serialized with concurrent statements. 396# Hence reads should take row locks. 397Success: 'select f5()' takes shared row locks on 't1'. 398Success: 'set @a:= f5()' takes shared row locks on 't1'. 399# 400# 4.7 SELECT/SET which uses a stored function which 401# doesn't modify data and reads tables through 402# a view. 403# 404# Calls to such functions won't get into 405# the binary log and thus don't need row locks. 406# 407# However in practice innodb takes strong lock on tables 408# being selected from within SF, when SF is called from 409# non SELECT statements like 'set' statement below. 410Success: 'select f6()' doesn't take row locks on 't1'. 411Success: 'set @a:= f6()' takes shared row locks on 't1'. 412Success: 'select f7()' doesn't take row locks on 't1'. 413Success: 'set @a:= f7()' takes shared row locks on 't1'. 414# 415# 4.8 INSERT which uses stored function which 416# doesn't modify data and reads a table 417# through a view. 418# 419# Since such statement is written to the binary log and 420# should be serialized with concurrent statements affecting 421# the data it uses. Therefore it should take row locks on 422# the rows it reads. 423Success: 'insert into t3 values (f6() + 5)' takes shared row locks on 't1'. 424Success: 'insert into t3 values (f7() + 5)' takes shared row locks on 't1'. 425# 426# 4.9 SELECT which uses a stored function which 427# modifies data and reads tables through a view. 428# 429# Since a call to such function is written to the binary log 430# it should be serialized with concurrent statements. 431# Hence, reads should take row locks. 432Success: 'select f8()' takes shared row locks on 't1'. 433Success: 'select f9()' takes shared row locks on 't1'. 434# 435# 4.10 SELECT which uses stored function which doesn't modify 436# data and reads a table indirectly, by calling another 437# function. 438# 439# Calls to such functions won't get into the binary 440# log and thus don't need to acquire row locks. 441Success: 'select f10()' doesn't take row locks on 't1'. 442# 443# 4.11 INSERT which uses a stored function which doesn't modify 444# data and reads a table indirectly, by calling another 445# function. 446# 447# Since such statement is written to the binary log, it should 448# be serialized with concurrent statements affecting the data it 449# uses. Therefore it should take row locks on data it reads. 450Success: 'insert into t2 values (f10() + 5)' takes shared row locks on 't1'. 451# 452# 4.12 SELECT which uses a stored function which modifies 453# data and reads a table indirectly, by calling another 454# function. 455# 456# Since a call to such function is written to the binary log 457# it should be serialized from concurrent statements. 458# Hence, reads should take row locks. 459Success: 'select f11()' takes shared row locks on 't1'. 460# 461# 4.13 SELECT that reads a table through a subquery passed 462# as a parameter to a stored function which modifies 463# data. 464# 465# Even though a call to this function is written to the 466# binary log, values of its parameters are written as literals. 467# So there is no need to acquire row locks on rows used in 468# the subquery. 469Success: 'select f12((select i+10 from t1 where i=1))' doesn't take row locks on 't1'. 470# 471# 4.14 INSERT that reads a table via a subquery passed 472# as a parameter to a stored function which doesn't 473# modify data. 474# 475# Since this statement is written to the binary log it should 476# be serialized with concurrent statements affecting the data it 477# uses. Therefore it should take row locks on the data it reads. 478Success: 'insert into t2 values (f13((select i+10 from t1 where i=1)))' takes shared row locks on 't1'. 479# 480# 5. Statements that read tables through stored procedures. 481# 482# 483# 5.1 CALL statement which reads a table via SELECT. 484# 485# Since neither this statement nor its components are 486# written to the binary log, there is no need to take 487# row locks on the data it reads. 488Success: 'call p2(@a)' doesn't take row locks on 't1'. 489# 490# 5.2 Function that modifies data and uses CALL, 491# which reads a table through SELECT. 492# 493# Since a call to such function is written to the binary 494# log, it should be serialized with concurrent statements. 495# Hence, in this case reads should take row locks on data. 496Success: 'select f14()' takes shared row locks on 't1'. 497# 498# 5.3 SELECT that calls a function that doesn't modify data and 499# uses a CALL statement that reads a table via SELECT. 500# 501# Calls to such functions won't get into the binary 502# log and thus don't need to acquire row locks. 503Success: 'select f15()' doesn't take row locks on 't1'. 504# 505# 5.4 INSERT which calls function which doesn't modify data and 506# uses CALL statement which reads table through SELECT. 507# 508# Since such statement is written to the binary log it should 509# be serialized with concurrent statements affecting data it 510# uses. Therefore it should take row locks on data it reads. 511Success: 'insert into t2 values (f15()+5)' takes shared row locks on 't1'. 512# 513# 6. Statements that use triggers. 514# 515# 516# 6.1 Statement invoking a trigger that reads table via SELECT. 517# 518# Since this statement is written to the binary log it should 519# be serialized with concurrent statements affecting the data 520# it uses. Therefore, it should take row locks on the data 521# it reads. 522Success: 'insert into t4 values (2)' takes shared row locks on 't1'. 523# 524# 6.2 Statement invoking a trigger that reads table through 525# a subquery in a control construct. 526# 527# The above is true for this statement as well. 528Success: 'update t4 set l= 2 where l = 1' takes shared row locks on 't1'. 529# 530# 6.3 Statement invoking a trigger that reads a table through 531# a view. 532# 533# And for this statement. 534Success: 'delete from t4 where l = 1' takes shared row locks on 't1'. 535# 536# 6.4 Statement invoking a trigger that reads a table through 537# a stored function. 538# 539# And for this statement. 540Success: 'insert into t5 values (2)' takes shared row locks on 't1'. 541# 542# 6.5 Statement invoking a trigger that reads a table through 543# stored procedure. 544# 545# And for this statement. 546Success: 'update t5 set l= 2 where l = 1' takes shared row locks on 't1'. 547# Clean-up. 548drop function f1; 549drop function f2; 550drop function f3; 551drop function f4; 552drop function f5; 553drop function f6; 554drop function f7; 555drop function f8; 556drop function f9; 557drop function f10; 558drop function f11; 559drop function f12; 560drop function f13; 561drop function f14; 562drop function f15; 563drop view v1, v2; 564drop procedure p1; 565drop procedure p2; 566drop table t1, t2, t3, t4, t5; 567# 568# Test for bug#51263 "Deadlock between transactional SELECT 569# and ALTER TABLE ... REBUILD PARTITION". 570# 571drop table if exists t1, t2; 572create table t1 (i int auto_increment not null primary key) engine=innodb; 573create table t2 (i int) engine=innodb; 574insert into t1 values (1), (2), (3), (4), (5); 575begin; 576# Acquire SR metadata lock on t1 and LOCK_S row-locks on its rows. 577insert into t2 select count(*) from t1; 578# Switching to connection 'con1'. 579# Sending: 580alter table t1 add column j int; 581# Switching to connection 'default'. 582# Wait until ALTER is blocked because it tries to upgrade SNW 583# metadata lock to X lock. 584# It should not be blocked during copying data to new version of 585# table as it acquires LOCK_S locks on rows of old version, which 586# are compatible with locks acquired by connection 'con1'. 587# The below statement will deadlock because it will try to acquire 588# SW lock on t1, which will conflict with ALTER's SNW lock. And 589# ALTER will be waiting for this connection to release its SR lock. 590# This deadlock should be detected by an MDL subsystem and this 591# statement should be aborted with an appropriate error. 592insert into t1 values (6); 593ERROR 40001: Deadlock found when trying to get lock; try restarting transaction 594# Unblock ALTER TABLE. 595commit; 596# Switching to connection 'con1'. 597# Reaping ALTER TABLE. 598# Switching to connection 'default'. 599# 600# Now test for scenario in which bug was reported originally. 601# 602drop tables t1, t2; 603create table t1 (i int auto_increment not null primary key) engine=innodb 604partition by hash (i) partitions 4; 605create table t2 (i int) engine=innodb; 606insert into t1 values (1), (2), (3), (4), (5); 607begin; 608# Acquire SR metadata lock on t1. 609select * from t1; 610i 6114 6121 6135 6142 6153 616# Switching to connection 'con1'. 617# Sending: 618alter table t1 rebuild partition p0; 619# Switching to connection 'default'. 620# Wait until ALTER is blocked because of active SR lock. 621# The below statement should succeed as transaction 622# has SR metadata lock on t1 and only going to read 623# rows from it. 624insert into t2 select count(*) from t1; 625# Unblock ALTER TABLE. 626commit; 627# Switching to connection 'con1'. 628# Reaping ALTER TABLE. 629# Switching to connection 'default'. 630# Clean-up. 631drop tables t1, t2; 632