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