1#
2# Test coverage for changes performed by the fix
3# for Bug#30977 "Concurrent statement using stored function
4# and DROP FUNCTION breaks SBR.
5#
6#
7# 1) Verify that the preceding transaction is
8# (implicitly) committed  before CREATE/ALTER/DROP
9# PROCEDURE. Note, that this is already tested
10# in implicit_commit.test, but here we use an alternative
11# approach.
12#
13# Start a transaction, create a savepoint,
14# then call a DDL operation on a procedure, and then check
15# that the savepoint is no longer present.
16drop table if exists t1;
17drop procedure if exists p1;
18drop procedure if exists p2;
19drop procedure if exists p3;
20drop procedure if exists p4;
21drop function if exists f1;
22create table t1 (a int);
23#
24# Test 'CREATE PROCEDURE'.
25#
26begin;
27savepoint sv;
28create procedure p1() begin end;
29rollback to savepoint sv;
30ERROR 42000: SAVEPOINT sv does not exist
31#
32# Test 'ALTER PROCEDURE'.
33#
34begin;
35savepoint sv;
36alter procedure p1 comment 'changed comment';
37rollback to savepoint sv;
38ERROR 42000: SAVEPOINT sv does not exist
39#
40# Test 'DROP PROCEDURE'.
41#
42begin;
43savepoint sv;
44drop procedure p1;
45rollback to savepoint sv;
46ERROR 42000: SAVEPOINT sv does not exist
47#
48# Test 'CREATE FUNCTION'.
49#
50begin;
51savepoint sv;
52create function f1() returns int return 1;
53rollback to savepoint sv;
54ERROR 42000: SAVEPOINT sv does not exist
55#
56# Test 'ALTER FUNCTION'.
57#
58begin;
59savepoint sv;
60alter function f1 comment 'new comment';
61rollback to savepoint sv;
62ERROR 42000: SAVEPOINT sv does not exist
63#
64# Test 'DROP FUNCTION'.
65#
66begin;
67savepoint sv;
68drop function f1;
69rollback to savepoint sv;
70ERROR 42000: SAVEPOINT sv does not exist
71#
72# 2) Verify that procedure DDL operations fail
73# under lock tables.
74#
75# Auxiliary routines to test ALTER.
76create procedure p1() begin end;
77create function f1() returns int return 1;
78lock table t1 write;
79create procedure p2() begin end;
80ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
81alter procedure p1 comment 'changed comment';
82ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
83drop procedure p1;
84ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
85create function f2() returns int return 1;
86ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
87alter function f1 comment 'changed comment';
88ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
89lock table t1 read;
90create procedure p2() begin end;
91ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
92alter procedure p1 comment 'changed comment';
93ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
94drop procedure p1;
95ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
96create function f2() returns int return 1;
97ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
98alter function f1 comment 'changed comment';
99ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
100unlock tables;
101#
102# Even if we locked a temporary table.
103# Todo: this is a restriction we could possibly lift.
104#
105drop table t1;
106create temporary table t1 (a int);
107lock table t1 read;
108create procedure p2() begin end;
109ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
110alter procedure p1 comment 'changed comment';
111ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
112drop procedure p1;
113ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
114create function f2() returns int return 1;
115ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
116alter function f1 comment 'changed comment';
117ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
118unlock tables;
119drop function f1;
120drop procedure p1;
121drop temporary table t1;
122#
123# 3) Verify that CREATE/ALTER/DROP routine grab an
124# exclusive lock.
125#
126# For that, start a transaction, use a routine. In a concurrent
127# connection, try to drop or alter the routine. It should place
128# a pending or exclusive lock and block. In another concurrnet
129# connection, try to use the routine.
130# That should block on the pending exclusive lock.
131#
132# Establish helper connections.
133#
134# Test DROP PROCEDURE.
135#
136# --> connection default
137create procedure p1() begin end;
138create function f1() returns int
139begin
140call p1();
141return 1;
142end|
143begin;
144select f1();
145f1()
1461
147# --> connection con1
148# Sending 'drop procedure p1'...
149drop procedure p1;
150# --> connection con2
151# Waiting for 'drop procedure t1' to get blocked on MDL lock...
152# Demonstrate that there is a pending exclusive lock.
153# Sending 'select f1()'...
154select f1();
155# --> connection con3
156# Waiting for 'select f1()' to get blocked by a pending MDL lock...
157# --> connection default
158commit;
159# --> connection con1
160# Reaping 'drop procedure p1'...
161# --> connection con2
162# Reaping 'select f1()'
163ERROR 42000: PROCEDURE test.p1 does not exist
164# --> connection default
165#
166# Test CREATE PROCEDURE.
167#
168create procedure p1() begin end;
169begin;
170select f1();
171f1()
1721
173# --> connection con1
174# Sending 'create procedure p1'...
175create procedure p1() begin end;
176# --> connection con2
177# Waiting for 'create procedure t1' to get blocked on MDL lock...
178# Demonstrate that there is a pending exclusive lock.
179# Sending 'select f1()'...
180select f1();
181# --> connection con3
182# Waiting for 'select f1()' to get blocked by a pending MDL lock...
183# --> connection default
184commit;
185# --> connection con1
186# Reaping 'create procedure p1'...
187ERROR 42000: PROCEDURE p1 already exists
188# --> connection con2
189# Reaping 'select f1()'
190f1()
1911
192#
193# Test ALTER PROCEDURE.
194#
195begin;
196select f1();
197f1()
1981
199# --> connection con1
200# Sending 'alter procedure p1'...
201alter procedure p1 contains sql;
202# --> connection con2
203# Waiting for 'alter procedure t1' to get blocked on MDL lock...
204# Demonstrate that there is a pending exclusive lock.
205# Sending 'select f1()'...
206select f1();
207# --> connection con3
208# Waiting for 'select f1()' to get blocked by a pending MDL lock...
209# --> connection default
210commit;
211# --> connection con1
212# Reaping 'alter procedure p1'...
213# --> connection con2
214# Reaping 'select f1()'
215f1()
2161
217# --> connection default
218#
219# Test DROP FUNCTION.
220#
221begin;
222select f1();
223f1()
2241
225# --> connection con1
226# Sending 'drop function f1'...
227drop function f1;
228# --> connection con2
229# Waiting for 'drop function f1' to get blocked on MDL lock...
230# Demonstrate that there is a pending exclusive lock.
231# Sending 'select f1()'...
232select f1();
233# --> connection con3
234# Waiting for 'select f1()' to get blocked by a pending MDL lock...
235# --> connection default
236commit;
237# --> connection con1
238# Reaping 'drop function f1'...
239# --> connection con2
240# Reaping 'select f1()'
241ERROR 42000: FUNCTION test.f1 does not exist
242# --> connection default
243#
244# Test CREATE FUNCTION.
245#
246create function f1() returns int return 1;
247begin;
248select f1();
249f1()
2501
251# --> connection con1
252# Sending 'create function f1'...
253create function f1() returns int return 2;
254# --> connection con2
255# Waiting for 'create function f1' to get blocked on MDL lock...
256# Demonstrate that there is a pending exclusive lock.
257# Sending 'select f1()'...
258select f1();
259# --> connection con3
260# Waiting for 'select f1()' to get blocked by a pending MDL lock...
261# --> connection default
262commit;
263# --> connection con1
264# Reaping 'create function f1'...
265ERROR 42000: FUNCTION f1 already exists
266# --> connection con2
267# Reaping 'select f1()'
268f1()
2691
270# --> connection default
271#
272# Test ALTER FUNCTION.
273#
274begin;
275select f1();
276f1()
2771
278# --> connection con1
279# Sending 'alter function f1'...
280alter function f1 contains sql;
281# --> connection con2
282# Waiting for 'alter function f1' to get blocked on MDL lock...
283# Demonstrate that there is a pending exclusive lock.
284# Sending 'select f1()'...
285select f1();
286# --> connection con3
287# Waiting for 'select f1()' to get blocked by a pending MDL lock...
288# --> connection default
289commit;
290# --> connection con1
291# Reaping 'alter function f1'...
292# --> connection con2
293# Reaping 'select f1()'
294f1()
2951
296# --> connection default
297drop function f1;
298drop procedure p1;
299#
300# 4) MDL lock should not be taken for
301# unrolled CALL statements.
302# The primary goal of metadata locks is a consistent binary log.
303# When a call statement is unrolled, it doesn't get to the
304# binary log, instead the statements that are contained
305# in the procedure body do. This can nest to any level.
306#
307create procedure p1() begin end;
308create procedure p2() begin end;
309create table t1 (a int);
310create procedure p3()
311begin
312call p1();
313call p1();
314call p2();
315end|
316create procedure p4()
317begin
318call p1();
319call p1();
320call p2();
321call p2();
322call p3();
323end|
324begin;
325select * from t1;
326a
327savepoint sv;
328call p4();
329# Prepared statement should not add any locks either.
330prepare stmt from "call p4()";
331execute stmt;
332execute stmt;
333# --> connection con1
334drop procedure p1;
335drop procedure p2;
336drop procedure p3;
337drop procedure p4;
338# --> connection default
339# This is to verify there was no implicit commit.
340rollback to savepoint sv;
341call p4();
342ERROR 42000: PROCEDURE test.p4 does not exist
343commit;
344drop table t1;
345#
346# 5) Locks should be taken on routines
347# used indirectly by views or triggers.
348#
349#
350# A function is used from a trigger.
351#
352create function f1() returns int return 1;
353create table t1 (a int);
354create table t2 (a int, b int);
355create trigger t1_ai after insert on t1 for each row
356insert into t2 (a, b) values (new.a, f1());
357begin;
358insert into t1 (a) values (1);
359# --> connection con1
360# Sending 'drop function f1'
361drop function f1;
362# --> connection con2
363# Waiting for 'drop function f1' to get blocked on MDL lock...
364# --> connnection default
365commit;
366# --> connection con1
367# Reaping 'drop function f1'...
368# --> connection default
369#
370# A function is used from a view.
371#
372create function f1() returns int return 1;
373create view v1 as select f1() as a;
374begin;
375select * from v1;
376a
3771
378# --> connection con1
379# Sending 'drop function f1'
380drop function f1;
381# --> connection con2
382# Waiting for 'drop function f1' to get blocked on MDL lock...
383# --> connnection default
384commit;
385# --> connection con1
386# Reaping 'drop function f1'...
387# --> connection default
388#
389# A procedure is used from a function.
390#
391create function f1() returns int
392begin
393declare v_out int;
394call p1(v_out);
395return v_out;
396end|
397create procedure p1(out v_out int) set v_out=3;
398begin;
399select * from v1;
400a
4013
402# --> connection con1
403# Sending 'drop procedure p1'
404drop procedure p1;
405# --> connection con2
406# Waiting for 'drop procedure p1' to get blocked on MDL lock...
407# --> connnection default
408commit;
409# --> connection con1
410# Reaping 'drop procedure p1'...
411# --> connection default
412#
413# Deep nesting: a function is used from a procedure used
414# from a function used from a view used in a trigger.
415#
416create function f2() returns int return 4;
417create procedure p1(out v_out int) set v_out=f2();
418drop trigger t1_ai;
419create trigger t1_ai after insert on t1 for each row
420insert into t2 (a, b) values (new.a, (select max(a) from v1));
421begin;
422insert into t1 (a) values (3);
423# --> connection con1
424# Sending 'drop function f2'
425drop function f2;
426# --> connection con2
427# Waiting for 'drop function f2' to get blocked on MDL lock...
428# --> connnection default
429commit;
430# --> connection con1
431# Reaping 'drop function f2'...
432# --> connection default
433drop view v1;
434drop function f1;
435drop procedure p1;
436drop table t1, t2;
437#
438# 6) Check that ER_LOCK_DEADLOCK is reported if
439# acquisition of a shared lock fails during a transaction or
440# we need to back off to flush the sp cache.
441#
442# Sic: now this situation does not require a back off since we
443# flush the cache on the fly.
444#
445create function f1() returns int return 7;
446create table t1 (a int);
447begin;
448select * from t1;
449a
450select f1();
451f1()
4527
453commit;
454drop table t1;
455drop function f1;
456#
457# 7) Demonstrate that under LOCK TABLES we accumulate locks
458# on stored routines, and release metadata locks in
459# ROLLBACK TO SAVEPOINT. That is done only for those stored
460# routines that are not part of LOCK TABLES prelocking list.
461# Those stored routines that are part of LOCK TABLES
462# prelocking list are implicitly locked when entering
463# LOCK TABLES, and ROLLBACK TO SAVEPOINT has no effect on
464# them.
465#
466create function f1() returns varchar(20) return "f1()";
467create function f2() returns varchar(20) return "f2()";
468create view v1 as select f1() as a;
469set @@session.autocommit=0;
470lock table v1 read;
471select * from v1;
472a
473f1()
474savepoint sv;
475select f2();
476f2()
477f2()
478# --> connection con1
479# Sending 'drop function f1'...
480drop function f1;
481# --> connection con2
482# Waiting for 'drop function f1' to get blocked on MDL lock...
483# Sending 'drop function f2'...
484drop function f2;
485# --> connection default
486# Waiting for 'drop function f2' to get blocked on MDL lock...
487rollback to savepoint sv;
488# --> connection con2
489# Reaping 'drop function f2'...
490# --> connection default
491unlock tables;
492# --> connection con1
493# Reaping 'drop function f1'...
494# --> connection default
495drop function f1;
496ERROR 42000: FUNCTION test.f1 does not exist
497drop function f2;
498ERROR 42000: FUNCTION test.f2 does not exist
499drop view v1;
500set @@session.autocommit=default;
501#
502# 8) Check the situation when we're preparing or executing a
503# prepared statement, and as part of that try to flush the
504# session sp cache. However, one of the procedures that
505# needs a flush is in use. Verify that there is no infinite
506# reprepare loop and no crash.
507#
508create function f1() returns int return 1;
509#
510# We just mention p1() in the body of f2() to make
511# sure that p1() metadata is validated when validating
512# 'select f2()'.
513# Recursion is not allowed in stored functions, so
514# an attempt to just invoke p1() from f2() which is in turn
515# called from p1() would have given a run-time error.
516#
517create function f2() returns int
518begin
519if @var is null then
520call p1();
521end if;
522return 1;
523end|
524create procedure p1()
525begin
526select f1() into @var;
527execute stmt;
528end|
529# --> connection con2
530prepare stmt from "select f2()";
531# --> connection default
532begin;
533select f1();
534f1()
5351
536# --> connection con1
537# Sending 'alter function f1 ...'...
538alter function f1 comment "comment";
539# --> connection con2
540# Waiting for 'alter function f1 ...' to get blocked on MDL lock...
541# Sending 'call p1()'...
542call p1();
543# Waiting for 'call p1()' to get blocked on MDL lock on f1...
544# Let 'alter function f1 ...' go through...
545commit;
546# --> connection con1
547# Reaping 'alter function f1 ...'
548# --> connection con2
549# Reaping 'call p1()'...
550f2()
5511
552deallocate prepare stmt;
553# --> connection default
554drop function f1;
555drop function f2;
556drop procedure p1;
557#
558# 9) Check the situation when a stored function is invoked
559# from a stored procedure, and recursively invokes the
560# stored procedure that is in use. But for the second
561# invocation, a cache flush is requested. We can't
562# flush the procedure that's in use, and are forced
563# to use an old version. It is not a violation of
564# consistency, since we unroll top-level calls.
565# Just verify the code works.
566#
567create function f1() returns int return 1;
568begin;
569select f1();
570f1()
5711
572# --> connection con1
573# Sending 'alter function f1 ...'...
574alter function f1 comment "comment";
575# --> connection con2
576# Waiting for 'alter function f1 ...' to get blocked on MDL lock...
577#
578# We just mention p1() in the body of f2() to make
579# sure that p1() is prelocked for f2().
580# Recursion is not allowed in stored functions, so
581# an attempt to just invoke p1() from f2() which is in turn
582# called from p1() would have given a run-time error.
583#
584create function f2() returns int
585begin
586if @var is null then
587call p1();
588end if;
589return 1;
590end|
591create procedure p1()
592begin
593select f1() into @var;
594select f2() into @var;
595end|
596# Sending 'call p1()'...
597call p1();
598# Waiting for 'call p1()' to get blocked on MDL lock on f1...
599# Let 'alter function f1 ...' go through...
600commit;
601# --> connection con1
602# Reaping 'alter function f1 ...'
603# --> connection con2
604# Reaping 'call p1()'...
605# --> connection default
606drop function f1;
607drop function f2;
608drop procedure p1;
609#
610# 10) A select from information_schema.routines now
611# flushes the stored routines caches. Test that this
612# does not remove from the cache a stored routine
613# that is already prelocked.
614#
615create function f1() returns int return get_lock("30977", 100000);
616create function f2() returns int return 2;
617create function f3() returns varchar(255)
618begin
619declare res varchar(255);
620declare c cursor for select routine_name from
621information_schema.routines where routine_name='f1';
622select f1() into @var;
623open c;
624fetch c into res;
625close c;
626select f2() into @var;
627return res;
628end|
629# --> connection con1
630select get_lock("30977", 0);
631get_lock("30977", 0)
6321
633# --> connection default
634# Sending 'select f3()'...
635select f3();
636# --> connection con1
637# Waiting for 'select f3()' to get blocked on the user level lock...
638# Do something to change the cache version.
639create function f4() returns int return  4;
640drop function f4;
641select release_lock("30977");
642release_lock("30977")
6431
644# --> connection default
645# Reaping 'select f3()'...
646# Routine 'f2()' should exist and get executed successfully.
647f3()
648f1
649select @var;
650@var
6512
652drop function f1;
653drop function f2;
654drop function f3;
655# 11) Check the situation when the connection is flushing the
656# SP cache which contains a procedure that is being executed.
657#
658# Function f1() calls p1(). Procedure p1() has a DROP
659# VIEW statement, which, we know, invalidates the routines cache.
660# During cache flush p1() must not be flushed since it's in
661# use.
662#
663create function f1() returns int
664begin
665call p1();
666return 1;
667end|
668create procedure p1()
669begin
670create view v1 as select 1;
671drop view v1;
672select f1() into @var;
673set @exec_count=@exec_count+1;
674end|
675set @exec_count=0;
676call p1();
677ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1
678select @exec_count;
679@exec_count
6800
681set @@session.max_sp_recursion_depth=5;
682set @exec_count=0;
683call p1();
684ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
685select @exec_count;
686@exec_count
6870
688drop procedure p1;
689drop function f1;
690set @@session.max_sp_recursion_depth=default;
691# --> connection con1
692# --> connection con2
693# --> connection con3
694# --> connection default
695#
696# SHOW CREATE PROCEDURE p1 called from p1, after p1 was altered
697#
698# We are just covering the existing behaviour with tests. The
699# results are not necessarily correct."
700#
701CREATE PROCEDURE p1()
702BEGIN
703SELECT get_lock("test", 10);
704SHOW CREATE PROCEDURE p1;
705END|
706# Connection default
707SELECT get_lock("test", 10);
708get_lock("test", 10)
7091
710# Connection 2
711# Will halt before executing SHOW CREATE PROCEDURE p1
712# Sending:
713CALL p1();
714# Connection 3
715# Alter p1
716DROP PROCEDURE p1;
717CREATE PROCEDURE p1() BEGIN END;
718# Connection default
719# Resume CALL p1, now with new p1
720SELECT release_lock("test");
721release_lock("test")
7221
723# Connection 2
724# Reaping: CALL p1()
725get_lock("test", 10)
7261
727Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
728p1	ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION	CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
729BEGIN
730SELECT get_lock("test", 10);
731SHOW CREATE PROCEDURE p1;
732END	latin1	latin1_swedish_ci	latin1_swedish_ci
733# Connection 3
734# Connection 2
735# Connection default;
736DROP PROCEDURE p1;
737#
738# Bug#57663 Concurrent statement using stored function and DROP DATABASE
739#           breaks SBR
740#
741DROP DATABASE IF EXISTS db1;
742DROP FUNCTION IF EXISTS f1;
743# Test 1: Check that DROP DATABASE block if a function is used
744#         by an active transaction.
745# Connection default
746CREATE DATABASE db1;
747CREATE FUNCTION db1.f1() RETURNS INTEGER RETURN 1;
748START TRANSACTION;
749SELECT db1.f1();
750db1.f1()
7511
752# Connection con1
753# Sending:
754DROP DATABASE db1;
755# Connection default
756# Waiting for DROP DATABASE to be blocked by the lock on f1()
757COMMIT;
758# Connection con1
759# Reaping: DROP DATABASE db1
760# Test 2: Check that DROP DATABASE blocks if a procedure is
761#         used by an active transaction.
762# Connection default
763CREATE DATABASE db1;
764CREATE PROCEDURE db1.p1() BEGIN END;
765CREATE FUNCTION f1() RETURNS INTEGER
766BEGIN
767CALL db1.p1();
768RETURN 1;
769END|
770START TRANSACTION;
771SELECT f1();
772f1()
7731
774# Connection con1
775# Sending:
776DROP DATABASE db1;
777# Connection default
778# Waiting for DROP DATABASE to be blocked by the lock on p1()
779COMMIT;
780# Connection con1
781# Reaping: DROP DATABASE db1
782# Test 3: Check that DROP DATABASE is not selected as a victim if a
783#         deadlock is discovered with DML statements.
784# Connection default
785CREATE DATABASE db1;
786CREATE TABLE db1.t1 (a INT);
787CREATE FUNCTION db1.f1() RETURNS INTEGER RETURN 1;
788START TRANSACTION;
789SELECT db1.f1();
790db1.f1()
7911
792# Connection con1
793# Sending:
794DROP DATABASE db1;
795# Connection default
796# Waiting for DROP DATABASE to be blocked by the lock on f1()
797SELECT * FROM db1.t1;
798ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
799COMMIT;
800# Connection con1
801# Reaping: DROP DATABASE db1
802# Test 4: Check that active DROP DATABASE blocks stored routine DDL.
803# Connection default
804CREATE DATABASE db1;
805CREATE FUNCTION db1.f1() RETURNS INTEGER RETURN 1;
806CREATE FUNCTION db1.f2() RETURNS INTEGER RETURN 2;
807START TRANSACTION;
808SELECT db1.f2();
809db1.f2()
8102
811# Connection con1
812# Sending:
813DROP DATABASE db1;
814# Connection con2
815# Waiting for DROP DATABASE to be blocked by the lock on f2()
816# Sending:
817ALTER FUNCTION db1.f1 COMMENT "test";
818# Connection default
819# Waiting for ALTER FUNCTION to be blocked by the schema lock on db1
820COMMIT;
821# Connection con1
822# Reaping: DROP DATABASE db1
823# Connection con2
824# Reaping: ALTER FUNCTION f1 COMMENT 'test'
825ERROR 42000: FUNCTION db1.f1 does not exist
826# Connection default
827DROP FUNCTION f1;
828#
829# End of 5.5 tests
830#
831