summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2014-07-31 12:03:20 +0200
committerSergei Golubchik <sergii@pisem.net>2014-07-31 12:03:20 +0200
commit8867a499f768b52d6ec2e774a1b6360e20ccafbe (patch)
tree888db5b21adec36f8811cff997234e8a8192b43b
parentde4a3c2a1dd82561a7ef53e3edd812265bd267b2 (diff)
downloadmariadb-git-8867a499f768b52d6ec2e774a1b6360e20ccafbe.tar.gz
MDEV-6050 MySQL Bug#13036505 62540: TABLE LOCKS WITHIN STORED FUNCTIONS ARE BACK IN 5.5 WITH MIXED AND ROW BI
cherry-pick revno 4053 committer: Gopal Shankar <gopal.shankar@oracle.com> branch nick: sf_mysql-5.6 timestamp: Fri 2012-07-20 12:25:34 +0530 message: Bug#13036505 62540: TABLE LOCKS WITHIN STORED FUNCTIONS ARE BACK IN 5.5 WITH MIXED AND ROW BI.
-rw-r--r--mysql-test/r/innodb_mysql_lock2.result55
-rw-r--r--mysql-test/r/lock_sync.result103
-rw-r--r--mysql-test/t/innodb_mysql_lock2.test55
-rw-r--r--mysql-test/t/lock_sync.test114
-rw-r--r--sql/sp_head.cc5
-rw-r--r--sql/sp_head.h32
-rw-r--r--sql/sql_base.cc107
-rw-r--r--sql/sql_base.h3
-rw-r--r--sql/sql_update.cc8
9 files changed, 321 insertions, 161 deletions
diff --git a/mysql-test/r/innodb_mysql_lock2.result b/mysql-test/r/innodb_mysql_lock2.result
index 17dd747de6f..54203c140a2 100644
--- a/mysql-test/r/innodb_mysql_lock2.result
+++ b/mysql-test/r/innodb_mysql_lock2.result
@@ -331,13 +331,14 @@ Success: 'update v2 set j= j-10 where j = 3' takes shared row locks on 't1'.
# 4.1 SELECT/SET with a stored function which does not
# modify data and uses SELECT in its turn.
#
-# In theory there is no need to take row locks on the table
+# There is no need to take row locks on the table
# being selected from in SF as the call to such function
-# won't get into the binary log. In practice, however, we
-# discover that fact too late in the process to be able to
-# affect the decision what locks should be taken.
-# Hence, strong locks are taken in this case.
-Success: 'select f1()' takes shared row locks on 't1'.
+# won't get into the binary log.
+#
+# However in practice innodb takes strong lock on tables
+# being selected from within SF, when SF is called from
+# non SELECT statements like 'set' statement below.
+Success: 'select f1()' doesn't take row locks on 't1'.
Success: 'set @a:= f1()' takes shared row locks on 't1'.
#
# 4.2 INSERT (or other statement which modifies data) with
@@ -364,13 +365,15 @@ Success: 'set @a:= f2()' takes shared row locks on 't1'.
# modify data and reads a table through subselect
# in a control construct.
#
-# Again, in theory a call to this function won't get to the
-# binary log and thus no locking is needed. But in practice
-# we don't detect this fact early enough (get_lock_type_for_table())
-# to avoid taking row locks.
-Success: 'select f3()' takes shared row locks on 't1'.
+# Call to this function won't get to the
+# binary log and thus no locking is needed.
+#
+# However in practice innodb takes strong lock on tables
+# being selected from within SF, when SF is called from
+# non SELECT statements like 'set' statement below.
+Success: 'select f3()' doesn't take row locks on 't1'.
Success: 'set @a:= f3()' takes shared row locks on 't1'.
-Success: 'select f4()' takes shared row locks on 't1'.
+Success: 'select f4()' doesn't take row locks on 't1'.
Success: 'set @a:= f4()' takes shared row locks on 't1'.
#
# 4.5. INSERT (or other statement which modifies data) with
@@ -398,13 +401,15 @@ Success: 'set @a:= f5()' takes shared row locks on 't1'.
# doesn't modify data and reads tables through
# a view.
#
-# Once again, in theory, calls to such functions won't
-# get into the binary log and thus don't need row
-# locks. But in practice this fact is discovered
-# too late to have any effect.
-Success: 'select f6()' takes shared row locks on 't1'.
+# Calls to such functions won't get into
+# the binary log and thus don't need row locks.
+#
+# However in practice innodb takes strong lock on tables
+# being selected from within SF, when SF is called from
+# non SELECT statements like 'set' statement below.
+Success: 'select f6()' doesn't take row locks on 't1'.
Success: 'set @a:= f6()' takes shared row locks on 't1'.
-Success: 'select f7()' takes shared row locks on 't1'.
+Success: 'select f7()' doesn't take row locks on 't1'.
Success: 'set @a:= f7()' takes shared row locks on 't1'.
#
# 4.8 INSERT which uses stored function which
@@ -431,10 +436,9 @@ Success: 'select f9()' takes shared row locks on 't1'.
# data and reads a table indirectly, by calling another
# function.
#
-# In theory, calls to such functions won't get into the binary
-# log and thus don't need to acquire row locks. But in practice
-# this fact is discovered too late to have any effect.
-Success: 'select f10()' takes shared row locks on 't1'.
+# Calls to such functions won't get into the binary
+# log and thus don't need to acquire row locks.
+Success: 'select f10()' doesn't take row locks on 't1'.
#
# 4.11 INSERT which uses a stored function which doesn't modify
# data and reads a table indirectly, by calling another
@@ -494,10 +498,9 @@ Success: 'select f14()' takes shared row locks on 't1'.
# 5.3 SELECT that calls a function that doesn't modify data and
# uses a CALL statement that reads a table via SELECT.
#
-# In theory, calls to such functions won't get into the binary
-# log and thus don't need to acquire row locks. But in practice
-# this fact is discovered too late to have any effect.
-Success: 'select f15()' takes shared row locks on 't1'.
+# Calls to such functions won't get into the binary
+# log and thus don't need to acquire row locks.
+Success: 'select f15()' doesn't take row locks on 't1'.
#
# 5.4 INSERT which calls function which doesn't modify data and
# uses CALL statement which reads table through SELECT.
diff --git a/mysql-test/r/lock_sync.result b/mysql-test/r/lock_sync.result
index 8fe94679e70..219cc08342e 100644
--- a/mysql-test/r/lock_sync.result
+++ b/mysql-test/r/lock_sync.result
@@ -27,6 +27,7 @@ drop table if exists t0, t1, t2, t3, t4, t5;
drop view if exists v1, v2;
drop procedure if exists p1;
drop procedure if exists p2;
+drop procedure if exists p3;
drop function if exists f1;
drop function if exists f2;
drop function if exists f3;
@@ -42,6 +43,8 @@ drop function if exists f12;
drop function if exists f13;
drop function if exists f14;
drop function if exists f15;
+drop function if exists f16;
+drop function if exists f17;
create table t1 (i int primary key);
insert into t1 values (1), (2), (3), (4), (5);
create table t2 (j int primary key);
@@ -146,6 +149,26 @@ declare k int;
call p2(k);
return k;
end|
+create function f16() returns int
+begin
+create temporary table if not exists temp1 (a int);
+insert into temp1 select * from t1;
+drop temporary table temp1;
+return 1;
+end|
+create function f17() returns int
+begin
+declare j int;
+select i from t1 where i = 1 into j;
+call p3;
+return 1;
+end|
+create procedure p3()
+begin
+create temporary table if not exists temp1 (a int);
+insert into temp1 select * from t1;
+drop temporary table temp1;
+end|
create trigger t4_bi before insert on t4 for each row
begin
declare k int;
@@ -185,6 +208,7 @@ end|
# once during its execution.
show create procedure p1;
show create procedure p2;
+show create procedure p3;
show create function f1;
show create function f2;
show create function f3;
@@ -200,6 +224,8 @@ show create function f12;
show create function f13;
show create function f14;
show create function f15;
+show create function f16;
+show create function f17;
# Switch back to connection 'default'.
#
# 1. Statements that read tables and do not use subqueries.
@@ -359,14 +385,11 @@ Success: 'update v2 set j= j-10 where j = 3' doesn't allow concurrent inserts in
# 4.1 SELECT/SET with a stored function which does not
# modify data and uses SELECT in its turn.
#
-# In theory there is no need to take strong locks on the table
+# There is no need to take strong locks on the table
# being selected from in SF as the call to such function
-# won't get into the binary log. In practice, however, we
-# discover that fact too late in the process to be able to
-# affect the decision what locks should be taken.
-# Hence, strong locks are taken in this case.
-Success: 'select f1()' doesn't allow concurrent inserts into 't1'.
-Success: 'set @a:= f1()' doesn't allow concurrent inserts into 't1'.
+# won't get into the binary log.
+Success: 'select f1()' allows concurrent inserts into 't1'.
+Success: 'set @a:= f1()' allows concurrent inserts into 't1'.
#
# 4.2 INSERT (or other statement which modifies data) with
# a stored function which does not modify data and uses
@@ -392,14 +415,12 @@ Success: 'set @a:= f2()' doesn't allow concurrent inserts into 't1'.
# modify data and reads a table through subselect
# in a control construct.
#
-# Again, in theory a call to this function won't get to the
-# binary log and thus no strong lock is needed. But in practice
-# we don't detect this fact early enough (get_lock_type_for_table())
-# to avoid taking a strong lock.
-Success: 'select f3()' doesn't allow concurrent inserts into 't1'.
-Success: 'set @a:= f3()' doesn't allow concurrent inserts into 't1'.
-Success: 'select f4()' doesn't allow concurrent inserts into 't1'.
-Success: 'set @a:= f4()' doesn't allow concurrent inserts into 't1'.
+# Call to this function won't get to the
+# binary log and thus no strong lock is needed.
+Success: 'select f3()' allows concurrent inserts into 't1'.
+Success: 'set @a:= f3()' allows concurrent inserts into 't1'.
+Success: 'select f4()' allows concurrent inserts into 't1'.
+Success: 'set @a:= f4()' allows concurrent inserts into 't1'.
#
# 4.5. INSERT (or other statement which modifies data) with
# a stored function which does not modify data and reads
@@ -426,14 +447,13 @@ Success: 'set @a:= f5()' doesn't allow concurrent inserts into 't1'.
# doesn't modify data and reads tables through
# a view.
#
-# Once again, in theory, calls to such functions won't
-# get into the binary log and thus don't need strong
-# locks. But in practice this fact is discovered
-# too late to have any effect.
-Success: 'select f6()' doesn't allow concurrent inserts into 't1'.
-Success: 'set @a:= f6()' doesn't allow concurrent inserts into 't1'.
-Success: 'select f7()' doesn't allow concurrent inserts into 't1'.
-Success: 'set @a:= f7()' doesn't allow concurrent inserts into 't1'.
+# Calls to such functions won't get into
+# the binary log and thus don't need strong
+# locks.
+Success: 'select f6()' allows concurrent inserts into 't1'.
+Success: 'set @a:= f6()' allows concurrent inserts into 't1'.
+Success: 'select f7()' allows concurrent inserts into 't1'.
+Success: 'set @a:= f7()' allows concurrent inserts into 't1'.
#
# 4.8 INSERT which uses stored function which
# doesn't modify data and reads a table
@@ -459,10 +479,9 @@ Success: 'select f9()' doesn't allow concurrent inserts into 't1'.
# data and reads a table indirectly, by calling another
# function.
#
-# In theory, calls to such functions won't get into the binary
-# log and thus don't need to acquire strong locks. But in practice
-# this fact is discovered too late to have any effect.
-Success: 'select f10()' doesn't allow concurrent inserts into 't1'.
+# Calls to such functions won't get into the binary
+# log and thus don't need to acquire strong locks.
+Success: 'select f10()' allows concurrent inserts into 't1'.
#
# 4.11 INSERT which uses a stored function which doesn't modify
# data and reads a table indirectly, by calling another
@@ -501,6 +520,26 @@ Success: 'select f12((select i+10 from t1 where i=1))' allows concurrent inserts
# uses. Therefore it should take strong locks on the data it reads.
Success: 'insert into t2 values (f13((select i+10 from t1 where i=1)))' doesn't allow concurrent inserts into 't1'.
#
+# 4.15 SELECT/SET with a stored function which
+# inserts data into a temporary table using
+# SELECT on t1.
+#
+# Since this statement is written to the binary log it should
+# be serialized with concurrent statements affecting the data it
+# uses. Therefore it should take strong locks on the data it reads.
+Success: 'select f16()' doesn't allow concurrent inserts into 't1'.
+Success: 'set @a:= f16()' doesn't allow concurrent inserts into 't1'.
+#
+# 4.16 SELECT/SET with a stored function which call procedure
+# which inserts data into a temporary table using
+# SELECT on t1.
+#
+# Since this statement is written to the binary log it should
+# be serialized with concurrent statements affecting the data it
+# uses. Therefore it should take strong locks on the data it reads.
+Success: 'select f17()' doesn't allow concurrent inserts into 't1'.
+Success: 'set @a:= f17()' doesn't allow concurrent inserts into 't1'.
+#
# 5. Statements that read tables through stored procedures.
#
#
@@ -522,10 +561,9 @@ Success: 'select f14()' doesn't allow concurrent inserts into 't1'.
# 5.3 SELECT that calls a function that doesn't modify data and
# uses a CALL statement that reads a table via SELECT.
#
-# In theory, calls to such functions won't get into the binary
-# log and thus don't need to acquire strong locks. But in practice
-# this fact is discovered too late to have any effect.
-Success: 'select f15()' doesn't allow concurrent inserts into 't1'.
+# Calls to such functions won't get into the binary
+# log and thus don't need to acquire strong locks.
+Success: 'select f15()' allows concurrent inserts into 't1'.
#
# 5.4 INSERT which calls function which doesn't modify data and
# uses CALL statement which reads table through SELECT.
@@ -585,9 +623,12 @@ drop function f12;
drop function f13;
drop function f14;
drop function f15;
+drop function f16;
+drop function f17;
drop view v1, v2;
drop procedure p1;
drop procedure p2;
+drop procedure p3;
drop table t1, t2, t3, t4, t5;
set @@global.concurrent_insert= @old_concurrent_insert;
#
diff --git a/mysql-test/t/innodb_mysql_lock2.test b/mysql-test/t/innodb_mysql_lock2.test
index b7259e771ae..640f9652462 100644
--- a/mysql-test/t/innodb_mysql_lock2.test
+++ b/mysql-test/t/innodb_mysql_lock2.test
@@ -440,15 +440,16 @@ let $wait_statement= $statement;
--echo # 4.1 SELECT/SET with a stored function which does not
--echo # modify data and uses SELECT in its turn.
--echo #
---echo # In theory there is no need to take row locks on the table
+--echo # There is no need to take row locks on the table
--echo # being selected from in SF as the call to such function
---echo # won't get into the binary log. In practice, however, we
---echo # discover that fact too late in the process to be able to
---echo # affect the decision what locks should be taken.
---echo # Hence, strong locks are taken in this case.
+--echo # won't get into the binary log.
+--echo #
+--echo # However in practice innodb takes strong lock on tables
+--echo # being selected from within SF, when SF is called from
+--echo # non SELECT statements like 'set' statement below.
let $statement= select f1();
let $wait_statement= select i from t1 where i = 1 into j;
---source include/check_shared_row_lock.inc
+--source include/check_no_row_lock.inc
let $statement= set @a:= f1();
let $wait_statement= select i from t1 where i = 1 into j;
--source include/check_shared_row_lock.inc
@@ -486,19 +487,21 @@ let $wait_statement= select i from t1 where i = 1 into k;
--echo # modify data and reads a table through subselect
--echo # in a control construct.
--echo #
---echo # Again, in theory a call to this function won't get to the
---echo # binary log and thus no locking is needed. But in practice
---echo # we don't detect this fact early enough (get_lock_type_for_table())
---echo # to avoid taking row locks.
+--echo # Call to this function won't get to the
+--echo # binary log and thus no locking is needed.
+--echo #
+--echo # However in practice innodb takes strong lock on tables
+--echo # being selected from within SF, when SF is called from
+--echo # non SELECT statements like 'set' statement below.
let $statement= select f3();
let $wait_statement= $statement;
---source include/check_shared_row_lock.inc
+--source include/check_no_row_lock.inc
let $statement= set @a:= f3();
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
let $statement= select f4();
let $wait_statement= $statement;
---source include/check_shared_row_lock.inc
+--source include/check_no_row_lock.inc
let $statement= set @a:= f4();
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
@@ -539,19 +542,21 @@ let $wait_statement= insert into t2 values ((select i from t1 where i = 1) + 5);
--echo # doesn't modify data and reads tables through
--echo # a view.
--echo #
---echo # Once again, in theory, calls to such functions won't
---echo # get into the binary log and thus don't need row
---echo # locks. But in practice this fact is discovered
---echo # too late to have any effect.
+--echo # Calls to such functions won't get into
+--echo # the binary log and thus don't need row locks.
+--echo #
+--echo # However in practice innodb takes strong lock on tables
+--echo # being selected from within SF, when SF is called from
+--echo # non SELECT statements like 'set' statement below.
let $statement= select f6();
let $wait_statement= select i from v1 where i = 1 into k;
---source include/check_shared_row_lock.inc
+--source include/check_no_row_lock.inc
let $statement= set @a:= f6();
let $wait_statement= select i from v1 where i = 1 into k;
--source include/check_shared_row_lock.inc
let $statement= select f7();
let $wait_statement= select j from v2 where j = 1 into k;
---source include/check_shared_row_lock.inc
+--source include/check_no_row_lock.inc
let $statement= set @a:= f7();
let $wait_statement= select j from v2 where j = 1 into k;
--source include/check_shared_row_lock.inc
@@ -592,12 +597,11 @@ let $wait_statement= update v2 set j=j+10 where j=1;
--echo # data and reads a table indirectly, by calling another
--echo # function.
--echo #
---echo # In theory, calls to such functions won't get into the binary
---echo # log and thus don't need to acquire row locks. But in practice
---echo # this fact is discovered too late to have any effect.
+--echo # Calls to such functions won't get into the binary
+--echo # log and thus don't need to acquire row locks.
let $statement= select f10();
let $wait_statement= select i from t1 where i = 1 into j;
---source include/check_shared_row_lock.inc
+--source include/check_no_row_lock.inc
--echo #
--echo # 4.11 INSERT which uses a stored function which doesn't modify
@@ -676,12 +680,11 @@ let $wait_statement= select i from t1 where i = 1 into p;
--echo # 5.3 SELECT that calls a function that doesn't modify data and
--echo # uses a CALL statement that reads a table via SELECT.
--echo #
---echo # In theory, calls to such functions won't get into the binary
---echo # log and thus don't need to acquire row locks. But in practice
---echo # this fact is discovered too late to have any effect.
+--echo # Calls to such functions won't get into the binary
+--echo # log and thus don't need to acquire row locks.
let $statement= select f15();
let $wait_statement= select i from t1 where i = 1 into p;
---source include/check_shared_row_lock.inc
+--source include/check_no_row_lock.inc
--echo #
--echo # 5.4 INSERT which calls function which doesn't modify data and
diff --git a/mysql-test/t/lock_sync.test b/mysql-test/t/lock_sync.test
index d5ad7becd7d..f00080d917b 100644
--- a/mysql-test/t/lock_sync.test
+++ b/mysql-test/t/lock_sync.test
@@ -49,6 +49,7 @@ drop table if exists t0, t1, t2, t3, t4, t5;
drop view if exists v1, v2;
drop procedure if exists p1;
drop procedure if exists p2;
+drop procedure if exists p3;
drop function if exists f1;
drop function if exists f2;
drop function if exists f3;
@@ -64,6 +65,8 @@ drop function if exists f12;
drop function if exists f13;
drop function if exists f14;
drop function if exists f15;
+drop function if exists f16;
+drop function if exists f17;
--enable_warnings
create table t1 (i int primary key);
insert into t1 values (1), (2), (3), (4), (5);
@@ -170,6 +173,26 @@ begin
call p2(k);
return k;
end|
+create function f16() returns int
+begin
+ create temporary table if not exists temp1 (a int);
+ insert into temp1 select * from t1;
+ drop temporary table temp1;
+ return 1;
+end|
+create function f17() returns int
+begin
+ declare j int;
+ select i from t1 where i = 1 into j;
+ call p3;
+ return 1;
+end|
+create procedure p3()
+begin
+ create temporary table if not exists temp1 (a int);
+ insert into temp1 select * from t1;
+ drop temporary table temp1;
+end|
create trigger t4_bi before insert on t4 for each row
begin
declare k int;
@@ -217,6 +240,7 @@ connection con1;
--disable_result_log
show create procedure p1;
show create procedure p2;
+show create procedure p3;
show create function f1;
show create function f2;
show create function f3;
@@ -232,6 +256,8 @@ show create function f12;
show create function f13;
show create function f14;
show create function f15;
+show create function f16;
+show create function f17;
--enable_result_log
--echo # Switch back to connection 'default'.
connection default;
@@ -492,18 +518,15 @@ let $restore_table= t2;
--echo # 4.1 SELECT/SET with a stored function which does not
--echo # modify data and uses SELECT in its turn.
--echo #
---echo # In theory there is no need to take strong locks on the table
+--echo # There is no need to take strong locks on the table
--echo # being selected from in SF as the call to such function
---echo # won't get into the binary log. In practice, however, we
---echo # discover that fact too late in the process to be able to
---echo # affect the decision what locks should be taken.
---echo # Hence, strong locks are taken in this case.
+--echo # won't get into the binary log.
let $statement= select f1();
let $restore_table= ;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
let $statement= set @a:= f1();
let $restore_table= ;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
--echo #
--echo # 4.2 INSERT (or other statement which modifies data) with
@@ -538,22 +561,20 @@ let $restore_table= t2;
--echo # modify data and reads a table through subselect
--echo # in a control construct.
--echo #
---echo # Again, in theory a call to this function won't get to the
---echo # binary log and thus no strong lock is needed. But in practice
---echo # we don't detect this fact early enough (get_lock_type_for_table())
---echo # to avoid taking a strong lock.
+--echo # Call to this function won't get to the
+--echo # binary log and thus no strong lock is needed.
let $statement= select f3();
let $restore_table= ;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
let $statement= set @a:= f3();
let $restore_table= ;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
let $statement= select f4();
let $restore_table= ;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
let $statement= set @a:= f4();
let $restore_table= ;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
--echo #
--echo # 4.5. INSERT (or other statement which modifies data) with
@@ -591,22 +612,21 @@ let $restore_table= t2;
--echo # doesn't modify data and reads tables through
--echo # a view.
--echo #
---echo # Once again, in theory, calls to such functions won't
---echo # get into the binary log and thus don't need strong
---echo # locks. But in practice this fact is discovered
---echo # too late to have any effect.
+--echo # Calls to such functions won't get into
+--echo # the binary log and thus don't need strong
+--echo # locks.
let $statement= select f6();
let $restore_table= t2;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
let $statement= set @a:= f6();
let $restore_table= t2;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
let $statement= select f7();
let $restore_table= t2;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
let $statement= set @a:= f7();
let $restore_table= t2;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
--echo #
--echo # 4.8 INSERT which uses stored function which
@@ -644,12 +664,11 @@ let $restore_table= t2;
--echo # data and reads a table indirectly, by calling another
--echo # function.
--echo #
---echo # In theory, calls to such functions won't get into the binary
---echo # log and thus don't need to acquire strong locks. But in practice
---echo # this fact is discovered too late to have any effect.
+--echo # Calls to such functions won't get into the binary
+--echo # log and thus don't need to acquire strong locks.
let $statement= select f10();
let $restore_table= ;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
--echo #
--echo # 4.11 INSERT which uses a stored function which doesn't modify
@@ -700,6 +719,36 @@ let $statement= insert into t2 values (f13((select i+10 from t1 where i=1)));
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
+--echo #
+--echo # 4.15 SELECT/SET with a stored function which
+--echo # inserts data into a temporary table using
+--echo # SELECT on t1.
+--echo #
+--echo # Since this statement is written to the binary log it should
+--echo # be serialized with concurrent statements affecting the data it
+--echo # uses. Therefore it should take strong locks on the data it reads.
+let $statement= select f16();
+let $restore_table= ;
+--source include/check_no_concurrent_insert.inc
+let $statement= set @a:= f16();
+let $restore_table= ;
+--source include/check_no_concurrent_insert.inc
+
+--echo #
+--echo # 4.16 SELECT/SET with a stored function which call procedure
+--echo # which inserts data into a temporary table using
+--echo # SELECT on t1.
+--echo #
+--echo # Since this statement is written to the binary log it should
+--echo # be serialized with concurrent statements affecting the data it
+--echo # uses. Therefore it should take strong locks on the data it reads.
+let $statement= select f17();
+let $restore_table= ;
+--source include/check_no_concurrent_insert.inc
+let $statement= set @a:= f17();
+let $restore_table= ;
+--source include/check_no_concurrent_insert.inc
+
--echo #
--echo # 5. Statements that read tables through stored procedures.
@@ -730,12 +779,11 @@ let $restore_table= t2;
--echo # 5.3 SELECT that calls a function that doesn't modify data and
--echo # uses a CALL statement that reads a table via SELECT.
--echo #
---echo # In theory, calls to such functions won't get into the binary
---echo # log and thus don't need to acquire strong locks. But in practice
---echo # this fact is discovered too late to have any effect.
+--echo # Calls to such functions won't get into the binary
+--echo # log and thus don't need to acquire strong locks.
let $statement= select f15();
let $restore_table= ;
---source include/check_no_concurrent_insert.inc
+--source include/check_concurrent_insert.inc
--echo #
--echo # 5.4 INSERT which calls function which doesn't modify data and
@@ -800,7 +848,6 @@ let $statement= update t5 set l= 2 where l = 1;
let $restore_table= t5;
--source include/check_no_concurrent_insert.inc
-
--echo # Clean-up.
drop function f1;
drop function f2;
@@ -817,9 +864,12 @@ drop function f12;
drop function f13;
drop function f14;
drop function f15;
+drop function f16;
+drop function f17;
drop view v1, v2;
drop procedure p1;
drop procedure p2;
+drop procedure p3;
drop table t1, t2, t3, t4, t5;
disconnect con1;
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index d51242c56bc..366679a2fb0 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -2323,6 +2323,11 @@ sp_head::restore_lex(THD *thd)
*/
if (sp_update_sp_used_routines(&m_sroutines, &sublex->sroutines))
DBUG_RETURN(TRUE);
+
+ /* If this substatement is a update query, then mark MODIFIES_DATA */
+ if (is_update_query(sublex->sql_command))
+ m_flags|= MODIFIES_DATA;
+
/*
Merge tables used by this statement (but not by its functions or
procedures) to multiset of tables used by this routine.
diff --git a/sql/sp_head.h b/sql/sp_head.h
index cc598186d08..dbdb957aa79 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -161,7 +161,21 @@ public:
LOG_SLOW_STATEMENTS= 256, // Used by events
LOG_GENERAL_LOG= 512, // Used by events
HAS_SQLCOM_RESET= 1024,
- HAS_SQLCOM_FLUSH= 2048
+ HAS_SQLCOM_FLUSH= 2048,
+
+ /**
+ Marks routines that directly (i.e. not by calling other routines)
+ change tables. Note that this flag is set automatically based on
+ type of statements used in the stored routine and is different
+ from routine characteristic provided by user in a form of CONTAINS
+ SQL, READS SQL DATA, MODIFIES SQL DATA clauses. The latter are
+ accepted by parser but pretty much ignored after that.
+ We don't rely on them:
+ a) for compatibility reasons.
+ b) because in CONTAINS SQL case they don't provide enough
+ information anyway.
+ */
+ MODIFIES_DATA= 4096
};
stored_procedure_type m_type;
@@ -332,11 +346,17 @@ public:
int
add_instr(sp_instr *instr);
- inline uint
- instructions()
- {
- return m_instr.elements;
- }
+ /**
+ Returns true if any substatement in the routine directly
+ (not through another routine) modifies data/changes table.
+
+ @sa Comment for MODIFIES_DATA flag.
+ */
+ bool modifies_data() const
+ { return m_flags & MODIFIES_DATA; }
+
+ inline uint instructions()
+ { return m_instr.elements; }
inline sp_instr *
last_instruction()
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 8d5d5058ed1..342a45247c5 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -3517,9 +3517,12 @@ Open_table_context::recover_from_failed_open()
/*
Return a appropriate read lock type given a table object.
- @param thd Thread context
- @param prelocking_ctx Prelocking context.
- @param table_list Table list element for table to be locked.
+ @param thd Thread context
+ @param prelocking_ctx Prelocking context.
+ @param table_list Table list element for table to be locked.
+ @param routine_modifies_data
+ Some routine that is invoked by statement
+ modifies data.
@remark Due to a statement-based replication limitation, statements such as
INSERT INTO .. SELECT FROM .. and CREATE TABLE .. SELECT FROM need
@@ -3532,9 +3535,13 @@ Open_table_context::recover_from_failed_open()
This also applies to SELECT/SET/DO statements which use stored
functions. Calls to such functions are going to be logged as a
whole and thus should be serialized against concurrent changes
- to tables used by those functions. This can be avoided if functions
- only read data but doing so requires more complex analysis than it
- is done now.
+ to tables used by those functions. This is avoided when functions
+ do not modify data but only read it, since in this case nothing is
+ written to the binary log. Argument routine_modifies_data
+ denotes the same. So effectively, if the statement is not a
+ update query and routine_modifies_data is false, then
+ prelocking_placeholder does not take importance.
+
Furthermore, this does not apply to I_S and log tables as it's
always unsafe to replicate such tables under statement-based
replication as the table on the slave might contain other data
@@ -3549,7 +3556,8 @@ Open_table_context::recover_from_failed_open()
thr_lock_type read_lock_type_for_table(THD *thd,
Query_tables_list *prelocking_ctx,
- TABLE_LIST *table_list)
+ TABLE_LIST *table_list,
+ bool routine_modifies_data)
{
/*
In cases when this function is called for a sub-statement executed in
@@ -3564,7 +3572,7 @@ thr_lock_type read_lock_type_for_table(THD *thd,
(table_list->table->s->table_category == TABLE_CATEGORY_LOG) ||
(table_list->table->s->table_category == TABLE_CATEGORY_PERFORMANCE) ||
!(is_update_query(prelocking_ctx->sql_command) ||
- table_list->prelocking_placeholder ||
+ (routine_modifies_data && table_list->prelocking_placeholder) ||
(thd->locked_tables_mode > LTM_LOCK_TABLES)))
return TL_READ;
else
@@ -3577,19 +3585,21 @@ thr_lock_type read_lock_type_for_table(THD *thd,
and, if prelocking strategy prescribes so, extend the prelocking set
with tables and routines used by it.
- @param[in] thd Thread context.
- @param[in] prelocking_ctx Prelocking context.
- @param[in] rt Element of prelocking set to be processed.
- @param[in] prelocking_strategy Strategy which specifies how the
- prelocking set should be extended when
- one of its elements is processed.
- @param[in] has_prelocking_list Indicates that prelocking set/list for
- this statement has already been built.
- @param[in] ot_ctx Context of open_table used to recover from
- locking failures.
- @param[out] need_prelocking Set to TRUE if it was detected that this
- statement will require prelocked mode for
- its execution, not touched otherwise.
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context.
+ @param[in] rt Element of prelocking set to be processed.
+ @param[in] prelocking_strategy Strategy which specifies how the
+ prelocking set should be extended when
+ one of its elements is processed.
+ @param[in] has_prelocking_list Indicates that prelocking set/list for
+ this statement has already been built.
+ @param[in] ot_ctx Context of open_table used to recover from
+ locking failures.
+ @param[out] need_prelocking Set to TRUE if it was detected that this
+ statement will require prelocked mode for
+ its execution, not touched otherwise.
+ @param[out] routine_modifies_data Set to TRUE if it was detected that this
+ routine does modify table data.
@retval FALSE Success.
@retval TRUE Failure (Conflicting metadata lock, OOM, other errors).
@@ -3601,11 +3611,13 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
Prelocking_strategy *prelocking_strategy,
bool has_prelocking_list,
Open_table_context *ot_ctx,
- bool *need_prelocking)
+ bool *need_prelocking, bool *routine_modifies_data)
{
MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace();
DBUG_ENTER("open_and_process_routine");
+ *routine_modifies_data= false;
+
switch (mdl_type)
{
case MDL_key::FUNCTION:
@@ -3658,10 +3670,13 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
DBUG_RETURN(TRUE);
/* 'sp' is NULL when there is no such routine. */
- if (sp && !has_prelocking_list)
+ if (sp)
{
- prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
- need_prelocking);
+ *routine_modifies_data= sp->modifies_data();
+
+ if (!has_prelocking_list)
+ prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
+ need_prelocking);
}
}
else
@@ -4006,16 +4021,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
goto end;
}
- if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode)
- {
- if (tables->lock_type == TL_WRITE_DEFAULT)
- tables->table->reginfo.lock_type= thd->update_lock_default;
- else if (tables->lock_type == TL_READ_DEFAULT)
- tables->table->reginfo.lock_type=
- read_lock_type_for_table(thd, lex, tables);
- else
- tables->table->reginfo.lock_type= tables->lock_type;
- }
+ /* Copy grant information from TABLE_LIST instance to TABLE one. */
tables->table->grant= tables->grant;
/* Check and update metadata version of a base table. */
@@ -4354,6 +4360,7 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Open_table_context ot_ctx(thd, flags);
bool error= FALSE;
MEM_ROOT new_frm_mem;
+ bool some_routine_modifies_data= FALSE;
bool has_prelocking_list;
DBUG_ENTER("open_tables");
@@ -4526,11 +4533,16 @@ restart:
sroutine_to_open= &rt->next, rt= rt->next)
{
bool need_prelocking= false;
+ bool routine_modifies_data;
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
error= open_and_process_routine(thd, thd->lex, rt, prelocking_strategy,
has_prelocking_list, &ot_ctx,
- &need_prelocking);
+ &need_prelocking,
+ &routine_modifies_data);
+
+ // Remember if any of SF modifies data.
+ some_routine_modifies_data|= routine_modifies_data;
if (need_prelocking && ! thd->lex->requires_prelocking())
thd->lex->mark_as_requiring_prelocking(save_query_tables_last);
@@ -4570,6 +4582,10 @@ restart:
children, attach the children to their parents. At end of statement,
the children are detached. Attaching and detaching are always done,
even under LOCK TABLES.
+
+ We also convert all TL_WRITE_DEFAULT and TL_READ_DEFAULT locks to
+ appropriate "real" lock types to be used for locking and to be passed
+ to storage engine.
*/
for (tables= *start; tables; tables= tables->next_global)
{
@@ -4586,6 +4602,19 @@ restart:
goto err;
}
}
+
+ /* Set appropriate TABLE::lock_type. */
+ if (tbl && tables->lock_type != TL_UNLOCK && !thd->locked_tables_mode)
+ {
+ if (tables->lock_type == TL_WRITE_DEFAULT)
+ tbl->reginfo.lock_type= thd->update_lock_default;
+ else if (tables->lock_type == TL_READ_DEFAULT)
+ tbl->reginfo.lock_type=
+ read_lock_type_for_table(thd, thd->lex, tables,
+ some_routine_modifies_data);
+ else
+ tbl->reginfo.lock_type= tables->lock_type;
+ }
}
err:
@@ -4847,11 +4876,15 @@ static bool check_lock_and_start_stmt(THD *thd,
engine is important as, for example, InnoDB uses it to determine
what kind of row locks should be acquired when executing statement
in prelocked mode or under LOCK TABLES with @@innodb_table_locks = 0.
+
+ Last argument routine_modifies_data for read_lock_type_for_table()
+ is ignored, as prelocking placeholder will never be set here.
*/
+ DBUG_ASSERT(table_list->prelocking_placeholder == false);
if (table_list->lock_type == TL_WRITE_DEFAULT)
lock_type= thd->update_lock_default;
else if (table_list->lock_type == TL_READ_DEFAULT)
- lock_type= read_lock_type_for_table(thd, prelocking_ctx, table_list);
+ lock_type= read_lock_type_for_table(thd, prelocking_ctx, table_list, true);
else
lock_type= table_list->lock_type;
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 61442843a39..8a0a1e42500 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -136,7 +136,8 @@ TABLE *find_write_locked_table(TABLE *list, const char *db,
const char *table_name);
thr_lock_type read_lock_type_for_table(THD *thd,
Query_tables_list *prelocking_ctx,
- TABLE_LIST *table_list);
+ TABLE_LIST *table_list,
+ bool routine_modifies_data);
my_bool mysql_rm_tmp_tables(void);
bool rm_temporary_table(handlerton *base, const char *path);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 1b808d333d8..1eda5b2681a 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1431,11 +1431,15 @@ int mysql_multi_update_prepare(THD *thd)
another table instance used by this statement which is going to
be write-locked (for example, trigger to be invoked might try
to update this table).
+ Last argument routine_modifies_data for read_lock_type_for_table()
+ is ignored, as prelocking placeholder will never be set here.
*/
+ DBUG_ASSERT(tl->prelocking_placeholder == false);
+ thr_lock_type lock_type= read_lock_type_for_table(thd, lex, tl, true);
if (using_lock_tables)
- tl->lock_type= read_lock_type_for_table(thd, lex, tl);
+ tl->lock_type= lock_type;
else
- tl->set_lock_type(thd, read_lock_type_for_table(thd, lex, tl));
+ tl->set_lock_type(thd, lock_type);
tl->updating= 0;
}
}