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