1
0
mirror of https://github.com/MariaDB/server.git synced 2025-05-28 13:01:41 +03:00

merge from mysql-trunk-runtime

This commit is contained in:
Jon Olav Hauglid 2010-05-21 15:49:15 +02:00
commit 923da1e889
47 changed files with 3663 additions and 164 deletions

View File

@ -262,6 +262,7 @@ IF(NOT WITHOUT_SERVER)
ENDIF()
INCLUDE(cmake/abi_check.cmake)
INCLUDE(cmake/tags.cmake)
CONFIGURE_FILE(config.h.cmake ${CMAKE_BINARY_DIR}/include/my_config.h)
CONFIGURE_FILE(config.h.cmake ${CMAKE_BINARY_DIR}/include/config.h)

View File

@ -24,6 +24,7 @@ EXTRA_DIST = \
dtrace_prelink.cmake \
versioninfo.rc.in \
mysql_add_executable.cmake \
tags.cmake \
install_layout.cmake \
build_configurations/mysql_release.cmake \
os/Windows.cmake \

26
cmake/tags.cmake Normal file
View File

@ -0,0 +1,26 @@
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# Generate tag files
IF(UNIX)
ADD_CUSTOM_TARGET (tags
COMMAND support-files/build-tags
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
ADD_CUSTOM_TARGET (ctags
COMMAND ctags -R -f CTAGS
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
ENDIF()

View File

@ -197,7 +197,14 @@ enum enum_server_command
& ~CLIENT_COMPRESS) \
& ~CLIENT_SSL_VERIFY_SERVER_CERT)
#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */
/**
Is raised when a multi-statement transaction
has been started, either explicitly, by means
of BEGIN or COMMIT AND CHAIN, or
implicitly, by the first transactional
statement, when autocommit=off.
*/
#define SERVER_STATUS_IN_TRANS 1
#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */
#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */
#define SERVER_QUERY_NO_GOOD_INDEX_USED 16

View File

@ -0,0 +1,96 @@
#
# SUMMARY
# Check if statement reading table '$table' allows concurrent
# inserts in it.
#
# PARAMETERS
# $table Table in which concurrent inserts should be allowed.
# $con_aux1 Name of the first auxiliary connection to be used by this
# script.
# $con_aux2 Name of the second auxiliary connection to be used by this
# script.
# $statement Statement to be checked.
# $restore_table Table which might be modified affected by statement to be
# checked and thus needs backing up before its execution
# and restoring after it (can be empty).
#
# EXAMPLE
# lock_sync.test
#
--disable_result_log
--disable_query_log
# Reset DEBUG_SYNC facility for safety.
set debug_sync= "RESET";
if (`SELECT '$restore_table' <> ''`)
{
--eval create table t_backup select * from $restore_table;
}
connection $con_aux1;
set debug_sync='after_lock_tables_takes_lock SIGNAL parked WAIT_FOR go';
--send_eval $statement;
connection $con_aux2;
set debug_sync='now WAIT_FOR parked';
--send_eval insert into $table values (0);
--enable_result_log
--enable_query_log
connection default;
# Wait until concurrent insert is successfully executed while
# statement being checked has its tables locked.
# We use wait_condition.inc instead of simply executing
# concurrent insert here in order to avoid deadlocks if test
# fails and timing out instead.
let $wait_condition=
select count(*) = 0 from information_schema.processlist
where info = "insert into $table values (0)";
--source include/wait_condition.inc
--disable_result_log
--disable_query_log
if ($success)
{
# Apparently concurrent insert was successfully executed.
# To be safe against wait_condition.inc succeeding due to
# races let us first reap concurrent insert to ensure that
# it has really been successfully executed.
connection $con_aux2;
--reap
connection default;
set debug_sync= 'now SIGNAL go';
connection $con_aux1;
--reap
connection default;
--echo Success: '$statement' allows concurrent inserts into '$table'.
}
if (!$success)
{
# Waiting has timed out. Apparently concurrent insert was blocked.
# So to be able to continue we need to end our statement first.
set debug_sync= 'now SIGNAL go';
connection $con_aux1;
--reap
connection $con_aux2;
--reap
connection default;
--echo Error: '$statement' doesn't allow concurrent inserts into '$table'!
}
--eval delete from $table where i = 0;
if (`SELECT '$restore_table' <> ''`)
{
--eval truncate table $restore_table;
--eval insert into $restore_table select * from t_backup;
drop table t_backup;
}
# Clean-up. Reset DEBUG_SYNC facility after use.
set debug_sync= "RESET";
--enable_result_log
--enable_query_log

View File

@ -0,0 +1,81 @@
#
# SUMMARY
# Check that statement reading table '$table' doesn't allow concurrent
# inserts in it.
#
# PARAMETERS
# $table Table in which concurrent inserts should be disallowed.
# $con_aux1 Name of the first auxiliary connection to be used by this
# script.
# $con_aux2 Name of the second auxiliary connection to be used by this
# script.
# $statement Statement to be checked.
# $restore_table Table which might be modified affected by statement to be
# checked and thus needs backing up before its execution
# and restoring after it (can be empty).
#
# EXAMPLE
# lock_sync.test
#
--disable_result_log
--disable_query_log
# Reset DEBUG_SYNC facility for safety.
set debug_sync= "RESET";
if (`SELECT '$restore_table' <> ''`)
{
--eval create table t_backup select * from $restore_table;
}
connection $con_aux1;
set debug_sync='after_lock_tables_takes_lock SIGNAL parked WAIT_FOR go';
--send_eval $statement;
connection $con_aux2;
set debug_sync='now WAIT_FOR parked';
--send_eval insert into $table values (0);
--enable_result_log
--enable_query_log
connection default;
# Wait until concurrent insert is successfully blocked because
# of our statement.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Table lock" and info = "insert into $table values (0)";
--source include/wait_condition.inc
--disable_result_log
--disable_query_log
set debug_sync= 'now SIGNAL go';
connection $con_aux1;
--reap
connection $con_aux2;
--reap
connection default;
if ($success)
{
--echo Success: '$statement' doesn't allow concurrent inserts into '$table'.
}
if (!$success)
{
--echo Error: '$statement' allows concurrent inserts into '$table'!
}
--eval delete from $table where i = 0;
if (`SELECT '$restore_table' <> ''`)
{
--eval truncate table $restore_table;
--eval insert into $restore_table select * from t_backup;
drop table t_backup;
}
# Clean-up. Reset DEBUG_SYNC facility after use.
set debug_sync= "RESET";
--enable_result_log
--enable_query_log

View File

@ -0,0 +1,71 @@
#
# SUMMARY
# Check if statement affecting or reading table '$table' doesn't
# take any kind of locks on its rows.
#
# PARAMETERS
# $table Table for which presence of row locks should be checked.
# $con_aux Name of auxiliary connection to be used by this script.
# $statement Statement to be checked.
#
# EXAMPLE
# innodb_mysql_lock2.test
#
--disable_result_log
--disable_query_log
connection default;
begin;
--eval select * from $table for update;
connection $con_aux;
begin;
--send_eval $statement;
--enable_result_log
--enable_query_log
connection default;
# Wait until statement is successfully executed while
# all rows in table are X-locked. This means that it
# does not acquire any row locks.
# We use wait_condition.inc instead of simply executing
# statement here in order to avoid deadlocks if test
# fails and timing out instead.
let $wait_condition=
select count(*) = 0 from information_schema.processlist
where info = "$statement";
--source include/wait_condition.inc
--disable_result_log
--disable_query_log
if ($success)
{
# Apparently statement was successfully executed and thus it
# has not required any row locks.
# To be safe against wait_condition.inc succeeding due to
# races let us first reap the statement being checked to
# ensure that it has been successfully executed.
connection $con_aux;
--reap
rollback;
connection default;
rollback;
--echo Success: '$statement' doesn't take row locks on '$table'.
}
if (!$success)
{
# Waiting has timed out. Apparently statement was blocked on
# some row lock. So to be able to continue we need to unlock
# rows first.
rollback;
connection $con_aux;
--reap
rollback;
connection default;
--echo Error: '$statement' takes some row locks on '$table'!
}
--enable_result_log
--enable_query_log

View File

@ -0,0 +1,61 @@
#
# SUMMARY
# Check if statement reading table '$table' takes shared locks
# on some of its rows.
#
# PARAMETERS
# $table Table for which presence of row locks should be checked.
# $con_aux Name of auxiliary connection to be used by this script.
# $statement Statement to be checked.
# $wait_statement Sub-statement which is supposed to acquire locks (should
# be the same as $statement for ordinary statements).
#
# EXAMPLE
# innodb_mysql_lock2.test
#
--disable_result_log
--disable_query_log
connection default;
begin;
--eval select * from $table for update;
connection $con_aux;
begin;
--send_eval $statement;
--enable_result_log
--enable_query_log
connection default;
# Wait until statement is successfully blocked because
# all rows in table are X-locked. This means that at
# least it acquires S-locks on some of rows.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state in ("Sending data","statistics", "preparing") and
info = "$wait_statement";
--source include/wait_condition.inc
--disable_result_log
--disable_query_log
rollback;
connection $con_aux;
--reap
rollback;
connection default;
--enable_result_log
--enable_query_log
if ($success)
{
--echo Success: '$statement' takes shared row locks on '$table'.
}
if (!$success)
{
--echo Error: '$statement' hasn't taken shared row locks on '$table'!
}

View File

@ -12,7 +12,7 @@ INSERT INTO t2 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),
START TRANSACTION;
# in thread2
REPLACE INTO t2 VALUES (-17);
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d);
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d) LOCK IN SHARE MODE;
d
# in thread1
REPLACE INTO t1(a,b) VALUES (67,20);
@ -21,10 +21,10 @@ COMMIT;
START TRANSACTION;
REPLACE INTO t1(a,b) VALUES (65,-50);
REPLACE INTO t2 VALUES (-91);
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d);
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d) LOCK IN SHARE MODE;
# in thread1
# should not crash
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d);
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d) LOCK IN SHARE MODE;
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
# in thread2
d

View File

@ -1977,3 +1977,59 @@ CREATE TABLE t1 LIKE t2;
ERROR 42S01: Table 't1' already exists
DROP TABLE t2;
DROP TABLE t1;
#
# Bug #48800 CREATE TABLE t...SELECT fails if t is a
# temporary table
#
CREATE TEMPORARY TABLE t1 (a INT);
CREATE TABLE t1 (a INT);
CREATE TEMPORARY TABLE t2 (a INT);
CREATE VIEW t2 AS SELECT 1;
CREATE TABLE t3 (a INT);
CREATE TEMPORARY TABLE t3 SELECT 1;
CREATE TEMPORARY TABLE t4 (a INT);
CREATE TABLE t4 AS SELECT 1;
DROP TEMPORARY TABLE t1, t2, t3, t4;
DROP TABLE t1, t3, t4;
DROP VIEW t2;
#
# Bug #49193 CREATE TABLE reacts differently depending
# on whether data is selected or not
#
CREATE TEMPORARY TABLE t2 (ID INT);
INSERT INTO t2 VALUES (1),(2),(3);
CREATE TEMPORARY TABLE t1 (ID INT);
CREATE TABLE IF NOT EXISTS t1 (ID INT);
INSERT INTO t1 SELECT * FROM t2;
SELECT * FROM t1;
ID
1
2
3
DROP TEMPORARY TABLE t1;
SELECT * FROM t1;
ID
DROP TABLE t1;
CREATE TEMPORARY TABLE t1 (ID INT);
CREATE TABLE IF NOT EXISTS t1 SELECT * FROM t2;
SELECT * FROM t1;
ID
DROP TEMPORARY TABLE t1;
SELECT * FROM t1;
ID
1
2
3
DROP TABLE t1;
CREATE TEMPORARY TABLE t1 (ID INT);
CREATE TABLE t1 SELECT * FROM t2;
SELECT * FROM t1;
ID
DROP TEMPORARY TABLE t1;
SELECT * FROM t1;
ID
1
2
3
DROP TABLE t1;
DROP TEMPORARY TABLE t2;

View File

@ -27,9 +27,10 @@ commit;
drop table t1;
#
# Old lock method (where LOCK TABLE was ignored by InnoDB) no longer
# works due to fix for bugs #46272 "MySQL 5.4.4, new MDL: unnecessary
# deadlock" and bug #37346 "innodb does not detect deadlock between
# update and alter table".
# works when LOCK TABLE ... WRITE is used due to fix for bugs #46272
# "MySQL 5.4.4, new MDL: unnecessary and bug #37346 "innodb does not
# detect deadlock between update and alter table". But it still works
# for LOCK TABLE ... READ.
#
set @@innodb_table_locks=0;
create table t1 (id integer primary key, x integer) engine=INNODB;
@ -61,4 +62,30 @@ commit;
# Reap LOCK TABLE.
unlock tables;
# Connection 'con1'.
select * from t1 where id = 0 for update;
id x
0 1
# Connection 'con2'.
# The below statement should not be blocked as LOCK TABLES ... READ
# does not take strong SQL-level lock on t1. SELECTs which do not
# conflict with transaction in the first connections should not be
# blocked.
lock table t1 read;
select * from t1;
id x
0 1
1 1
2 2
select * from t1 where id = 1 lock in share mode;
id x
1 1
unlock tables;
select * from t1;
id x
0 1
1 1
2 2
commit;
# Connection 'con1'.
commit;
drop table t1;

View File

@ -86,3 +86,33 @@ release_lock('bug42147_lock')
UNLOCK TABLES;
# Connection 1
DROP TABLE t1;
#
# Bug#53798 OPTIMIZE TABLE breaks repeatable read
#
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (a INT) engine=innodb;
INSERT INTO t1 VALUES (1), (2), (3);
# Connection con1
START TRANSACTION WITH CONSISTENT SNAPSHOT;
SELECT * FROM t1;
a
1
2
3
# Connection default
# This should block
# Sending:
OPTIMIZE TABLE t1;
# Connection con1
SELECT * FROM t1;
a
1
2
3
COMMIT;
# Connection default
# Reaping OPTIMIZE TABLE t1
Table Op Msg_type Msg_text
test.t1 optimize note Table does not support optimize, doing recreate + analyze instead
test.t1 optimize status OK
DROP TABLE t1;

View File

@ -0,0 +1,564 @@
#
# Test how do we handle locking in various cases when
# we read data from InnoDB tables.
#
# In fact by performing this test we check two things:
# 1) That SQL-layer correctly determine type of thr_lock.c
# lock to be acquired/passed to InnoDB engine.
# 2) That InnoDB engine correctly interprets this lock
# type and takes necessary row locks or does not
# take them if they are not necessary.
#
# This test makes sense only in REPEATABLE-READ mode as
# in SERIALIZABLE mode all statements that read data take
# shared lock on them to enforce its semantics.
select @@session.tx_isolation;
@@session.tx_isolation
REPEATABLE-READ
# Prepare playground by creating tables, views,
# routines and triggers used in tests.
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 function if exists f1;
drop function if exists f2;
drop function if exists f3;
drop function if exists f4;
drop function if exists f5;
drop function if exists f6;
drop function if exists f7;
drop function if exists f8;
drop function if exists f9;
drop function if exists f10;
drop function if exists f11;
drop function if exists f12;
drop function if exists f13;
drop function if exists f14;
drop function if exists f15;
create table t1 (i int primary key) engine=innodb;
insert into t1 values (1), (2), (3), (4), (5);
create table t2 (j int primary key) engine=innodb;
insert into t2 values (1), (2), (3), (4), (5);
create table t3 (k int primary key) engine=innodb;
insert into t3 values (1), (2), (3);
create table t4 (l int primary key) engine=innodb;
insert into t4 values (1);
create table t5 (l int primary key) engine=innodb;
insert into t5 values (1);
create view v1 as select i from t1;
create view v2 as select j from t2 where j in (select i from t1);
create procedure p1(k int) insert into t2 values (k);
create function f1() returns int
begin
declare j int;
select i from t1 where i = 1 into j;
return j;
end|
create function f2() returns int
begin
declare k int;
select i from t1 where i = 1 into k;
insert into t2 values (k + 5);
return 0;
end|
create function f3() returns int
begin
return (select i from t1 where i = 3);
end|
create function f4() returns int
begin
if (select i from t1 where i = 3) then
return 1;
else
return 0;
end if;
end|
create function f5() returns int
begin
insert into t2 values ((select i from t1 where i = 1) + 5);
return 0;
end|
create function f6() returns int
begin
declare k int;
select i from v1 where i = 1 into k;
return k;
end|
create function f7() returns int
begin
declare k int;
select j from v2 where j = 1 into k;
return k;
end|
create function f8() returns int
begin
declare k int;
select i from v1 where i = 1 into k;
insert into t2 values (k+5);
return k;
end|
create function f9() returns int
begin
update v2 set j=j+10 where j=1;
return 1;
end|
create function f10() returns int
begin
return f1();
end|
create function f11() returns int
begin
declare k int;
set k= f1();
insert into t2 values (k+5);
return k;
end|
create function f12(p int) returns int
begin
insert into t2 values (p);
return p;
end|
create function f13(p int) returns int
begin
return p;
end|
create procedure p2(inout p int)
begin
select i from t1 where i = 1 into p;
end|
create function f14() returns int
begin
declare k int;
call p2(k);
insert into t2 values (k+5);
return k;
end|
create function f15() returns int
begin
declare k int;
call p2(k);
return k;
end|
create trigger t4_bi before insert on t4 for each row
begin
declare k int;
select i from t1 where i=1 into k;
set new.l= k+1;
end|
create trigger t4_bu before update on t4 for each row
begin
if (select i from t1 where i=1) then
set new.l= 2;
end if;
end|
create trigger t4_bd before delete on t4 for each row
begin
if !(select i from v1 where i=1) then
signal sqlstate '45000';
end if;
end|
create trigger t5_bi before insert on t5 for each row
begin
set new.l= f1()+1;
end|
create trigger t5_bu before update on t5 for each row
begin
declare j int;
call p2(j);
set new.l= j + 1;
end|
#
# Set common variables to be used by scripts called below.
#
#
# 1. Statements that read tables and do not use subqueries.
#
#
# 1.1 Simple SELECT statement.
#
# No locks are necessary as this statement won't be written
# to the binary log and thanks to how MyISAM works SELECT
# will see version of the table prior to concurrent insert.
Success: 'select * from t1' doesn't take row locks on 't1'.
#
# 1.2 Multi-UPDATE statement.
#
# Has to take shared locks on rows in the table being read as this
# statement will be written to the binary log and therefore should
# be serialized with concurrent statements.
Success: 'update t2, t1 set j= j - 1 where i = j' takes shared row locks on 't1'.
#
# 1.3 Multi-DELETE statement.
#
# The above is true for this statement as well.
Success: 'delete t2 from t1, t2 where i = j' takes shared row locks on 't1'.
#
# 1.4 DESCRIBE statement.
#
# This statement does not really read data from the
# target table and thus does not take any lock on it.
# We check this for completeness of coverage.
Success: 'describe t1' doesn't take row locks on 't1'.
#
# 1.5 SHOW statements.
#
# The above is true for SHOW statements as well.
Success: 'show create table t1' doesn't take row locks on 't1'.
Success: 'show keys from t1' doesn't take row locks on 't1'.
#
# 2. Statements which read tables through subqueries.
#
#
# 2.1 CALL with a subquery.
#
# A strong lock is not necessary as this statement is not
# written to the binary log as a whole (it is written
# statement-by-statement) and thanks to MVCC we can always get
# versions of rows prior to the update that has locked them.
# But in practice InnoDB does locking reads for all statements
# other than SELECT (unless it is a READ-COMITTED mode or
# innodb_locks_unsafe_for_binlog is ON).
Success: 'call p1((select i + 5 from t1 where i = 1))' takes shared row locks on 't1'.
#
# 2.2 CREATE TABLE with a subquery.
#
# Has to take shared locks on rows in the table being read as
# this statement is written to the binary log and therefore
# should be serialized with concurrent statements.
Success: 'create table t0 engine=innodb select * from t1' takes shared row locks on 't1'.
drop table t0;
Success: 'create table t0 engine=innodb select j from t2 where j in (select i from t1)' takes shared row locks on 't1'.
drop table t0;
#
# 2.3 DELETE with a subquery.
#
# The above is true for this statement as well.
Success: 'delete from t2 where j in (select i from t1)' takes shared row locks on 't1'.
#
# 2.4 MULTI-DELETE with a subquery.
#
# Same is true for this statement as well.
Success: 'delete t2 from t3, t2 where k = j and j in (select i from t1)' takes shared row locks on 't1'.
#
# 2.5 DO with a subquery.
#
# In theory should not take row locks as it is not logged.
# In practice InnoDB takes shared row locks.
Success: 'do (select i from t1 where i = 1)' takes shared row locks on 't1'.
#
# 2.6 INSERT with a subquery.
#
# Has to take shared locks on rows in the table being read as
# this statement is written to the binary log and therefore
# should be serialized with concurrent statements.
Success: 'insert into t2 select i+5 from t1' takes shared row locks on 't1'.
Success: 'insert into t2 values ((select i+5 from t1 where i = 4))' takes shared row locks on 't1'.
#
# 2.7 LOAD DATA with a subquery.
#
# The above is true for this statement as well.
Success: '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'.
#
# 2.8 REPLACE with a subquery.
#
# Same is true for this statement as well.
Success: 'replace into t2 select i+5 from t1' takes shared row locks on 't1'.
Success: 'replace into t2 values ((select i+5 from t1 where i = 4))' takes shared row locks on 't1'.
#
# 2.9 SELECT with a subquery.
#
# Locks are not necessary as this statement is not written
# to the binary log and thanks to MVCC we can always get
# versions of rows prior to the update that has locked them.
#
# Also serves as a test case for bug #46947 "Embedded SELECT
# without FOR UPDATE is causing a lock".
Success: 'select * from t2 where j in (select i from t1)' doesn't take row locks on 't1'.
#
# 2.10 SET with a subquery.
#
# In theory should not require locking as it is not written
# to the binary log. In practice InnoDB acquires shared row
# locks.
Success: 'set @a:= (select i from t1 where i = 1)' takes shared row locks on 't1'.
#
# 2.11 SHOW with a subquery.
#
# Similarly to the previous case, in theory should not require locking
# as it is not written to the binary log. In practice InnoDB
# acquires shared row locks.
Success: 'show tables from test where Tables_in_test = 't2' and (select i from t1 where i = 1)' takes shared row locks on 't1'.
Success: 'show columns from t2 where (select i from t1 where i = 1)' takes shared row locks on 't1'.
#
# 2.12 UPDATE with a subquery.
#
# Has to take shared locks on rows in the table being read as
# this statement is written to the binary log and therefore
# should be serialized with concurrent statements.
Success: 'update t2 set j= j-10 where j in (select i from t1)' takes shared row locks on 't1'.
#
# 2.13 MULTI-UPDATE with a subquery.
#
# Same is true for this statement as well.
Success: 'update t2, t3 set j= j -10 where j=k and j in (select i from t1)' takes shared row locks on 't1'.
#
# 3. Statements which read tables through a view.
#
#
# 3.1 SELECT statement which uses some table through a view.
#
# Since this statement is not written to the binary log
# and old version of rows are accessible thanks to MVCC,
# no locking is necessary.
Success: 'select * from v1' doesn't take row locks on 't1'.
Success: 'select * from v2' doesn't take row locks on 't1'.
Success: 'select * from t2 where j in (select i from v1)' doesn't take row locks on 't1'.
Success: 'select * from t3 where k in (select j from v2)' doesn't take row locks on 't1'.
#
# 3.2 Statements which modify a table and use views.
#
# Since such statements are going to be written to the binary
# log they need to be serialized against concurrent statements
# and therefore should take shared row locks on data read.
Success: 'update t2 set j= j-10 where j in (select i from v1)' takes shared row locks on 't1'.
Success: 'update t3 set k= k-10 where k in (select j from v2)' takes shared row locks on 't1'.
Success: 'update t2, v1 set j= j-10 where j = i' takes shared row locks on 't1'.
Success: 'update v2 set j= j-10 where j = 3' takes shared row locks on 't1'.
#
# 4. Statements which read tables through stored functions.
#
#
# 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
# 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'.
Success: 'set @a:= f1()' takes shared row locks on 't1'.
#
# 4.2 INSERT (or other statement which modifies data) with
# a stored function which does not modify data and uses
# SELECT.
#
# Since such statement is written to the binary log it should
# be serialized with concurrent statements affecting the data
# it uses. Therefore it should take row locks on the data
# it reads.
Success: 'insert into t2 values (f1() + 5)' takes shared row locks on 't1'.
#
# 4.3 SELECT/SET with a stored function which
# reads and modifies data.
#
# Since a call to such function is written to the binary log,
# it should be serialized with concurrent statements affecting
# the data it uses. Hence, row locks on the data read
# should be taken.
Success: 'select f2()' takes shared row locks on 't1'.
Success: 'set @a:= f2()' takes shared row locks on 't1'.
#
# 4.4. SELECT/SET with a stored function which does not
# 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'.
Success: 'set @a:= f3()' takes shared row locks on 't1'.
Success: 'select f4()' takes shared row locks on 't1'.
Success: 'set @a:= f4()' takes shared row locks on 't1'.
#
# 4.5. INSERT (or other statement which modifies data) with
# a stored function which does not modify data and reads
# the table through a subselect in one of its control
# constructs.
#
# Since such statement is written to the binary log it should
# be serialized with concurrent statements affecting data it
# uses. Therefore it should take row locks on the data
# it reads.
Success: 'insert into t2 values (f3() + 5)' takes shared row locks on 't1'.
Success: 'insert into t2 values (f4() + 6)' takes shared row locks on 't1'.
#
# 4.6 SELECT/SET which uses a stored function with
# DML which reads a table via a subquery.
#
# Since call to such function is written to the binary log
# it should be serialized with concurrent statements.
# Hence reads should take row locks.
Success: 'select f5()' takes shared row locks on 't1'.
Success: 'set @a:= f5()' takes shared row locks on 't1'.
#
# 4.7 SELECT/SET which uses a stored function which
# 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'.
Success: 'set @a:= f6()' takes shared row locks on 't1'.
Success: 'select f7()' takes shared row locks on 't1'.
Success: 'set @a:= f7()' takes shared row locks on 't1'.
#
# 4.8 INSERT which uses stored function which
# doesn't modify data and reads a table
# through a view.
#
# Since such statement is written to the binary log and
# should be serialized with concurrent statements affecting
# the data it uses. Therefore it should take row locks on
# the rows it reads.
Success: 'insert into t3 values (f6() + 5)' takes shared row locks on 't1'.
Success: 'insert into t3 values (f7() + 5)' takes shared row locks on 't1'.
#
# 4.9 SELECT which uses a stored function which
# modifies data and reads tables through a view.
#
# Since a call to such function is written to the binary log
# it should be serialized with concurrent statements.
# Hence, reads should take row locks.
Success: 'select f8()' takes shared row locks on 't1'.
Success: 'select f9()' takes shared row locks on 't1'.
#
# 4.10 SELECT which uses stored function which doesn't modify
# 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'.
#
# 4.11 INSERT which uses a stored function which doesn't modify
# data and reads a table indirectly, by calling another
# function.
#
# Since such statement is written to the binary log, it should
# be serialized with concurrent statements affecting the data it
# uses. Therefore it should take row locks on data it reads.
Success: 'insert into t2 values (f10() + 5)' takes shared row locks on 't1'.
#
# 4.12 SELECT which uses a stored function which modifies
# data and reads a table indirectly, by calling another
# function.
#
# Since a call to such function is written to the binary log
# it should be serialized from concurrent statements.
# Hence, reads should take row locks.
Success: 'select f11()' takes shared row locks on 't1'.
#
# 4.13 SELECT that reads a table through a subquery passed
# as a parameter to a stored function which modifies
# data.
#
# Even though a call to this function is written to the
# binary log, values of its parameters are written as literals.
# So there is no need to acquire row locks on rows used in
# the subquery.
Success: 'select f12((select i+10 from t1 where i=1))' doesn't take row locks on 't1'.
#
# 4.14 INSERT that reads a table via a subquery passed
# as a parameter to a stored function which doesn't
# modify data.
#
# 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 row locks on the data it reads.
Success: 'insert into t2 values (f13((select i+10 from t1 where i=1)))' takes shared row locks on 't1'.
#
# 5. Statements that read tables through stored procedures.
#
#
# 5.1 CALL statement which reads a table via SELECT.
#
# Since neither this statement nor its components are
# written to the binary log, there is no need to take
# row locks on the data it reads.
Success: 'call p2(@a)' doesn't take row locks on 't1'.
#
# 5.2 Function that modifes data and uses CALL,
# which reads a table through SELECT.
#
# Since a call to such function is written to the binary
# log, it should be serialized with concurrent statements.
# Hence, in this case reads should take row locks on data.
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'.
#
# 5.4 INSERT which calls function which doesn't modify data and
# uses CALL statement which reads table through SELECT.
#
# Since such statement is written to the binary log it should
# be serialized with concurrent statements affecting data it
# uses. Therefore it should take row locks on data it reads.
Success: 'insert into t2 values (f15()+5)' takes shared row locks on 't1'.
#
# 6. Statements that use triggers.
#
#
# 6.1 Statement invoking a trigger that reads table via SELECT.
#
# 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 row locks on the data
# it reads.
Success: 'insert into t4 values (2)' takes shared row locks on 't1'.
#
# 6.2 Statement invoking a trigger that reads table through
# a subquery in a control construct.
#
# The above is true for this statement as well.
Success: 'update t4 set l= 2 where l = 1' takes shared row locks on 't1'.
#
# 6.3 Statement invoking a trigger that reads a table through
# a view.
#
# And for this statement.
Success: 'delete from t4 where l = 1' takes shared row locks on 't1'.
#
# 6.4 Statement invoking a trigger that reads a table through
# a stored function.
#
# And for this statement.
Success: 'insert into t5 values (2)' takes shared row locks on 't1'.
#
# 6.5 Statement invoking a trigger that reads a table through
# stored procedure.
#
# And for this statement.
Success: 'update t5 set l= 2 where l = 1' takes shared row locks on 't1'.
# Clean-up.
drop function f1;
drop function f2;
drop function f3;
drop function f4;
drop function f5;
drop function f6;
drop function f7;
drop function f8;
drop function f9;
drop function f10;
drop function f11;
drop function f12;
drop function f13;
drop function f14;
drop function f15;
drop view v1, v2;
drop procedure p1;
drop procedure p2;
drop table t1, t2, t3, t4, t5;

View File

@ -1,4 +1,596 @@
#
# Test how we handle locking in various cases when
# we read data from MyISAM tables.
#
# In this test we mostly check that the SQL-layer correctly
# determines the type of thr_lock.c lock for a table being
# read.
# I.e. that it disallows concurrent inserts when the statement
# is going to be written to the binary log and therefore
# should be serialized, and allows concurrent inserts when
# such serialization is not necessary (e.g. when
# the statement is not written to binary log).
#
# Force concurrent inserts to be performed even if the table
# has gaps. This allows to simplify clean up in scripts
# used below (instead of backing up table being inserted
# into and then restoring it from backup at the end of the
# script we can simply delete rows which were inserted).
set @old_concurrent_insert= @@global.concurrent_insert;
set @@global.concurrent_insert= 2;
select @@global.concurrent_insert;
@@global.concurrent_insert
ALWAYS
# Prepare playground by creating tables, views,
# routines and triggers used in tests.
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 function if exists f1;
drop function if exists f2;
drop function if exists f3;
drop function if exists f4;
drop function if exists f5;
drop function if exists f6;
drop function if exists f7;
drop function if exists f8;
drop function if exists f9;
drop function if exists f10;
drop function if exists f11;
drop function if exists f12;
drop function if exists f13;
drop function if exists f14;
drop function if exists f15;
create table t1 (i int primary key);
insert into t1 values (1), (2), (3), (4), (5);
create table t2 (j int primary key);
insert into t2 values (1), (2), (3), (4), (5);
create table t3 (k int primary key);
insert into t3 values (1), (2), (3);
create table t4 (l int primary key);
insert into t4 values (1);
create table t5 (l int primary key);
insert into t5 values (1);
create view v1 as select i from t1;
create view v2 as select j from t2 where j in (select i from t1);
create procedure p1(k int) insert into t2 values (k);
create function f1() returns int
begin
declare j int;
select i from t1 where i = 1 into j;
return j;
end|
create function f2() returns int
begin
declare k int;
select i from t1 where i = 1 into k;
insert into t2 values (k + 5);
return 0;
end|
create function f3() returns int
begin
return (select i from t1 where i = 3);
end|
create function f4() returns int
begin
if (select i from t1 where i = 3) then
return 1;
else
return 0;
end if;
end|
create function f5() returns int
begin
insert into t2 values ((select i from t1 where i = 1) + 5);
return 0;
end|
create function f6() returns int
begin
declare k int;
select i from v1 where i = 1 into k;
return k;
end|
create function f7() returns int
begin
declare k int;
select j from v2 where j = 1 into k;
return k;
end|
create function f8() returns int
begin
declare k int;
select i from v1 where i = 1 into k;
insert into t2 values (k+5);
return k;
end|
create function f9() returns int
begin
update v2 set j=j+10 where j=1;
return 1;
end|
create function f10() returns int
begin
return f1();
end|
create function f11() returns int
begin
declare k int;
set k= f1();
insert into t2 values (k+5);
return k;
end|
create function f12(p int) returns int
begin
insert into t2 values (p);
return p;
end|
create function f13(p int) returns int
begin
return p;
end|
create procedure p2(inout p int)
begin
select i from t1 where i = 1 into p;
end|
create function f14() returns int
begin
declare k int;
call p2(k);
insert into t2 values (k+5);
return k;
end|
create function f15() returns int
begin
declare k int;
call p2(k);
return k;
end|
create trigger t4_bi before insert on t4 for each row
begin
declare k int;
select i from t1 where i=1 into k;
set new.l= k+1;
end|
create trigger t4_bu before update on t4 for each row
begin
if (select i from t1 where i=1) then
set new.l= 2;
end if;
end|
create trigger t4_bd before delete on t4 for each row
begin
if !(select i from v1 where i=1) then
signal sqlstate '45000';
end if;
end|
create trigger t5_bi before insert on t5 for each row
begin
set new.l= f1()+1;
end|
create trigger t5_bu before update on t5 for each row
begin
declare j int;
call p2(j);
set new.l= j + 1;
end|
#
# Set common variables to be used by the scripts
# called below.
#
# Switch to connection 'con1'.
# Cache all functions used in the tests below so statements
# calling them won't need to open and lock mysql.proc table
# and we can assume that each statement locks its tables
# once during its execution.
show create procedure p1;
show create procedure p2;
show create function f1;
show create function f2;
show create function f3;
show create function f4;
show create function f5;
show create function f6;
show create function f7;
show create function f8;
show create function f9;
show create function f10;
show create function f11;
show create function f12;
show create function f13;
show create function f14;
show create function f15;
# Switch back to connection 'default'.
#
# 1. Statements that read tables and do not use subqueries.
#
#
# 1.1 Simple SELECT statement.
#
# No locks are necessary as this statement won't be written
# to the binary log and thanks to how MyISAM works SELECT
# will see version of the table prior to concurrent insert.
Success: 'select * from t1' allows concurrent inserts into 't1'.
#
# 1.2 Multi-UPDATE statement.
#
# Has to take shared locks on rows in the table being read as this
# statement will be written to the binary log and therefore should
# be serialized with concurrent statements.
Success: 'update t2, t1 set j= j - 1 where i = j' doesn't allow concurrent inserts into 't1'.
#
# 1.3 Multi-DELETE statement.
#
# The above is true for this statement as well.
Success: 'delete t2 from t1, t2 where i = j' doesn't allow concurrent inserts into 't1'.
#
# 1.4 DESCRIBE statement.
#
# This statement does not really read data from the
# target table and thus does not take any lock on it.
# We check this for completeness of coverage.
lock table t1 write;
# Switching to connection 'con1'.
# This statement should not be blocked.
describe t1;
# Switching to connection 'default'.
unlock tables;
#
# 1.5 SHOW statements.
#
# The above is true for SHOW statements as well.
lock table t1 write;
# Switching to connection 'con1'.
# These statements should not be blocked.
show keys from t1;
# Switching to connection 'default'.
unlock tables;
#
# 2. Statements which read tables through subqueries.
#
#
# 2.1 CALL with a subquery.
#
# A strong lock is not necessary as this statement is not
# written to the binary log as a whole (it is written
# statement-by-statement).
Success: 'call p1((select i + 5 from t1 where i = 1))' allows concurrent inserts into 't1'.
#
# 2.2 CREATE TABLE with a subquery.
#
# Has to take a strong lock on the table being read as
# this statement is written to the binary log and therefore
# should be serialized with concurrent statements.
Success: 'create table t0 select * from t1' doesn't allow concurrent inserts into 't1'.
drop table t0;
Success: 'create table t0 select j from t2 where j in (select i from t1)' doesn't allow concurrent inserts into 't1'.
drop table t0;
#
# 2.3 DELETE with a subquery.
#
# The above is true for this statement as well.
Success: 'delete from t2 where j in (select i from t1)' doesn't allow concurrent inserts into 't1'.
#
# 2.4 MULTI-DELETE with a subquery.
#
# Same is true for this statement as well.
Success: 'delete t2 from t3, t2 where k = j and j in (select i from t1)' doesn't allow concurrent inserts into 't1'.
#
# 2.5 DO with a subquery.
#
# A strong lock is not necessary as it is not logged.
Success: 'do (select i from t1 where i = 1)' allows concurrent inserts into 't1'.
#
# 2.6 INSERT with a subquery.
#
# Has to take a strong lock on the table being read as
# this statement is written to the binary log and therefore
# should be serialized with concurrent inserts.
Success: 'insert into t2 select i+5 from t1' doesn't allow concurrent inserts into 't1'.
Success: 'insert into t2 values ((select i+5 from t1 where i = 4))' doesn't allow concurrent inserts into 't1'.
#
# 2.7 LOAD DATA with a subquery.
#
# The above is true for this statement as well.
Success: 'load data infile '../../std_data/rpl_loaddata.dat' into table t2 (@a, @b) set j= @b + (select i from t1 where i = 1)' doesn't allow concurrent inserts into 't1'.
#
# 2.8 REPLACE with a subquery.
#
# Same is true for this statement as well.
Success: 'replace into t2 select i+5 from t1' doesn't allow concurrent inserts into 't1'.
Success: 'replace into t2 values ((select i+5 from t1 where i = 4))' doesn't allow concurrent inserts into 't1'.
#
# 2.9 SELECT with a subquery.
#
# Strong locks are not necessary as this statement is not written
# to the binary log and thanks to how MyISAM works this statement
# sees a version of the table prior to the concurrent insert.
Success: 'select * from t2 where j in (select i from t1)' allows concurrent inserts into 't1'.
#
# 2.10 SET with a subquery.
#
# The same is true for this statement as well.
Success: 'set @a:= (select i from t1 where i = 1)' allows concurrent inserts into 't1'.
#
# 2.11 SHOW with a subquery.
#
# And for this statement too.
Success: 'show tables from test where Tables_in_test = 't2' and (select i from t1 where i = 1)' allows concurrent inserts into 't1'.
Success: 'show columns from t2 where (select i from t1 where i = 1)' allows concurrent inserts into 't1'.
#
# 2.12 UPDATE with a subquery.
#
# Has to take a strong lock on the table being read as
# this statement is written to the binary log and therefore
# should be serialized with concurrent inserts.
Success: 'update t2 set j= j-10 where j in (select i from t1)' doesn't allow concurrent inserts into 't1'.
#
# 2.13 MULTI-UPDATE with a subquery.
#
# Same is true for this statement as well.
Success: 'update t2, t3 set j= j -10 where j=k and j in (select i from t1)' doesn't allow concurrent inserts into 't1'.
#
# 3. Statements which read tables through a view.
#
#
# 3.1 SELECT statement which uses some table through a view.
#
# Since this statement is not written to the binary log and
# an old version of the table is accessible thanks to how MyISAM
# handles concurrent insert, no locking is necessary.
Success: 'select * from v1' allows concurrent inserts into 't1'.
Success: 'select * from v2' allows concurrent inserts into 't1'.
Success: 'select * from t2 where j in (select i from v1)' allows concurrent inserts into 't1'.
Success: 'select * from t3 where k in (select j from v2)' allows concurrent inserts into 't1'.
#
# 3.2 Statements which modify a table and use views.
#
# Since such statements are going to be written to the binary
# log they need to be serialized against concurrent statements
# and therefore should take strong locks on the data read.
Success: 'update t2 set j= j-10 where j in (select i from v1)' doesn't allow concurrent inserts into 't1'.
Success: 'update t3 set k= k-10 where k in (select j from v2)' doesn't allow concurrent inserts into 't1'.
Success: 'update t2, v1 set j= j-10 where j = i' doesn't allow concurrent inserts into 't1'.
Success: 'update v2 set j= j-10 where j = 3' doesn't allow concurrent inserts into 't1'.
#
# 4. Statements which read tables through stored functions.
#
#
# 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
# 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'.
#
# 4.2 INSERT (or other statement which modifies data) with
# a stored function which does not modify data and uses
# SELECT.
#
# Since such statement is written to the binary log it should
# be serialized with concurrent statements affecting the data
# it uses. Therefore it should take strong lock on the data
# it reads.
Success: 'insert into t2 values (f1() + 5)' doesn't allow concurrent inserts into 't1'.
#
# 4.3 SELECT/SET with a stored function which
# reads and modifies data.
#
# Since a call to such function is written to the binary log,
# it should be serialized with concurrent statements affecting
# the data it uses. Hence, a strong lock on the data read
# should be taken.
Success: 'select f2()' doesn't allow concurrent inserts into 't1'.
Success: 'set @a:= f2()' doesn't allow concurrent inserts into 't1'.
#
# 4.4. SELECT/SET with a stored function which does not
# 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'.
#
# 4.5. INSERT (or other statement which modifies data) with
# a stored function which does not modify data and reads
# the table through a subselect in one of its control
# constructs.
#
# Since such statement is written to the binary log it should
# be serialized with concurrent statements affecting data it
# uses. Therefore it should take a strong lock on the data
# it reads.
Success: 'insert into t2 values (f3() + 5)' doesn't allow concurrent inserts into 't1'.
Success: 'insert into t2 values (f4() + 6)' doesn't allow concurrent inserts into 't1'.
#
# 4.6 SELECT/SET which uses a stored function with
# DML which reads a table via a subquery.
#
# Since call to such function is written to the binary log
# it should be serialized with concurrent statements.
# Hence reads should take a strong lock.
Success: 'select f5()' doesn't allow concurrent inserts into 't1'.
Success: 'set @a:= f5()' doesn't allow concurrent inserts into 't1'.
#
# 4.7 SELECT/SET which uses a stored function which
# 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'.
#
# 4.8 INSERT which uses stored function which
# doesn't modify data and reads a table
# through a view.
#
# Since such statement is written to the binary log and
# should be serialized with concurrent statements affecting
# the data it uses. Therefore it should take a strong lock on
# the table it reads.
Success: 'insert into t3 values (f6() + 5)' doesn't allow concurrent inserts into 't1'.
Success: 'insert into t3 values (f7() + 5)' doesn't allow concurrent inserts into 't1'.
#
# 4.9 SELECT which uses a stored function which
# modifies data and reads tables through a view.
#
# Since a call to such function is written to the binary log
# it should be serialized with concurrent statements.
# Hence, reads should take strong locks.
Success: 'select f8()' doesn't allow concurrent inserts into 't1'.
Success: 'select f9()' doesn't allow concurrent inserts into 't1'.
#
# 4.10 SELECT which uses a stored function which doesn't modify
# 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'.
#
# 4.11 INSERT which uses a stored function which doesn't modify
# data and reads a table indirectly, by calling another
# function.
#
# Since such 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 data it reads.
Success: 'insert into t2 values (f10() + 5)' doesn't allow concurrent inserts into 't1'.
#
# 4.12 SELECT which uses a stored function which modifies
# data and reads a table indirectly, by calling another
# function.
#
# Since a call to such function is written to the binary log
# it should be serialized from concurrent statements.
# Hence, read should take a strong lock.
Success: 'select f11()' doesn't allow concurrent inserts into 't1'.
#
# 4.13 SELECT that reads a table through a subquery passed
# as a parameter to a stored function which modifies
# data.
#
# Even though a call to this function is written to the
# binary log, values of its parameters are written as literals.
# So there is no need to acquire strong locks for tables used in
# the subquery.
Success: 'select f12((select i+10 from t1 where i=1))' allows concurrent inserts into 't1'.
#
# 4.14 INSERT that reads a table via a subquery passed
# as a parameter to a stored function which doesn't
# modify data.
#
# 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: 'insert into t2 values (f13((select i+10 from t1 where i=1)))' doesn't allow concurrent inserts into 't1'.
#
# 5. Statements that read tables through stored procedures.
#
#
# 5.1 CALL statement which reads a table via SELECT.
#
# Since neither this statement nor its components are
# written to the binary log, there is no need to take
# strong locks on the data it reads.
Success: 'call p2(@a)' allows concurrent inserts into 't1'.
#
# 5.2 Function that modifes data and uses CALL,
# which reads a table through SELECT.
#
# Since a call to such function is written to the binary
# log, it should be serialized with concurrent statements.
# Hence, in this case reads should take strong locks on data.
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'.
#
# 5.4 INSERT which calls function which doesn't modify data and
# uses CALL statement which reads table through SELECT.
#
# Since such statement is written to the binary log it should
# be serialized with concurrent statements affecting data it
# uses. Therefore it should take strong locks on data it reads.
Success: 'insert into t2 values (f15()+5)' doesn't allow concurrent inserts into 't1'.
#
# 6. Statements that use triggers.
#
#
# 6.1 Statement invoking a trigger that reads table via SELECT.
#
# 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: 'insert into t4 values (2)' doesn't allow concurrent inserts into 't1'.
#
# 6.2 Statement invoking a trigger that reads table through
# a subquery in a control construct.
#
# The above is true for this statement as well.
Success: 'update t4 set l= 2 where l = 1' doesn't allow concurrent inserts into 't1'.
#
# 6.3 Statement invoking a trigger that reads a table through
# a view.
#
# And for this statement.
Success: 'delete from t4 where l = 1' doesn't allow concurrent inserts into 't1'.
#
# 6.4 Statement invoking a trigger that reads a table through
# a stored function.
#
# And for this statement.
Success: 'insert into t5 values (2)' doesn't allow concurrent inserts into 't1'.
#
# 6.5 Statement invoking a trigger that reads a table through
# stored procedure.
#
# And for this statement.
Success: 'update t5 set l= 2 where l = 1' doesn't allow concurrent inserts into 't1'.
# Clean-up.
drop function f1;
drop function f2;
drop function f3;
drop function f4;
drop function f5;
drop function f6;
drop function f7;
drop function f8;
drop function f9;
drop function f10;
drop function f11;
drop function f12;
drop function f13;
drop function f14;
drop function f15;
drop view v1, v2;
drop procedure p1;
drop procedure p2;
drop table t1, t2, t3, t4, t5;
set @@global.concurrent_insert= @old_concurrent_insert;
#
# Test for bug #45143 "All connections hang on concurrent ALTER TABLE".
#
# Concurrent execution of statements which required weak write lock

View File

@ -24,7 +24,7 @@ START TRANSACTION;
connection thread2;
--echo # in thread2
REPLACE INTO t2 VALUES (-17);
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d);
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d) LOCK IN SHARE MODE;
connection thread1;
--echo # in thread1
@ -37,14 +37,14 @@ START TRANSACTION;
REPLACE INTO t1(a,b) VALUES (65,-50);
REPLACE INTO t2 VALUES (-91);
send;
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); #waits
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d) LOCK IN SHARE MODE; #waits
connection thread1;
--echo # in thread1
--echo # should not crash
--error ER_LOCK_DEADLOCK
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); #crashes
SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d) LOCK IN SHARE MODE; #crashes
connection thread2;
--echo # in thread2

View File

@ -1668,3 +1668,66 @@ CREATE TABLE t1 LIKE t2;
DROP TABLE t2;
DROP TABLE t1;
--echo #
--echo # Bug #48800 CREATE TABLE t...SELECT fails if t is a
--echo # temporary table
--echo #
CREATE TEMPORARY TABLE t1 (a INT);
CREATE TABLE t1 (a INT);
CREATE TEMPORARY TABLE t2 (a INT);
CREATE VIEW t2 AS SELECT 1;
CREATE TABLE t3 (a INT);
CREATE TEMPORARY TABLE t3 SELECT 1;
CREATE TEMPORARY TABLE t4 (a INT);
CREATE TABLE t4 AS SELECT 1;
DROP TEMPORARY TABLE t1, t2, t3, t4;
DROP TABLE t1, t3, t4;
DROP VIEW t2;
--echo #
--echo # Bug #49193 CREATE TABLE reacts differently depending
--echo # on whether data is selected or not
--echo #
CREATE TEMPORARY TABLE t2 (ID INT);
INSERT INTO t2 VALUES (1),(2),(3);
# Case 1 -- did not fail
CREATE TEMPORARY TABLE t1 (ID INT);
CREATE TABLE IF NOT EXISTS t1 (ID INT);
INSERT INTO t1 SELECT * FROM t2;
SELECT * FROM t1;
DROP TEMPORARY TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
# Case 2 -- The DROP TABLE t1 failed with
# Table 'test.t1' doesn't exist in the SELECT *
# as the (permanent) table was not created
CREATE TEMPORARY TABLE t1 (ID INT);
CREATE TABLE IF NOT EXISTS t1 SELECT * FROM t2;
SELECT * FROM t1;
DROP TEMPORARY TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
# Case 3 -- The CREATE TABLE failed with
# Table 't1' already exists
CREATE TEMPORARY TABLE t1 (ID INT);
CREATE TABLE t1 SELECT * FROM t2;
SELECT * FROM t1;
DROP TEMPORARY TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
DROP TEMPORARY TABLE t2;

View File

@ -58,9 +58,10 @@ drop table t1;
--echo #
--echo # Old lock method (where LOCK TABLE was ignored by InnoDB) no longer
--echo # works due to fix for bugs #46272 "MySQL 5.4.4, new MDL: unnecessary
--echo # deadlock" and bug #37346 "innodb does not detect deadlock between
--echo # update and alter table".
--echo # works when LOCK TABLE ... WRITE is used due to fix for bugs #46272
--echo # "MySQL 5.4.4, new MDL: unnecessary and bug #37346 "innodb does not
--echo # detect deadlock between update and alter table". But it still works
--echo # for LOCK TABLE ... READ.
--echo #
set @@innodb_table_locks=0;
@ -102,6 +103,26 @@ unlock tables;
--echo # Connection 'con1'.
connection con1;
select * from t1 where id = 0 for update;
--echo # Connection 'con2'.
connection con2;
--echo # The below statement should not be blocked as LOCK TABLES ... READ
--echo # does not take strong SQL-level lock on t1. SELECTs which do not
--echo # conflict with transaction in the first connections should not be
--echo # blocked.
lock table t1 read;
select * from t1;
select * from t1 where id = 1 lock in share mode;
unlock tables;
select * from t1;
commit;
--echo # Connection 'con1'.
connection con1;
commit;
drop table t1;
# End of 4.1 tests

View File

@ -170,6 +170,45 @@ connection default;
disconnect con2;
DROP TABLE t1;
--echo #
--echo # Bug#53798 OPTIMIZE TABLE breaks repeatable read
--echo #
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
CREATE TABLE t1 (a INT) engine=innodb;
INSERT INTO t1 VALUES (1), (2), (3);
--echo # Connection con1
connect (con1, localhost, root);
START TRANSACTION WITH CONSISTENT SNAPSHOT;
SELECT * FROM t1;
--echo # Connection default
connection default;
--echo # This should block
--echo # Sending:
--send OPTIMIZE TABLE t1
--echo # Connection con1
connection con1;
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
WHERE state='Waiting for table' AND info='OPTIMIZE TABLE t1';
--source include/wait_condition.inc
SELECT * FROM t1;
COMMIT;
--echo # Connection default
connection default;
--echo # Reaping OPTIMIZE TABLE t1
--reap
disconnect con1;
DROP TABLE t1;
# Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc

View File

@ -0,0 +1,765 @@
# This test covers behavior for InnoDB tables.
--source include/have_innodb.inc
# This test requires statement/mixed mode binary logging.
# Row-based mode puts weaker serializability requirements
# so weaker locks are acquired for it.
--source include/have_binlog_format_mixed_or_statement.inc
# Save the initial number of concurrent sessions.
--source include/count_sessions.inc
--echo #
--echo # Test how do we handle locking in various cases when
--echo # we read data from InnoDB tables.
--echo #
--echo # In fact by performing this test we check two things:
--echo # 1) That SQL-layer correctly determine type of thr_lock.c
--echo # lock to be acquired/passed to InnoDB engine.
--echo # 2) That InnoDB engine correctly interprets this lock
--echo # type and takes necessary row locks or does not
--echo # take them if they are not necessary.
--echo #
--echo # This test makes sense only in REPEATABLE-READ mode as
--echo # in SERIALIZABLE mode all statements that read data take
--echo # shared lock on them to enforce its semantics.
select @@session.tx_isolation;
--echo # Prepare playground by creating tables, views,
--echo # routines and triggers used in tests.
connect (con1, localhost, root,,);
connection default;
--disable_warnings
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 function if exists f1;
drop function if exists f2;
drop function if exists f3;
drop function if exists f4;
drop function if exists f5;
drop function if exists f6;
drop function if exists f7;
drop function if exists f8;
drop function if exists f9;
drop function if exists f10;
drop function if exists f11;
drop function if exists f12;
drop function if exists f13;
drop function if exists f14;
drop function if exists f15;
--enable_warnings
create table t1 (i int primary key) engine=innodb;
insert into t1 values (1), (2), (3), (4), (5);
create table t2 (j int primary key) engine=innodb;
insert into t2 values (1), (2), (3), (4), (5);
create table t3 (k int primary key) engine=innodb;
insert into t3 values (1), (2), (3);
create table t4 (l int primary key) engine=innodb;
insert into t4 values (1);
create table t5 (l int primary key) engine=innodb;
insert into t5 values (1);
create view v1 as select i from t1;
create view v2 as select j from t2 where j in (select i from t1);
create procedure p1(k int) insert into t2 values (k);
delimiter |;
create function f1() returns int
begin
declare j int;
select i from t1 where i = 1 into j;
return j;
end|
create function f2() returns int
begin
declare k int;
select i from t1 where i = 1 into k;
insert into t2 values (k + 5);
return 0;
end|
create function f3() returns int
begin
return (select i from t1 where i = 3);
end|
create function f4() returns int
begin
if (select i from t1 where i = 3) then
return 1;
else
return 0;
end if;
end|
create function f5() returns int
begin
insert into t2 values ((select i from t1 where i = 1) + 5);
return 0;
end|
create function f6() returns int
begin
declare k int;
select i from v1 where i = 1 into k;
return k;
end|
create function f7() returns int
begin
declare k int;
select j from v2 where j = 1 into k;
return k;
end|
create function f8() returns int
begin
declare k int;
select i from v1 where i = 1 into k;
insert into t2 values (k+5);
return k;
end|
create function f9() returns int
begin
update v2 set j=j+10 where j=1;
return 1;
end|
create function f10() returns int
begin
return f1();
end|
create function f11() returns int
begin
declare k int;
set k= f1();
insert into t2 values (k+5);
return k;
end|
create function f12(p int) returns int
begin
insert into t2 values (p);
return p;
end|
create function f13(p int) returns int
begin
return p;
end|
create procedure p2(inout p int)
begin
select i from t1 where i = 1 into p;
end|
create function f14() returns int
begin
declare k int;
call p2(k);
insert into t2 values (k+5);
return k;
end|
create function f15() returns int
begin
declare k int;
call p2(k);
return k;
end|
create trigger t4_bi before insert on t4 for each row
begin
declare k int;
select i from t1 where i=1 into k;
set new.l= k+1;
end|
create trigger t4_bu before update on t4 for each row
begin
if (select i from t1 where i=1) then
set new.l= 2;
end if;
end|
create trigger t4_bd before delete on t4 for each row
begin
if !(select i from v1 where i=1) then
signal sqlstate '45000';
end if;
end|
create trigger t5_bi before insert on t5 for each row
begin
set new.l= f1()+1;
end|
create trigger t5_bu before update on t5 for each row
begin
declare j int;
call p2(j);
set new.l= j + 1;
end|
delimiter ;|
--echo #
--echo # Set common variables to be used by scripts called below.
--echo #
let $con_aux= con1;
let $table= t1;
--echo #
--echo # 1. Statements that read tables and do not use subqueries.
--echo #
--echo #
--echo # 1.1 Simple SELECT statement.
--echo #
--echo # No locks are necessary as this statement won't be written
--echo # to the binary log and thanks to how MyISAM works SELECT
--echo # will see version of the table prior to concurrent insert.
let $statement= select * from t1;
--source include/check_no_row_lock.inc
--echo #
--echo # 1.2 Multi-UPDATE statement.
--echo #
--echo # Has to take shared locks on rows in the table being read as this
--echo # statement will be written to the binary log and therefore should
--echo # be serialized with concurrent statements.
let $statement= update t2, t1 set j= j - 1 where i = j;
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 1.3 Multi-DELETE statement.
--echo #
--echo # The above is true for this statement as well.
let $statement= delete t2 from t1, t2 where i = j;
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 1.4 DESCRIBE statement.
--echo #
--echo # This statement does not really read data from the
--echo # target table and thus does not take any lock on it.
--echo # We check this for completeness of coverage.
let $statement= describe t1;
--source include/check_no_row_lock.inc
--echo #
--echo # 1.5 SHOW statements.
--echo #
--echo # The above is true for SHOW statements as well.
let $statement= show create table t1;
--source include/check_no_row_lock.inc
let $statement= show keys from t1;
--source include/check_no_row_lock.inc
--echo #
--echo # 2. Statements which read tables through subqueries.
--echo #
--echo #
--echo # 2.1 CALL with a subquery.
--echo #
--echo # A strong lock is not necessary as this statement is not
--echo # written to the binary log as a whole (it is written
--echo # statement-by-statement) and thanks to MVCC we can always get
--echo # versions of rows prior to the update that has locked them.
--echo # But in practice InnoDB does locking reads for all statements
--echo # other than SELECT (unless it is a READ-COMITTED mode or
--echo # innodb_locks_unsafe_for_binlog is ON).
let $statement= call p1((select i + 5 from t1 where i = 1));
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.2 CREATE TABLE with a subquery.
--echo #
--echo # Has to take shared locks on rows in the table being read as
--echo # this statement is written to the binary log and therefore
--echo # should be serialized with concurrent statements.
let $statement= create table t0 engine=innodb select * from t1;
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
drop table t0;
let $statement= create table t0 engine=innodb select j from t2 where j in (select i from t1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
drop table t0;
--echo #
--echo # 2.3 DELETE with a subquery.
--echo #
--echo # The above is true for this statement as well.
let $statement= delete from t2 where j in (select i from t1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.4 MULTI-DELETE with a subquery.
--echo #
--echo # Same is true for this statement as well.
let $statement= delete t2 from t3, t2 where k = j and j in (select i from t1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.5 DO with a subquery.
--echo #
--echo # In theory should not take row locks as it is not logged.
--echo # In practice InnoDB takes shared row locks.
let $statement= do (select i from t1 where i = 1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.6 INSERT with a subquery.
--echo #
--echo # Has to take shared locks on rows in the table being read as
--echo # this statement is written to the binary log and therefore
--echo # should be serialized with concurrent statements.
let $statement= insert into t2 select i+5 from t1;
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
let $statement= insert into t2 values ((select i+5 from t1 where i = 4));
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.7 LOAD DATA with a subquery.
--echo #
--echo # The above is true for this statement as well.
let $statement= load data infile '../../std_data/rpl_loaddata.dat' into table t2 (@a, @b) set j= @b + (select i from t1 where i = 1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.8 REPLACE with a subquery.
--echo #
--echo # Same is true for this statement as well.
let $statement= replace into t2 select i+5 from t1;
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
let $statement= replace into t2 values ((select i+5 from t1 where i = 4));
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.9 SELECT with a subquery.
--echo #
--echo # Locks are not necessary as this statement is not written
--echo # to the binary log and thanks to MVCC we can always get
--echo # versions of rows prior to the update that has locked them.
--echo #
--echo # Also serves as a test case for bug #46947 "Embedded SELECT
--echo # without FOR UPDATE is causing a lock".
let $statement= select * from t2 where j in (select i from t1);
--source include/check_no_row_lock.inc
--echo #
--echo # 2.10 SET with a subquery.
--echo #
--echo # In theory should not require locking as it is not written
--echo # to the binary log. In practice InnoDB acquires shared row
--echo # locks.
let $statement= set @a:= (select i from t1 where i = 1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.11 SHOW with a subquery.
--echo #
--echo # Similarly to the previous case, in theory should not require locking
--echo # as it is not written to the binary log. In practice InnoDB
--echo # acquires shared row locks.
let $statement= show tables from test where Tables_in_test = 't2' and (select i from t1 where i = 1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
let $statement= show columns from t2 where (select i from t1 where i = 1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.12 UPDATE with a subquery.
--echo #
--echo # Has to take shared locks on rows in the table being read as
--echo # this statement is written to the binary log and therefore
--echo # should be serialized with concurrent statements.
let $statement= update t2 set j= j-10 where j in (select i from t1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 2.13 MULTI-UPDATE with a subquery.
--echo #
--echo # Same is true for this statement as well.
let $statement= update t2, t3 set j= j -10 where j=k and j in (select i from t1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 3. Statements which read tables through a view.
--echo #
--echo #
--echo # 3.1 SELECT statement which uses some table through a view.
--echo #
--echo # Since this statement is not written to the binary log
--echo # and old version of rows are accessible thanks to MVCC,
--echo # no locking is necessary.
let $statement= select * from v1;
--source include/check_no_row_lock.inc
let $statement= select * from v2;
--source include/check_no_row_lock.inc
let $statement= select * from t2 where j in (select i from v1);
--source include/check_no_row_lock.inc
let $statement= select * from t3 where k in (select j from v2);
--source include/check_no_row_lock.inc
--echo #
--echo # 3.2 Statements which modify a table and use views.
--echo #
--echo # Since such statements are going to be written to the binary
--echo # log they need to be serialized against concurrent statements
--echo # and therefore should take shared row locks on data read.
let $statement= update t2 set j= j-10 where j in (select i from v1);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
let $statement= update t3 set k= k-10 where k in (select j from v2);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
let $statement= update t2, v1 set j= j-10 where j = i;
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
let $statement= update v2 set j= j-10 where j = 3;
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4. Statements which read tables through stored functions.
--echo #
--echo #
--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 # 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.
let $statement= select f1();
let $wait_statement= select i from t1 where i = 1 into j;
--source include/check_shared_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
--echo #
--echo # 4.2 INSERT (or other statement which modifies data) with
--echo # a stored function which does not modify data and uses
--echo # SELECT.
--echo #
--echo # Since such statement is written to the binary log it should
--echo # be serialized with concurrent statements affecting the data
--echo # it uses. Therefore it should take row locks on the data
--echo # it reads.
let $statement= insert into t2 values (f1() + 5);
let $wait_statement= select i from t1 where i = 1 into j;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.3 SELECT/SET with a stored function which
--echo # reads and modifies data.
--echo #
--echo # Since a call to such function is written to the binary log,
--echo # it should be serialized with concurrent statements affecting
--echo # the data it uses. Hence, row locks on the data read
--echo # should be taken.
let $statement= select f2();
let $wait_statement= select i from t1 where i = 1 into k;
--source include/check_shared_row_lock.inc
let $statement= set @a:= f2();
let $wait_statement= select i from t1 where i = 1 into k;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.4. SELECT/SET with a stored function which does not
--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.
let $statement= select f3();
let $wait_statement= $statement;
--source include/check_shared_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
let $statement= set @a:= f4();
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.5. INSERT (or other statement which modifies data) with
--echo # a stored function which does not modify data and reads
--echo # the table through a subselect in one of its control
--echo # constructs.
--echo #
--echo # Since such statement is written to the binary log it should
--echo # be serialized with concurrent statements affecting data it
--echo # uses. Therefore it should take row locks on the data
--echo # it reads.
let $statement= insert into t2 values (f3() + 5);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
let $statement= insert into t2 values (f4() + 6);
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.6 SELECT/SET which uses a stored function with
--echo # DML which reads a table via a subquery.
--echo #
--echo # Since call to such function is written to the binary log
--echo # it should be serialized with concurrent statements.
--echo # Hence reads should take row locks.
let $statement= select f5();
let $wait_statement= insert into t2 values ((select i from t1 where i = 1) + 5);
--source include/check_shared_row_lock.inc
let $statement= set @a:= f5();
let $wait_statement= insert into t2 values ((select i from t1 where i = 1) + 5);
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.7 SELECT/SET which uses a stored function which
--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.
let $statement= select f6();
let $wait_statement= select i from v1 where i = 1 into k;
--source include/check_shared_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
let $statement= set @a:= f7();
let $wait_statement= select j from v2 where j = 1 into k;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.8 INSERT which uses stored function which
--echo # doesn't modify data and reads a table
--echo # through a view.
--echo #
--echo # Since such statement is written to the binary log and
--echo # should be serialized with concurrent statements affecting
--echo # the data it uses. Therefore it should take row locks on
--echo # the rows it reads.
let $statement= insert into t3 values (f6() + 5);
let $wait_statement= select i from v1 where i = 1 into k;
--source include/check_shared_row_lock.inc
let $statement= insert into t3 values (f7() + 5);
let $wait_statement= select j from v2 where j = 1 into k;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.9 SELECT which uses a stored function which
--echo # modifies data and reads tables through a view.
--echo #
--echo # Since a call to such function is written to the binary log
--echo # it should be serialized with concurrent statements.
--echo # Hence, reads should take row locks.
let $statement= select f8();
let $wait_statement= select i from v1 where i = 1 into k;
--source include/check_shared_row_lock.inc
let $statement= select f9();
let $wait_statement= update v2 set j=j+10 where j=1;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.10 SELECT which uses stored function which doesn't modify
--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.
let $statement= select f10();
let $wait_statement= select i from t1 where i = 1 into j;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.11 INSERT which uses a stored function which doesn't modify
--echo # data and reads a table indirectly, by calling another
--echo # function.
--echo #
--echo # Since such 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 row locks on data it reads.
let $statement= insert into t2 values (f10() + 5);
let $wait_statement= select i from t1 where i = 1 into j;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.12 SELECT which uses a stored function which modifies
--echo # data and reads a table indirectly, by calling another
--echo # function.
--echo #
--echo # Since a call to such function is written to the binary log
--echo # it should be serialized from concurrent statements.
--echo # Hence, reads should take row locks.
let $statement= select f11();
let $wait_statement= select i from t1 where i = 1 into j;
--source include/check_shared_row_lock.inc
--echo #
--echo # 4.13 SELECT that reads a table through a subquery passed
--echo # as a parameter to a stored function which modifies
--echo # data.
--echo #
--echo # Even though a call to this function is written to the
--echo # binary log, values of its parameters are written as literals.
--echo # So there is no need to acquire row locks on rows used in
--echo # the subquery.
let $statement= select f12((select i+10 from t1 where i=1));
--source include/check_no_row_lock.inc
--echo #
--echo # 4.14 INSERT that reads a table via a subquery passed
--echo # as a parameter to a stored function which doesn't
--echo # modify data.
--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 row locks on the data it reads.
let $statement= insert into t2 values (f13((select i+10 from t1 where i=1)));
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 5. Statements that read tables through stored procedures.
--echo #
--echo #
--echo # 5.1 CALL statement which reads a table via SELECT.
--echo #
--echo # Since neither this statement nor its components are
--echo # written to the binary log, there is no need to take
--echo # row locks on the data it reads.
let $statement= call p2(@a);
--source include/check_no_row_lock.inc
--echo #
--echo # 5.2 Function that modifes data and uses CALL,
--echo # which reads a table through SELECT.
--echo #
--echo # Since a call to such function is written to the binary
--echo # log, it should be serialized with concurrent statements.
--echo # Hence, in this case reads should take row locks on data.
let $statement= select f14();
let $wait_statement= select i from t1 where i = 1 into p;
--source include/check_shared_row_lock.inc
--echo #
--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.
let $statement= select f15();
let $wait_statement= select i from t1 where i = 1 into p;
--source include/check_shared_row_lock.inc
--echo #
--echo # 5.4 INSERT which calls function which doesn't modify data and
--echo # uses CALL statement which reads table through SELECT.
--echo #
--echo # Since such statement is written to the binary log it should
--echo # be serialized with concurrent statements affecting data it
--echo # uses. Therefore it should take row locks on data it reads.
let $statement= insert into t2 values (f15()+5);
let $wait_statement= select i from t1 where i = 1 into p;
--source include/check_shared_row_lock.inc
--echo #
--echo # 6. Statements that use triggers.
--echo #
--echo #
--echo # 6.1 Statement invoking a trigger that reads table via SELECT.
--echo #
--echo # Since this statement is written to the binary log it should
--echo # be serialized with concurrent statements affecting the data
--echo # it uses. Therefore, it should take row locks on the data
--echo # it reads.
let $statement= insert into t4 values (2);
let $wait_statement= select i from t1 where i=1 into k;
--source include/check_shared_row_lock.inc
--echo #
--echo # 6.2 Statement invoking a trigger that reads table through
--echo # a subquery in a control construct.
--echo #
--echo # The above is true for this statement as well.
let $statement= update t4 set l= 2 where l = 1;
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 6.3 Statement invoking a trigger that reads a table through
--echo # a view.
--echo #
--echo # And for this statement.
let $statement= delete from t4 where l = 1;
let $wait_statement= $statement;
--source include/check_shared_row_lock.inc
--echo #
--echo # 6.4 Statement invoking a trigger that reads a table through
--echo # a stored function.
--echo #
--echo # And for this statement.
let $statement= insert into t5 values (2);
let $wait_statement= select i from t1 where i = 1 into j;
--source include/check_shared_row_lock.inc
--echo #
--echo # 6.5 Statement invoking a trigger that reads a table through
--echo # stored procedure.
--echo #
--echo # And for this statement.
let $statement= update t5 set l= 2 where l = 1;
let $wait_statement= select i from t1 where i = 1 into p;
--source include/check_shared_row_lock.inc
--echo # Clean-up.
drop function f1;
drop function f2;
drop function f3;
drop function f4;
drop function f5;
drop function f6;
drop function f7;
drop function f8;
drop function f9;
drop function f10;
drop function f11;
drop function f12;
drop function f13;
drop function f14;
drop function f15;
drop view v1, v2;
drop procedure p1;
drop procedure p2;
drop table t1, t2, t3, t4, t5;
disconnect con1;
# Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc

View File

@ -4,10 +4,10 @@
--source include/have_debug_sync.inc
# We need InnoDB to be able use TL_WRITE_ALLOW_WRITE type of locks in our tests.
--source include/have_innodb.inc
# The test for Bug#50821 requires binary logging turned on.
# With binary logging on, sub-queries in DML statements acquire
# TL_READ_NO_INSERT which was needed to reproduce this deadlock bug.
--source include/have_log_bin.inc
# This test requires statement/mixed mode binary logging.
# Row-based mode puts weaker serializability requirements
# so weaker locks are acquired for it.
--source include/have_binlog_format_mixed_or_statement.inc
# Until bug#41971 'Thread state on embedded server is always "Writing to net"'
# is fixed this test can't be run on embedded version of server.
--source include/not_embedded.inc
@ -16,6 +16,818 @@
--source include/count_sessions.inc
--echo #
--echo # Test how we handle locking in various cases when
--echo # we read data from MyISAM tables.
--echo #
--echo # In this test we mostly check that the SQL-layer correctly
--echo # determines the type of thr_lock.c lock for a table being
--echo # read.
--echo # I.e. that it disallows concurrent inserts when the statement
--echo # is going to be written to the binary log and therefore
--echo # should be serialized, and allows concurrent inserts when
--echo # such serialization is not necessary (e.g. when
--echo # the statement is not written to binary log).
--echo #
--echo # Force concurrent inserts to be performed even if the table
--echo # has gaps. This allows to simplify clean up in scripts
--echo # used below (instead of backing up table being inserted
--echo # into and then restoring it from backup at the end of the
--echo # script we can simply delete rows which were inserted).
set @old_concurrent_insert= @@global.concurrent_insert;
set @@global.concurrent_insert= 2;
select @@global.concurrent_insert;
--echo # Prepare playground by creating tables, views,
--echo # routines and triggers used in tests.
connect (con1, localhost, root,,);
connect (con2, localhost, root,,);
connection default;
--disable_warnings
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 function if exists f1;
drop function if exists f2;
drop function if exists f3;
drop function if exists f4;
drop function if exists f5;
drop function if exists f6;
drop function if exists f7;
drop function if exists f8;
drop function if exists f9;
drop function if exists f10;
drop function if exists f11;
drop function if exists f12;
drop function if exists f13;
drop function if exists f14;
drop function if exists f15;
--enable_warnings
create table t1 (i int primary key);
insert into t1 values (1), (2), (3), (4), (5);
create table t2 (j int primary key);
insert into t2 values (1), (2), (3), (4), (5);
create table t3 (k int primary key);
insert into t3 values (1), (2), (3);
create table t4 (l int primary key);
insert into t4 values (1);
create table t5 (l int primary key);
insert into t5 values (1);
create view v1 as select i from t1;
create view v2 as select j from t2 where j in (select i from t1);
create procedure p1(k int) insert into t2 values (k);
delimiter |;
create function f1() returns int
begin
declare j int;
select i from t1 where i = 1 into j;
return j;
end|
create function f2() returns int
begin
declare k int;
select i from t1 where i = 1 into k;
insert into t2 values (k + 5);
return 0;
end|
create function f3() returns int
begin
return (select i from t1 where i = 3);
end|
create function f4() returns int
begin
if (select i from t1 where i = 3) then
return 1;
else
return 0;
end if;
end|
create function f5() returns int
begin
insert into t2 values ((select i from t1 where i = 1) + 5);
return 0;
end|
create function f6() returns int
begin
declare k int;
select i from v1 where i = 1 into k;
return k;
end|
create function f7() returns int
begin
declare k int;
select j from v2 where j = 1 into k;
return k;
end|
create function f8() returns int
begin
declare k int;
select i from v1 where i = 1 into k;
insert into t2 values (k+5);
return k;
end|
create function f9() returns int
begin
update v2 set j=j+10 where j=1;
return 1;
end|
create function f10() returns int
begin
return f1();
end|
create function f11() returns int
begin
declare k int;
set k= f1();
insert into t2 values (k+5);
return k;
end|
create function f12(p int) returns int
begin
insert into t2 values (p);
return p;
end|
create function f13(p int) returns int
begin
return p;
end|
create procedure p2(inout p int)
begin
select i from t1 where i = 1 into p;
end|
create function f14() returns int
begin
declare k int;
call p2(k);
insert into t2 values (k+5);
return k;
end|
create function f15() returns int
begin
declare k int;
call p2(k);
return k;
end|
create trigger t4_bi before insert on t4 for each row
begin
declare k int;
select i from t1 where i=1 into k;
set new.l= k+1;
end|
create trigger t4_bu before update on t4 for each row
begin
if (select i from t1 where i=1) then
set new.l= 2;
end if;
end|
create trigger t4_bd before delete on t4 for each row
begin
if !(select i from v1 where i=1) then
signal sqlstate '45000';
end if;
end|
create trigger t5_bi before insert on t5 for each row
begin
set new.l= f1()+1;
end|
create trigger t5_bu before update on t5 for each row
begin
declare j int;
call p2(j);
set new.l= j + 1;
end|
delimiter ;|
--echo #
--echo # Set common variables to be used by the scripts
--echo # called below.
--echo #
let $con_aux1= con1;
let $con_aux2= con2;
let $table= t1;
--echo # Switch to connection 'con1'.
connection con1;
--echo # Cache all functions used in the tests below so statements
--echo # calling them won't need to open and lock mysql.proc table
--echo # and we can assume that each statement locks its tables
--echo # once during its execution.
--disable_result_log
show create procedure p1;
show create procedure p2;
show create function f1;
show create function f2;
show create function f3;
show create function f4;
show create function f5;
show create function f6;
show create function f7;
show create function f8;
show create function f9;
show create function f10;
show create function f11;
show create function f12;
show create function f13;
show create function f14;
show create function f15;
--enable_result_log
--echo # Switch back to connection 'default'.
connection default;
--echo #
--echo # 1. Statements that read tables and do not use subqueries.
--echo #
--echo #
--echo # 1.1 Simple SELECT statement.
--echo #
--echo # No locks are necessary as this statement won't be written
--echo # to the binary log and thanks to how MyISAM works SELECT
--echo # will see version of the table prior to concurrent insert.
let $statement= select * from t1;
let $restore_table= ;
--source include/check_concurrent_insert.inc
--echo #
--echo # 1.2 Multi-UPDATE statement.
--echo #
--echo # Has to take shared locks on rows in the table being read as this
--echo # statement will be written to the binary log and therefore should
--echo # be serialized with concurrent statements.
let $statement= update t2, t1 set j= j - 1 where i = j;
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 1.3 Multi-DELETE statement.
--echo #
--echo # The above is true for this statement as well.
let $statement= delete t2 from t1, t2 where i = j;
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 1.4 DESCRIBE statement.
--echo #
--echo # This statement does not really read data from the
--echo # target table and thus does not take any lock on it.
--echo # We check this for completeness of coverage.
lock table t1 write;
--echo # Switching to connection 'con1'.
connection con1;
--echo # This statement should not be blocked.
--disable_result_log
describe t1;
--enable_result_log
--echo # Switching to connection 'default'.
connection default;
unlock tables;
--echo #
--echo # 1.5 SHOW statements.
--echo #
--echo # The above is true for SHOW statements as well.
lock table t1 write;
--echo # Switching to connection 'con1'.
connection con1;
--echo # These statements should not be blocked.
# The below test for SHOW CREATE TABLE is disabled until bug 52593
# "SHOW CREATE TABLE is blocked if table is locked for write by another
# connection" is fixed.
--disable_parsing
show create table t1;
--enable_parsing
--disable_result_log
show keys from t1;
--enable_result_log
--echo # Switching to connection 'default'.
connection default;
unlock tables;
--echo #
--echo # 2. Statements which read tables through subqueries.
--echo #
--echo #
--echo # 2.1 CALL with a subquery.
--echo #
--echo # A strong lock is not necessary as this statement is not
--echo # written to the binary log as a whole (it is written
--echo # statement-by-statement).
let $statement= call p1((select i + 5 from t1 where i = 1));
let $restore_table= t2;
--source include/check_concurrent_insert.inc
--echo #
--echo # 2.2 CREATE TABLE with a subquery.
--echo #
--echo # Has to take a strong lock on the table being read as
--echo # this statement is written to the binary log and therefore
--echo # should be serialized with concurrent statements.
let $statement= create table t0 select * from t1;
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
drop table t0;
let $statement= create table t0 select j from t2 where j in (select i from t1);
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
drop table t0;
--echo #
--echo # 2.3 DELETE with a subquery.
--echo #
--echo # The above is true for this statement as well.
let $statement= delete from t2 where j in (select i from t1);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 2.4 MULTI-DELETE with a subquery.
--echo #
--echo # Same is true for this statement as well.
let $statement= delete t2 from t3, t2 where k = j and j in (select i from t1);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 2.5 DO with a subquery.
--echo #
--echo # A strong lock is not necessary as it is not logged.
let $statement= do (select i from t1 where i = 1);
let $restore_table= ;
--source include/check_concurrent_insert.inc
--echo #
--echo # 2.6 INSERT with a subquery.
--echo #
--echo # Has to take a strong lock on the table being read as
--echo # this statement is written to the binary log and therefore
--echo # should be serialized with concurrent inserts.
let $statement= insert into t2 select i+5 from t1;
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= insert into t2 values ((select i+5 from t1 where i = 4));
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 2.7 LOAD DATA with a subquery.
--echo #
--echo # The above is true for this statement as well.
let $statement= load data infile '../../std_data/rpl_loaddata.dat' into table t2 (@a, @b) set j= @b + (select i from t1 where i = 1);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 2.8 REPLACE with a subquery.
--echo #
--echo # Same is true for this statement as well.
let $statement= replace into t2 select i+5 from t1;
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= replace into t2 values ((select i+5 from t1 where i = 4));
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 2.9 SELECT with a subquery.
--echo #
--echo # Strong locks are not necessary as this statement is not written
--echo # to the binary log and thanks to how MyISAM works this statement
--echo # sees a version of the table prior to the concurrent insert.
let $statement= select * from t2 where j in (select i from t1);
let $restore_table= ;
--source include/check_concurrent_insert.inc
--echo #
--echo # 2.10 SET with a subquery.
--echo #
--echo # The same is true for this statement as well.
let $statement= set @a:= (select i from t1 where i = 1);
let $restore_table= ;
--source include/check_concurrent_insert.inc
--echo #
--echo # 2.11 SHOW with a subquery.
--echo #
--echo # And for this statement too.
let $statement= show tables from test where Tables_in_test = 't2' and (select i from t1 where i = 1);
let $restore_table= ;
--source include/check_concurrent_insert.inc
let $statement= show columns from t2 where (select i from t1 where i = 1);
let $restore_table= ;
--source include/check_concurrent_insert.inc
--echo #
--echo # 2.12 UPDATE with a subquery.
--echo #
--echo # Has to take a strong lock on the table being read as
--echo # this statement is written to the binary log and therefore
--echo # should be serialized with concurrent inserts.
let $statement= update t2 set j= j-10 where j in (select i from t1);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 2.13 MULTI-UPDATE with a subquery.
--echo #
--echo # Same is true for this statement as well.
let $statement= update t2, t3 set j= j -10 where j=k and j in (select i from t1);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 3. Statements which read tables through a view.
--echo #
--echo #
--echo # 3.1 SELECT statement which uses some table through a view.
--echo #
--echo # Since this statement is not written to the binary log and
--echo # an old version of the table is accessible thanks to how MyISAM
--echo # handles concurrent insert, no locking is necessary.
let $statement= select * from v1;
let $restore_table= ;
--source include/check_concurrent_insert.inc
let $statement= select * from v2;
let $restore_table= ;
--source include/check_concurrent_insert.inc
let $statement= select * from t2 where j in (select i from v1);
let $restore_table= ;
--source include/check_concurrent_insert.inc
let $statement= select * from t3 where k in (select j from v2);
let $restore_table= ;
--source include/check_concurrent_insert.inc
--echo #
--echo # 3.2 Statements which modify a table and use views.
--echo #
--echo # Since such statements are going to be written to the binary
--echo # log they need to be serialized against concurrent statements
--echo # and therefore should take strong locks on the data read.
let $statement= update t2 set j= j-10 where j in (select i from v1);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= update t3 set k= k-10 where k in (select j from v2);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= update t2, v1 set j= j-10 where j = i;
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= update v2 set j= j-10 where j = 3;
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4. Statements which read tables through stored functions.
--echo #
--echo #
--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 # 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.
let $statement= select f1();
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
let $statement= set @a:= f1();
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.2 INSERT (or other statement which modifies data) with
--echo # a stored function which does not modify data and uses
--echo # SELECT.
--echo #
--echo # Since such statement is written to the binary log it should
--echo # be serialized with concurrent statements affecting the data
--echo # it uses. Therefore it should take strong lock on the data
--echo # it reads.
let $statement= insert into t2 values (f1() + 5);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.3 SELECT/SET with a stored function which
--echo # reads and modifies data.
--echo #
--echo # Since a call to such function is written to the binary log,
--echo # it should be serialized with concurrent statements affecting
--echo # the data it uses. Hence, a strong lock on the data read
--echo # should be taken.
let $statement= select f2();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= set @a:= f2();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.4. SELECT/SET with a stored function which does not
--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.
let $statement= select f3();
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
let $statement= set @a:= f3();
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
let $statement= select f4();
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
let $statement= set @a:= f4();
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.5. INSERT (or other statement which modifies data) with
--echo # a stored function which does not modify data and reads
--echo # the table through a subselect in one of its control
--echo # constructs.
--echo #
--echo # Since such statement is written to the binary log it should
--echo # be serialized with concurrent statements affecting data it
--echo # uses. Therefore it should take a strong lock on the data
--echo # it reads.
let $statement= insert into t2 values (f3() + 5);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= insert into t2 values (f4() + 6);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.6 SELECT/SET which uses a stored function with
--echo # DML which reads a table via a subquery.
--echo #
--echo # Since call to such function is written to the binary log
--echo # it should be serialized with concurrent statements.
--echo # Hence reads should take a strong lock.
let $statement= select f5();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= set @a:= f5();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.7 SELECT/SET which uses a stored function which
--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.
let $statement= select f6();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= set @a:= f6();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= select f7();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= set @a:= f7();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.8 INSERT which uses stored function which
--echo # doesn't modify data and reads a table
--echo # through a view.
--echo #
--echo # Since such statement is written to the binary log and
--echo # should be serialized with concurrent statements affecting
--echo # the data it uses. Therefore it should take a strong lock on
--echo # the table it reads.
let $statement= insert into t3 values (f6() + 5);
let $restore_table= t3;
--source include/check_no_concurrent_insert.inc
let $statement= insert into t3 values (f7() + 5);
let $restore_table= t3;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.9 SELECT which uses a stored function which
--echo # modifies data and reads tables through a view.
--echo #
--echo # Since a call to such function is written to the binary log
--echo # it should be serialized with concurrent statements.
--echo # Hence, reads should take strong locks.
let $statement= select f8();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
let $statement= select f9();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.10 SELECT which uses a stored function which doesn't modify
--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.
let $statement= select f10();
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.11 INSERT which uses a stored function which doesn't modify
--echo # data and reads a table indirectly, by calling another
--echo # function.
--echo #
--echo # Since such 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 data it reads.
let $statement= insert into t2 values (f10() + 5);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.12 SELECT which uses a stored function which modifies
--echo # data and reads a table indirectly, by calling another
--echo # function.
--echo #
--echo # Since a call to such function is written to the binary log
--echo # it should be serialized from concurrent statements.
--echo # Hence, read should take a strong lock.
let $statement= select f11();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 4.13 SELECT that reads a table through a subquery passed
--echo # as a parameter to a stored function which modifies
--echo # data.
--echo #
--echo # Even though a call to this function is written to the
--echo # binary log, values of its parameters are written as literals.
--echo # So there is no need to acquire strong locks for tables used in
--echo # the subquery.
let $statement= select f12((select i+10 from t1 where i=1));
let $restore_table= t2;
--source include/check_concurrent_insert.inc
--echo #
--echo # 4.14 INSERT that reads a table via a subquery passed
--echo # as a parameter to a stored function which doesn't
--echo # modify data.
--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= 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 # 5. Statements that read tables through stored procedures.
--echo #
--echo #
--echo # 5.1 CALL statement which reads a table via SELECT.
--echo #
--echo # Since neither this statement nor its components are
--echo # written to the binary log, there is no need to take
--echo # strong locks on the data it reads.
let $statement= call p2(@a);
let $restore_table= ;
--source include/check_concurrent_insert.inc
--echo #
--echo # 5.2 Function that modifes data and uses CALL,
--echo # which reads a table through SELECT.
--echo #
--echo # Since a call to such function is written to the binary
--echo # log, it should be serialized with concurrent statements.
--echo # Hence, in this case reads should take strong locks on data.
let $statement= select f14();
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--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.
let $statement= select f15();
let $restore_table= ;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 5.4 INSERT which calls function which doesn't modify data and
--echo # uses CALL statement which reads table through SELECT.
--echo #
--echo # Since such statement is written to the binary log it should
--echo # be serialized with concurrent statements affecting data it
--echo # uses. Therefore it should take strong locks on data it reads.
let $statement= insert into t2 values (f15()+5);
let $restore_table= t2;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 6. Statements that use triggers.
--echo #
--echo #
--echo # 6.1 Statement invoking a trigger that reads table via SELECT.
--echo #
--echo # Since this statement is written to the binary log it should
--echo # be serialized with concurrent statements affecting the data
--echo # it uses. Therefore, it should take strong locks on the data
--echo # it reads.
let $statement= insert into t4 values (2);
let $restore_table= t4;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 6.2 Statement invoking a trigger that reads table through
--echo # a subquery in a control construct.
--echo #
--echo # The above is true for this statement as well.
let $statement= update t4 set l= 2 where l = 1;
let $restore_table= t4;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 6.3 Statement invoking a trigger that reads a table through
--echo # a view.
--echo #
--echo # And for this statement.
let $statement= delete from t4 where l = 1;
let $restore_table= t4;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 6.4 Statement invoking a trigger that reads a table through
--echo # a stored function.
--echo #
--echo # And for this statement.
let $statement= insert into t5 values (2);
let $restore_table= t5;
--source include/check_no_concurrent_insert.inc
--echo #
--echo # 6.5 Statement invoking a trigger that reads a table through
--echo # stored procedure.
--echo #
--echo # And for this statement.
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;
drop function f3;
drop function f4;
drop function f5;
drop function f6;
drop function f7;
drop function f8;
drop function f9;
drop function f10;
drop function f11;
drop function f12;
drop function f13;
drop function f14;
drop function f15;
drop view v1, v2;
drop procedure p1;
drop procedure p2;
drop table t1, t2, t3, t4, t5;
disconnect con1;
disconnect con2;
set @@global.concurrent_insert= @old_concurrent_insert;
--echo #
--echo # Test for bug #45143 "All connections hang on concurrent ALTER TABLE".
--echo #

View File

@ -4628,7 +4628,7 @@ int ha_ndbcluster::start_statement(THD *thd,
trans_register_ha(thd, FALSE, ndbcluster_hton);
if (!thd_ndb->trans)
{
if (thd->in_multi_stmt_transaction())
if (thd->in_multi_stmt_transaction_mode())
trans_register_ha(thd, TRUE, ndbcluster_hton);
DBUG_PRINT("trans",("Starting transaction"));
thd_ndb->trans= ndb->startTransaction();
@ -4698,7 +4698,7 @@ int ha_ndbcluster::init_handler_for_statement(THD *thd, Thd_ndb *thd_ndb)
}
#endif
if (thd->in_multi_stmt_transaction())
if (thd->in_multi_stmt_transaction_mode())
{
const void *key= m_table;
HASH_SEARCH_STATE state;
@ -4782,7 +4782,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
if (opt_ndb_cache_check_time && m_rows_changed)
{
DBUG_PRINT("info", ("Rows has changed and util thread is running"));
if (thd->in_multi_stmt_transaction())
if (thd->in_multi_stmt_transaction_mode())
{
DBUG_PRINT("info", ("Add share to list of tables to be invalidated"));
/* NOTE push_back allocates memory using transactions mem_root! */
@ -4801,7 +4801,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
DBUG_PRINT("trans", ("Last external_lock"));
PRINT_OPTION_FLAGS(thd);
if (!thd->in_multi_stmt_transaction())
if (!thd->in_multi_stmt_transaction_mode())
{
if (thd_ndb->trans)
{
@ -4911,7 +4911,7 @@ static int ndbcluster_commit(handlerton *hton, THD *thd, bool all)
PRINT_OPTION_FLAGS(thd);
DBUG_PRINT("enter", ("Commit %s", (all ? "all" : "stmt")));
thd_ndb->start_stmt_count= 0;
if (trans == NULL || (!all && thd->in_multi_stmt_transaction()))
if (trans == NULL || (!all && thd->in_multi_stmt_transaction_mode()))
{
/*
An odditity in the handler interface is that commit on handlerton
@ -4981,7 +4981,7 @@ static int ndbcluster_rollback(handlerton *hton, THD *thd, bool all)
DBUG_ASSERT(ndb);
thd_ndb->start_stmt_count= 0;
if (trans == NULL || (!all &&
thd->in_multi_stmt_transaction()))
thd->in_multi_stmt_transaction_mode()))
{
/* Ignore end-of-statement until real rollback or commit is called */
DBUG_PRINT("info", ("Rollback before start or end-of-statement only"));
@ -8271,7 +8271,7 @@ ndbcluster_cache_retrieval_allowed(THD *thd,
DBUG_ENTER("ndbcluster_cache_retrieval_allowed");
DBUG_PRINT("enter", ("dbname: %s, tabname: %s", dbname, tabname));
if (thd->in_multi_stmt_transaction())
if (thd->in_multi_stmt_transaction_mode())
{
DBUG_PRINT("exit", ("No, don't use cache in transaction"));
DBUG_RETURN(FALSE);
@ -8339,7 +8339,7 @@ ha_ndbcluster::register_query_cache_table(THD *thd,
DBUG_ENTER("ha_ndbcluster::register_query_cache_table");
DBUG_PRINT("enter",("dbname: %s, tabname: %s", m_dbname, m_tabname));
if (thd->in_multi_stmt_transaction())
if (thd->in_multi_stmt_transaction_mode())
{
DBUG_PRINT("exit", ("Can't register table during transaction"));
DBUG_RETURN(FALSE);

View File

@ -1245,7 +1245,14 @@ end:
/**
@note
This function does not care about global read lock. A caller should.
@param[in] all Is set in case of explicit commit
(COMMIT statement), or implicit commit
issued by DDL. Is not set when called
at the end of statement, even if
autocommit=1.
*/
int ha_commit_one_phase(THD *thd, bool all)
{
int error=0;
@ -1253,9 +1260,15 @@ int ha_commit_one_phase(THD *thd, bool all)
/*
"real" is a nick name for a transaction for which a commit will
make persistent changes. E.g. a 'stmt' transaction inside a 'all'
transation is not 'real': even though it's possible to commit it,
transaction is not 'real': even though it's possible to commit it,
the changes are not durable as they might be rolled back if the
enclosing 'all' transaction is rolled back.
We establish the value of 'is_real_trans' by checking
if it's an explicit COMMIT/BEGIN statement, or implicit
commit issued by DDL (all == TRUE), or if we're running
in autocommit mode (it's only in the autocommit mode
ha_commit_one_phase() can be called with an empty
transaction.all.ha_list, see why in trans_register_ha()).
*/
bool is_real_trans=all || thd->transaction.all.ha_list == 0;
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
@ -1303,9 +1316,15 @@ int ha_rollback_trans(THD *thd, bool all)
/*
"real" is a nick name for a transaction for which a commit will
make persistent changes. E.g. a 'stmt' transaction inside a 'all'
transation is not 'real': even though it's possible to commit it,
transaction is not 'real': even though it's possible to commit it,
the changes are not durable as they might be rolled back if the
enclosing 'all' transaction is rolled back.
We establish the value of 'is_real_trans' by checking
if it's an explicit COMMIT or BEGIN statement, or implicit
commit issued by DDL (in these cases all == TRUE),
or if we're running in autocommit mode (it's only in the autocommit mode
ha_commit_one_phase() is called with an empty
transaction.all.ha_list, see why in trans_register_ha()).
*/
bool is_real_trans=all || thd->transaction.all.ha_list == 0;
DBUG_ENTER("ha_rollback_trans");
@ -1358,7 +1377,7 @@ int ha_rollback_trans(THD *thd, bool all)
if (all)
thd->variables.tx_isolation=thd->session_tx_isolation;
}
/* Always cleanup. Even if there nht==0. There may be savepoints. */
/* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
thd->transaction.cleanup();
if (all)

View File

@ -1686,7 +1686,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
DBUG_PRINT("debug",
("all: %d, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
all,
YESNO(thd->in_multi_stmt_transaction()),
YESNO(thd->in_multi_stmt_transaction_mode()),
YESNO(thd->transaction.all.modified_non_trans_table),
YESNO(thd->transaction.stmt.modified_non_trans_table)));
@ -4267,7 +4267,7 @@ bool use_trans_cache(const THD* thd, bool is_transactional)
*/
bool ending_trans(THD* thd, const bool all)
{
return (all || (!all && !thd->in_multi_stmt_transaction()));
return (all || (!all && !thd->in_multi_stmt_transaction_mode()));
}
/**
@ -4370,7 +4370,7 @@ THD::binlog_start_trans_and_stmt()
cache_mngr->trx_cache.get_prev_position() == MY_OFF_T_UNDEF)
{
this->binlog_set_stmt_begin();
if (in_multi_stmt_transaction())
if (in_multi_stmt_transaction_mode())
trans_register_ha(this, TRUE, binlog_hton);
trans_register_ha(this, FALSE, binlog_hton);
/*

View File

@ -2485,13 +2485,13 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
implicit_commit= TRUE;
break;
case SQLCOM_DROP_TABLE:
force_trans= lex->drop_temporary && thd->in_multi_stmt_transaction();
force_trans= lex->drop_temporary && thd->in_multi_stmt_transaction_mode();
implicit_commit= !force_trans;
break;
case SQLCOM_ALTER_TABLE:
case SQLCOM_CREATE_TABLE:
force_trans= (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
thd->in_multi_stmt_transaction();
thd->in_multi_stmt_transaction_mode();
implicit_commit= !force_trans &&
!(lex->select_lex.item_list.elements &&
thd->is_current_stmt_binlog_format_row());
@ -4226,7 +4226,7 @@ void Load_log_event::print_query(bool need_db, const char *cs, char *buf,
pos= strmov(pos, "LOAD DATA ");
if (thd->lex->lock_option == TL_WRITE_CONCURRENT_INSERT)
if (is_concurrent)
pos= strmov(pos, "CONCURRENT ");
if (fn_start)
@ -4368,6 +4368,7 @@ bool Load_log_event::write_data_body(IO_CACHE* file)
Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
const char *db_arg, const char *table_name_arg,
List<Item> &fields_arg,
bool is_concurrent_arg,
enum enum_duplicates handle_dup,
bool ignore, bool using_trans)
:Log_event(thd_arg,
@ -4378,7 +4379,8 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
num_fields(0),fields(0),
field_lens(0),field_block_len(0),
table_name(table_name_arg ? table_name_arg : ""),
db(db_arg), fname(ex->file_name), local_fname(FALSE)
db(db_arg), fname(ex->file_name), local_fname(FALSE),
is_concurrent(is_concurrent_arg)
{
time_t end_time;
time(&end_time);
@ -4459,7 +4461,13 @@ Load_log_event::Load_log_event(const char *buf, uint event_len,
const Format_description_log_event *description_event)
:Log_event(buf, description_event), num_fields(0), fields(0),
field_lens(0),field_block_len(0),
table_name(0), db(0), fname(0), local_fname(FALSE)
table_name(0), db(0), fname(0), local_fname(FALSE),
/*
Load_log_event which comes from the binary log does not contain
information about the type of insert which was used on the master.
Assume that it was an ordinary, non-concurrent LOAD DATA.
*/
is_concurrent(FALSE)
{
DBUG_ENTER("Load_log_event");
/*
@ -6149,11 +6157,14 @@ int Stop_log_event::do_update_pos(Relay_log_info *rli)
Create_file_log_event::
Create_file_log_event(THD* thd_arg, sql_exchange* ex,
const char* db_arg, const char* table_name_arg,
List<Item>& fields_arg, enum enum_duplicates handle_dup,
List<Item>& fields_arg,
bool is_concurrent_arg,
enum enum_duplicates handle_dup,
bool ignore,
uchar* block_arg, uint block_len_arg, bool using_trans)
:Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup, ignore,
using_trans),
:Load_log_event(thd_arg, ex, db_arg, table_name_arg, fields_arg,
is_concurrent_arg,
handle_dup, ignore, using_trans),
fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
{

View File

@ -2069,6 +2069,17 @@ public:
uint32 skip_lines;
sql_ex_info sql_ex;
bool local_fname;
/**
Indicates that this event corresponds to LOAD DATA CONCURRENT,
@note Since Load_log_event event coming from the binary log
lacks information whether LOAD DATA on master was concurrent
or not, this flag is only set to TRUE for an auxiliary
Load_log_event object which is used in mysql_load() to
re-construct LOAD DATA statement from function parameters,
for logging.
*/
bool is_concurrent;
/* fname doesn't point to memory inside Log_event::temp_buf */
void set_fname_outside_temp_buf(const char *afname, uint alen)
@ -2089,7 +2100,9 @@ public:
Load_log_event(THD* thd, sql_exchange* ex, const char* db_arg,
const char* table_name_arg,
List<Item>& fields_arg, enum enum_duplicates handle_dup, bool ignore,
List<Item>& fields_arg,
bool is_concurrent_arg,
enum enum_duplicates handle_dup, bool ignore,
bool using_trans);
void set_fields(const char* db, List<Item> &fields_arg,
Name_resolution_context *context);
@ -2708,6 +2721,7 @@ public:
Create_file_log_event(THD* thd, sql_exchange* ex, const char* db_arg,
const char* table_name_arg,
List<Item>& fields_arg,
bool is_concurrent_arg,
enum enum_duplicates handle_dup, bool ignore,
uchar* block_arg, uint block_len_arg,
bool using_trans);

View File

@ -2127,6 +2127,9 @@ sp_head::reset_lex(THD *thd)
sublex->interval_list.empty();
sublex->type= 0;
/* Reset part of parser state which needs this. */
thd->m_parser_state->m_yacc.reset_before_substatement();
DBUG_RETURN(FALSE);
}

View File

@ -3159,6 +3159,12 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
*/
Query_tables_list backup;
thd->lex->reset_n_backup_query_tables_list(&backup);
/*
Restore Query_tables_list::sql_command value, which was reset
above, as the code writing query to the binary log assumes that
this value corresponds to the statement being executed.
*/
thd->lex->sql_command= backup.sql_command;
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // Should never happen
close_thread_tables(thd); /* purecov: deadcode */

View File

@ -1558,7 +1558,7 @@ void close_thread_tables(THD *thd)
- If in autocommit mode, or outside a transactional context,
automatically release metadata locks of the current statement.
*/
if (! thd->in_multi_stmt_transaction() &&
if (! thd->in_multi_stmt_transaction_mode() &&
! (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
{
thd->mdl_context.release_transactional_locks();
@ -3783,7 +3783,7 @@ end_with_lock_open:
Open_table_context::Open_table_context(THD *thd, ulong timeout)
:m_action(OT_NO_ACTION),
m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()),
m_has_locks((thd->in_multi_stmt_transaction() &&
m_has_locks((thd->in_multi_stmt_transaction_mode() &&
thd->mdl_context.has_locks()) ||
thd->mdl_context.trans_sentinel()),
m_global_mdl_request(NULL),
@ -3963,7 +3963,8 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
Return a appropriate read lock type given a table object.
@param thd Thread context
@param table TABLE object for table to be locked
@param prelocking_ctx Prelocking context.
@param table_list Table list element for table to be locked.
@remark Due to a statement-based replication limitation, statements such as
INSERT INTO .. SELECT FROM .. and CREATE TABLE .. SELECT FROM need
@ -3972,20 +3973,44 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
source table. If such a statement gets applied on the slave before
the INSERT .. SELECT statement finishes, data on the master could
differ from data on the slave and end-up with a discrepancy between
the binary log and table state. 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 (ie: general_log is enabled on the slave). The
statement will be marked as unsafe for SBR in decide_logging_format().
the binary log and table state.
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.
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
(ie: general_log is enabled on the slave). The statement will
be marked as unsafe for SBR in decide_logging_format().
@remark Note that even in prelocked mode it is important to correctly
determine lock type value. In this mode lock type is passed to
handler::start_stmt() method and can be used by storage engine,
for example, to determine what kind of row locks it should acquire
when reading data from the table.
*/
thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table)
thr_lock_type read_lock_type_for_table(THD *thd,
Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list)
{
bool log_on= mysql_bin_log.is_open() && (thd->variables.option_bits & OPTION_BIN_LOG);
/*
In cases when this function is called for a sub-statement executed in
prelocked mode we can't rely on OPTION_BIN_LOG flag in THD::options
bitmap to determine that binary logging is turned on as this bit can
be cleared before executing sub-statement. So instead we have to look
at THD::sql_log_bin_toplevel member.
*/
bool log_on= mysql_bin_log.is_open() && thd->sql_log_bin_toplevel;
ulong binlog_format= thd->variables.binlog_format;
if ((log_on == FALSE) || (binlog_format == BINLOG_FORMAT_ROW) ||
(table->s->table_category == TABLE_CATEGORY_LOG) ||
(table->s->table_category == TABLE_CATEGORY_PERFORMANCE))
(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 ||
(thd->locked_tables_mode > LTM_LOCK_TABLES)))
return TL_READ;
else
return TL_READ_NO_INSERT;
@ -4336,7 +4361,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
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, tables->table);
read_lock_type_for_table(thd, lex, tables);
else
tables->table->reginfo.lock_type= tables->lock_type;
}
@ -4989,35 +5014,49 @@ handle_view(THD *thd, Query_tables_list *prelocking_ctx,
}
/*
/**
Check that lock is ok for tables; Call start stmt if ok
SYNOPSIS
check_lock_and_start_stmt()
thd Thread handle
table_list Table to check
lock_type Lock used for table
@param thd Thread handle.
@param prelocking_ctx Prelocking context.
@param table_list Table list element for table to be checked.
RETURN VALUES
0 ok
1 error
@retval FALSE - Ok.
@retval TRUE - Error.
*/
static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
thr_lock_type lock_type)
static bool check_lock_and_start_stmt(THD *thd,
Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list)
{
int error;
thr_lock_type lock_type;
DBUG_ENTER("check_lock_and_start_stmt");
/*
TL_WRITE_DEFAULT and TL_READ_DEFAULT are supposed to be parser only
types of locks so they should be converted to appropriate other types
to be passed to storage engine. The exact lock type passed to the
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.
*/
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);
else
lock_type= table_list->lock_type;
if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ &&
(int) table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ)
(int) table_list->table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ)
{
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),table->alias);
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->alias);
DBUG_RETURN(1);
}
if ((error=table->file->start_stmt(thd, lock_type)))
if ((error= table_list->table->file->start_stmt(thd, lock_type)))
{
table->file->print_error(error,MYF(0));
table_list->table->file->print_error(error, MYF(0));
DBUG_RETURN(1);
}
DBUG_RETURN(0);
@ -5162,7 +5201,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
table->grant= table_list->grant;
if (thd->locked_tables_mode)
{
if (check_lock_and_start_stmt(thd, table, lock_type))
if (check_lock_and_start_stmt(thd, thd->lex, table_list))
table= 0;
}
else
@ -5390,7 +5429,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
if (!table->placeholder())
{
table->table->query_id= thd->query_id;
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
if (check_lock_and_start_stmt(thd, thd->lex, table))
{
mysql_unlock_tables(thd, thd->lock);
thd->lock= 0;
@ -5444,7 +5483,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
}
}
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
if (check_lock_and_start_stmt(thd, thd->lex, table))
{
DBUG_RETURN(TRUE);
}

View File

@ -56,9 +56,6 @@ enum enum_resolution_type {
RESOLVED_AGAINST_ALIAS
};
enum enum_open_table_action {OT_NO_ACTION= 0, OT_BACK_OFF_AND_RETRY,
OT_DISCOVER, OT_REPAIR};
enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
IGNORE_EXCEPT_NON_UNIQUE};
@ -123,7 +120,9 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name);
TABLE *find_write_locked_table(TABLE *list, const char *db,
const char *table_name);
thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table);
thr_lock_type read_lock_type_for_table(THD *thd,
Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list);
my_bool mysql_rm_tmp_tables(void);
bool rm_temporary_table(handlerton *base, char *path);

View File

@ -1177,7 +1177,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
DBUG_ASSERT(flags.protocol_type != (unsigned int) Protocol::PROTOCOL_LOCAL);
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
flags.in_trans= thd->in_active_multi_stmt_transaction();
flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
flags.pkt_nr= net->pkt_nr;
flags.character_set_client_num=
@ -1470,7 +1470,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
flags.protocol_type= (unsigned int) thd->protocol->type();
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
flags.in_trans= thd->in_active_multi_stmt_transaction();
flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
flags.pkt_nr= thd->net.pkt_nr;
flags.character_set_client_num= thd->variables.character_set_client->number;
@ -1541,7 +1541,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
}
DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query));
if (thd->in_multi_stmt_transaction() &&
if (thd->in_multi_stmt_transaction_mode() &&
(query->tables_type() & HA_CACHE_TBL_TRANSACT))
{
DBUG_PRINT("qcache",
@ -1698,7 +1698,7 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
if (is_disabled())
DBUG_VOID_RETURN;
using_transactions= using_transactions && thd->in_multi_stmt_transaction();
using_transactions= using_transactions && thd->in_multi_stmt_transaction_mode();
for (; tables_used; tables_used= tables_used->next_local)
{
DBUG_ASSERT(!using_transactions || tables_used->table!=0);
@ -1782,7 +1782,7 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
if (is_disabled())
DBUG_VOID_RETURN;
using_transactions= using_transactions && thd->in_multi_stmt_transaction();
using_transactions= using_transactions && thd->in_multi_stmt_transaction_mode();
if (using_transactions &&
(table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
thd->add_changed_table(table);
@ -1800,7 +1800,7 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
if (is_disabled())
DBUG_VOID_RETURN;
using_transactions= using_transactions && thd->in_multi_stmt_transaction();
using_transactions= using_transactions && thd->in_multi_stmt_transaction_mode();
if (using_transactions) // used for innodb => has_transactions() is TRUE
thd->add_changed_table(key, key_length);
else
@ -3572,7 +3572,7 @@ Query_cache::is_cacheable(THD *thd, size_t query_len, const char *query,
tables_type)))
DBUG_RETURN(0);
if (thd->in_multi_stmt_transaction() &&
if (thd->in_multi_stmt_transaction_mode() &&
((*tables_type)&HA_CACHE_TBL_TRANSACT))
{
DBUG_PRINT("qcache", ("not in autocommin mode"));

View File

@ -1443,7 +1443,7 @@ void THD::add_changed_table(TABLE *table)
{
DBUG_ENTER("THD::add_changed_table(table)");
DBUG_ASSERT(in_multi_stmt_transaction() && table->file->has_transactions());
DBUG_ASSERT(in_multi_stmt_transaction_mode() && table->file->has_transactions());
add_changed_table(table->s->table_cache_key.str,
(long) table->s->table_cache_key.length);
DBUG_VOID_RETURN;

View File

@ -2352,10 +2352,6 @@ public:
{
return limit_found_rows;
}
inline bool active_transaction()
{
return server_status & SERVER_STATUS_IN_TRANS;
}
/**
Returns TRUE if session is in a multi-statement transaction mode.
@ -2366,11 +2362,60 @@ public:
OPTION_BEGIN: Regardless of the autocommit status, a multi-statement
transaction can be explicitly started with the statements "START
TRANSACTION", "BEGIN [WORK]", "[COMMIT | ROLLBACK] AND CHAIN", etc.
Note: this doesn't tell you whether a transaction is active.
A session can be in multi-statement transaction mode, and yet
have no active transaction, e.g., in case of:
set @@autocommit=0;
set @a= 3; <-- these statements don't
set transaction isolation level serializable; <-- start an active
flush tables; <-- transaction
I.e. for the above scenario this function returns TRUE, even
though no active transaction has begun.
@sa in_active_multi_stmt_transaction()
*/
inline bool in_multi_stmt_transaction()
inline bool in_multi_stmt_transaction_mode()
{
return variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN);
}
/**
TRUE if the session is in a multi-statement transaction mode
(@sa in_multi_stmt_transaction_mode()) *and* there is an
active transaction, i.e. there is an explicit start of a
transaction with BEGIN statement, or implicit with a
statement that uses a transactional engine.
For example, these scenarios don't start an active transaction
(even though the server is in multi-statement transaction mode):
set @@autocommit=0;
select * from nontrans_table;
set @var=TRUE;
flush tables;
Note, that even for a statement that starts a multi-statement
transaction (i.e. select * from trans_table), this
flag won't be set until we open the statement's tables
and the engines register themselves for the transaction
(see trans_register_ha()),
hence this method is reliable to use only after
open_tables() has completed.
Why do we need a flag?
----------------------
We need to maintain a (at first glance redundant)
session flag, rather than looking at thd->transaction.all.ha_list
because of explicit start of a transaction with BEGIN.
I.e. in case of
BEGIN;
select * from nontrans_t1; <-- in_active_multi_stmt_transaction() is true
*/
inline bool in_active_multi_stmt_transaction()
{
return server_status & SERVER_STATUS_IN_TRANS;
}
inline bool fill_derived_tables()
{
return !stmt_arena->is_stmt_prepare() && !lex->only_view_structure();

View File

@ -377,7 +377,6 @@ void lex_start(THD *thd)
lex->subqueries= FALSE;
lex->view_prepare_mode= FALSE;
lex->derived_tables= 0;
lex->lock_option= TL_READ;
lex->safe_to_cache_query= 1;
lex->leaf_tables_insert= 0;
lex->parsing_options.reset();
@ -390,7 +389,6 @@ void lex_start(THD *thd)
lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc;
lex->select_lex.group_list.empty();
lex->select_lex.order_list.empty();
lex->sql_command= SQLCOM_END;
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
lex->spname= NULL;
@ -1735,7 +1733,6 @@ void st_select_lex::init_query()
exclude_from_table_unique_test= no_wrap_view_item= FALSE;
nest_level= 0;
link_next= 0;
lock_option= TL_READ_DEFAULT;
}
void st_select_lex::init_select()
@ -2246,6 +2243,7 @@ void LEX::cleanup_lex_after_parse_error(THD *thd)
void Query_tables_list::reset_query_tables_list(bool init)
{
sql_command= SQLCOM_END;
if (!init && query_tables)
{
TABLE_LIST *table= query_tables;
@ -2308,8 +2306,7 @@ void Query_tables_list::destroy_query_tables_list()
*/
LEX::LEX()
:result(0),
sql_command(SQLCOM_END), option_type(OPT_DEFAULT), is_lex_started(0)
:result(0), option_type(OPT_DEFAULT), is_lex_started(0)
{
my_init_dynamic_array2(&plugins, sizeof(plugin_ref),

View File

@ -739,14 +739,6 @@ public:
List<udf_func> udf_list; /* udf function calls stack */
/**
Per sub-query locking strategy.
Note: This variable might interfer with the corresponding statement-level
variable Lex::lock_option because on how different parser rules depend
on eachother.
*/
thr_lock_type lock_option;
/*
This is a copy of the original JOIN USING list that comes from
the parser. The parser :
@ -1005,8 +997,11 @@ extern const LEX_STRING empty_lex_str;
/*
Class representing list of all tables used by statement.
It also contains information about stored functions used by statement
Class representing list of all tables used by statement and other
information which is necessary for opening and locking its tables,
like SQL command for this statement.
Also contains information about stored functions used by statement
since during its execution we may have to add all tables used by its
stored functions/triggers to this list in order to pre-open and lock
them.
@ -1018,6 +1013,13 @@ extern const LEX_STRING empty_lex_str;
class Query_tables_list
{
public:
/**
SQL command for this statement. Part of this class since the
process of opening and locking tables for the statement needs
this information to determine correct type of lock for some of
the tables.
*/
enum_sql_command sql_command;
/* Global list of all tables used by this statement */
TABLE_LIST *query_tables;
/* Pointer to next_global member of last element in the previous list. */
@ -1920,7 +1922,6 @@ struct LEX: public Query_tables_list
the variable can contain 0 or 1 for each nest level.
*/
nesting_map allow_sum_func;
enum_sql_command sql_command;
Sql_statement *m_stmt;
@ -1932,7 +1933,6 @@ struct LEX: public Query_tables_list
*/
bool expr_allows_subselect;
thr_lock_type lock_option;
enum SSL_type ssl_type; /* defined in violite.h */
enum enum_duplicates duplicates;
enum enum_tx_isolation tx_isolation;
@ -2248,10 +2248,20 @@ public:
yacc_yyss= NULL;
yacc_yyvs= NULL;
m_set_signal_info.clear();
m_lock_type= TL_READ_DEFAULT;
}
~Yacc_state();
/**
Reset part of the state which needs resetting before parsing
substatement.
*/
void reset_before_substatement()
{
m_lock_type= TL_READ_DEFAULT;
}
/**
Bison internal state stack, yyss, when dynamically allocated using
my_yyoverflow().
@ -2270,6 +2280,25 @@ public:
*/
Set_signal_information m_set_signal_info;
/**
Type of lock to be used for tables being added to the statement's
table list in table_factor, table_alias_ref, single_multi and
table_wild_one rules.
Statements which use these rules but require lock type different
from one specified by this member have to override it by using
st_select_lex::set_lock_for_tables() method.
The default value of this member is TL_READ_DEFAULT. The only two
cases in which we change it are:
- When parsing SELECT HIGH_PRIORITY.
- Rule for DELETE. In which we use this member to pass information
about type of lock from delete to single_multi part of rule.
We should try to avoid introducing new use cases as we would like
to get rid of this member eventually.
*/
thr_lock_type m_lock_type;
/*
TODO: move more attributes from the LEX structure here.
*/

View File

@ -132,6 +132,7 @@ static int read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
const char* db_arg, /* table's database */
const char* table_name_arg,
bool is_concurrent,
enum enum_duplicates duplicates,
bool ignore,
bool transactional_table,
@ -184,6 +185,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
char *tdb= thd->db ? thd->db : db; // Result is never null
ulong skip_lines= ex->skip_lines;
bool transactional_table;
bool is_concurrent;
THD::killed_state killed_status= THD::NOT_KILLED;
DBUG_ENTER("mysql_load");
@ -245,6 +247,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table= table_list->table;
transactional_table= table->file->has_transactions();
is_concurrent= (table_list->lock_type == TL_WRITE_CONCURRENT_INSERT);
if (!fields_vars.elements)
{
@ -562,6 +565,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
(void) write_execute_load_query_log_event(thd, ex,
table_list->db,
table_list->table_name,
is_concurrent,
handle_duplicates, ignore,
transactional_table,
errcode);
@ -610,6 +614,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
error= write_execute_load_query_log_event(thd, ex,
table_list->db, table_list->table_name,
is_concurrent,
handle_duplicates, ignore,
transactional_table,
errcode);
@ -638,6 +643,7 @@ err:
static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
const char* db_arg, /* table's database */
const char* table_name_arg,
bool is_concurrent,
enum enum_duplicates duplicates,
bool ignore,
bool transactional_table,
@ -673,8 +679,8 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
tbl= string_buf.c_ptr_safe();
}
Load_log_event lle(thd, ex, tdb, tbl, fv, duplicates,
ignore, transactional_table);
Load_log_event lle(thd, ex, tdb, tbl, fv, is_concurrent,
duplicates, ignore, transactional_table);
/*
force in a LOCAL if there was one in the original.

View File

@ -2765,7 +2765,7 @@ end_with_restore_list:
client thread has locked tables
*/
if (thd->locked_tables_mode ||
thd->active_transaction() || thd->global_read_lock.is_acquired())
thd->in_active_multi_stmt_transaction() || thd->global_read_lock.is_acquired())
{
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
@ -3273,7 +3273,7 @@ end_with_restore_list:
Don't allow this within a transaction because we want to use
re-generate table
*/
if (thd->active_transaction())
if (thd->in_active_multi_stmt_transaction())
{
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
@ -4703,6 +4703,9 @@ finish:
thd->global_read_lock.start_waiting_global_read_lock(thd);
}
DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
thd->in_multi_stmt_transaction_mode());
if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
{
/* If commit fails, we should be able to reset the OK status. */
@ -5516,7 +5519,7 @@ void THD::reset_for_next_command()
OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings
in ha_rollback_trans() about some tables couldn't be rolled back.
*/
if (!thd->in_multi_stmt_transaction())
if (!thd->in_multi_stmt_transaction_mode())
{
thd->variables.option_bits&= ~OPTION_KEEP_LOG;
thd->transaction.all.modified_non_trans_table= FALSE;
@ -5702,7 +5705,6 @@ void mysql_init_multi_delete(LEX *lex)
lex->select_lex.select_limit= 0;
lex->unit.select_limit_cnt= HA_POS_ERROR;
lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
lex->lock_option= TL_READ_DEFAULT;
lex->query_tables= 0;
lex->query_tables_last= &lex->query_tables;
}

View File

@ -1708,7 +1708,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
res= select_like_stmt_test(stmt, 0, 0);
lex->link_first_table_back(create_table, &link_to_local);
lex->link_first_table_back(create_table, link_to_local);
}
else
{
@ -3246,7 +3246,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
locks have already been released and our savepoint points
to ticket which has been released as well.
*/
if (thd->in_multi_stmt_transaction())
if (thd->in_multi_stmt_transaction_mode())
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena;

View File

@ -129,6 +129,12 @@ extern char err_shared_dir[];
*/
#define TMP_TABLE_FORCE_MYISAM (1ULL << 32)
#define OPTION_PROFILING (1ULL << 33)
/**
Indicates that this is a HIGH_PRIORITY SELECT.
Currently used only for printing of such selects.
Type of locks to be acquired is specified directly.
*/
#define SELECT_HIGH_PRIORITY (1ULL << 34) // SELECT, user
/* The rest of the file is included in the server only */

View File

@ -54,7 +54,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
if the user is trying to to do this in a transcation context
*/
if (thd->locked_tables_mode || thd->active_transaction())
if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
{
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));

View File

@ -17179,8 +17179,7 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
/* First add options */
if (options & SELECT_STRAIGHT_JOIN)
str->append(STRING_WITH_LEN("straight_join "));
if ((thd->lex->lock_option == TL_READ_HIGH_PRIORITY) &&
(this == &thd->lex->select_lex))
if (options & SELECT_HIGH_PRIORITY)
str->append(STRING_WITH_LEN("high_priority "));
if (options & SELECT_DISTINCT)
str->append(STRING_WITH_LEN("distinct "));

View File

@ -3341,7 +3341,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
LEX *lex= thd->lex;
TABLE *table= tables->table;
SELECT_LEX *old_all_select_lex= lex->all_selects_list;
enum_sql_command save_sql_command= lex->sql_command;
SELECT_LEX *lsel= tables->schema_select_lex;
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
SELECT_LEX sel;
@ -3377,6 +3376,12 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
lex->view_prepare_mode= TRUE;
lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
/*
Restore Query_tables_list::sql_command value, which was reset
above, as ST_SCHEMA_TABLE::process_table() functions often rely
that this value reflects which SHOW statement is executed.
*/
lex->sql_command= query_tables_list_backup.sql_command;
/*
We should not introduce deadlocks even if we already have some
@ -3539,7 +3544,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
(MYSQL_OPEN_IGNORE_FLUSH |
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
(can_deadlock ? MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
lex->sql_command= save_sql_command;
lex->sql_command= query_tables_list_backup.sql_command;
/*
XXX: show_table_list has a flag i_is_requested,
and when it's set, open_normal_and_derived_tables()
@ -3598,7 +3603,6 @@ err:
lex->derived_tables= derived_tables;
lex->all_selects_list= old_all_select_lex;
lex->view_prepare_mode= save_view_prepare_mode;
lex->sql_command= save_sql_command;
DBUG_RETURN(error);
}

View File

@ -4808,6 +4808,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
/* purecov: begin inspected */
char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
size_t length;
enum_sql_command save_sql_command= lex->sql_command;
DBUG_PRINT("admin", ("sending error message"));
protocol->prepare_for_resend();
protocol->store(table_name, system_charset_info);
@ -4821,6 +4822,11 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
close_thread_tables(thd);
thd->mdl_context.release_transactional_locks();
lex->reset_query_tables_list(FALSE);
/*
Restore Query_tables_list::sql_command value to make statement
safe for re-execution.
*/
lex->sql_command= save_sql_command;
table->table=0; // For query cache
if (protocol->write())
goto err;
@ -5018,7 +5024,7 @@ send_result_message:
/* Clear the ticket released in close_thread_tables(). */
table->mdl_request.ticket= NULL;
DEBUG_SYNC(thd, "ha_admin_open_ltable");
if (table->table= open_ltable(thd, table, lock_type, 0))
if ((table->table= open_ltable(thd, table, lock_type, 0)))
{
result_code= table->table->file->ha_analyze(thd, check_opt);
if (result_code == HA_ADMIN_ALREADY_DONE)
@ -6553,7 +6559,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
if the user is trying to to do this in a transcation context
*/
if (thd->locked_tables_mode || thd->active_transaction())
if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
{
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));

View File

@ -411,6 +411,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
destructive changes necessary to open the trigger's table.
*/
thd->lex->reset_n_backup_query_tables_list(&backup);
/*
Restore Query_tables_list::sql_command, which was
reset above, as the code that writes the query to the
binary log assumes that this value corresponds to the
statement that is being executed.
*/
thd->lex->sql_command= backup.sql_command;
if (add_table_for_trigger(thd, thd->lex->spname, if_exists, & tables))
goto end;

View File

@ -1053,7 +1053,7 @@ int mysql_multi_update_prepare(THD *thd)
be write-locked (for example, trigger to be invoked might try
to update this table).
*/
tl->lock_type= read_lock_type_for_table(thd, table);
tl->lock_type= read_lock_type_for_table(thd, lex, tl);
tl->updating= 0;
/* Update TABLE::lock_type accordingly. */
if (!tl->placeholder() && !using_lock_tables)

View File

@ -29,6 +29,7 @@
#define YYLEX_PARAM yythd
#define YYTHD ((THD *)yythd)
#define YYLIP (& YYTHD->m_parser_state->m_lip)
#define YYPS (& YYTHD->m_parser_state->m_yacc)
#define MYSQL_YACC
#define YYINITDEPTH 100
@ -4937,7 +4938,6 @@ create_select:
SELECT_SYM
{
LEX *lex=Lex;
lex->lock_option= TL_READ_DEFAULT;
if (lex->sql_command == SQLCOM_INSERT)
lex->sql_command= SQLCOM_INSERT_SELECT;
else if (lex->sql_command == SQLCOM_REPLACE)
@ -7302,7 +7302,6 @@ select_lock_type:
{
LEX *lex=Lex;
lex->current_select->set_lock_for_tables(TL_WRITE);
lex->current_select->lock_option= TL_WRITE;
lex->safe_to_cache_query=0;
lex->protect_against_global_read_lock= TRUE;
}
@ -7311,7 +7310,6 @@ select_lock_type:
LEX *lex=Lex;
lex->current_select->
set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS);
lex->current_select->lock_option= TL_READ_WITH_SHARED_LOCKS;
lex->safe_to_cache_query=0;
}
;
@ -9221,7 +9219,7 @@ table_factor:
{
if (!($$= Select->add_table_to_list(YYTHD, $2, $3,
Select->get_table_join_options(),
Lex->lock_option,
YYPS->m_lock_type,
Select->pop_index_hints())))
MYSQL_YYABORT;
Select->add_joined_table($$);
@ -10278,7 +10276,7 @@ table_alias_ref:
{
if (!Select->add_table_to_list(YYTHD, $1, NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS,
Lex->lock_option ))
YYPS->m_lock_type))
MYSQL_YYABORT;
}
;
@ -10303,8 +10301,6 @@ insert:
lex->sql_command= SQLCOM_INSERT;
lex->duplicates= DUP_ERROR;
mysql_init_select(lex);
/* for subselects */
lex->lock_option= TL_READ_DEFAULT;
}
insert_lock_option
opt_ignore insert2
@ -10495,7 +10491,6 @@ update:
LEX *lex= Lex;
mysql_init_select(lex);
lex->sql_command= SQLCOM_UPDATE;
lex->lock_option= TL_UNLOCK; /* Will be set later */
lex->duplicates= DUP_ERROR;
}
opt_low_priority opt_ignore join_table_list
@ -10562,7 +10557,7 @@ delete:
LEX *lex= Lex;
lex->sql_command= SQLCOM_DELETE;
mysql_init_select(lex);
lex->lock_option= TL_WRITE_DEFAULT;
YYPS->m_lock_type= TL_WRITE_DEFAULT;
lex->ignore= 0;
lex->select_lex.init_order();
}
@ -10573,20 +10568,27 @@ single_multi:
FROM table_ident
{
if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING,
Lex->lock_option))
YYPS->m_lock_type))
MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_DEFAULT;
}
where_clause opt_order_clause
delete_limit_clause {}
| table_wild_list
{ mysql_init_multi_delete(Lex); }
{
mysql_init_multi_delete(Lex);
YYPS->m_lock_type= TL_READ_DEFAULT;
}
FROM join_table_list where_clause
{
if (multi_delete_set_locks_and_link_aux_tables(Lex))
MYSQL_YYABORT;
}
| FROM table_alias_ref_list
{ mysql_init_multi_delete(Lex); }
{
mysql_init_multi_delete(Lex);
YYPS->m_lock_type= TL_READ_DEFAULT;
}
USING join_table_list where_clause
{
if (multi_delete_set_locks_and_link_aux_tables(Lex))
@ -10609,7 +10611,7 @@ table_wild_one:
ti,
NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS,
Lex->lock_option))
YYPS->m_lock_type))
MYSQL_YYABORT;
}
| ident '.' ident opt_wild
@ -10621,7 +10623,7 @@ table_wild_one:
ti,
NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS,
Lex->lock_option))
YYPS->m_lock_type))
MYSQL_YYABORT;
}
;
@ -10638,7 +10640,7 @@ opt_delete_options:
opt_delete_option:
QUICK { Select->options|= OPTION_QUICK; }
| LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; }
| LOW_PRIORITY { YYPS->m_lock_type= TL_WRITE_LOW_PRIORITY; }
| IGNORE_SYM { Lex->ignore= 1; }
;
@ -10724,7 +10726,6 @@ show:
{
LEX *lex=Lex;
lex->wild=0;
lex->lock_option= TL_READ;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
bzero((char*) &lex->create_info,sizeof(lex->create_info));
@ -11077,7 +11078,6 @@ describe:
describe_command table_ident
{
LEX *lex= Lex;
lex->lock_option= TL_READ;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
lex->sql_command= SQLCOM_SHOW_FIELDS;
@ -11291,7 +11291,6 @@ load:
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_LOAD;
lex->lock_option= $4;
lex->local_file= $5;
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
@ -11302,7 +11301,7 @@ load:
{
LEX *lex=Lex;
if (!Select->add_table_to_list(YYTHD, $12, NULL, TL_OPTION_UPDATING,
lex->lock_option))
$4))
MYSQL_YYABORT;
lex->field_list.empty();
lex->update_list.empty();
@ -13734,17 +13733,6 @@ subselect_start:
subselect_end:
{
LEX *lex=Lex;
/*
Set the required lock level for the tables associated with the
current sub-select. This will overwrite previous lock options set
using st_select_lex::add_table_to_list in any of the following
rules: single_multi, table_wild_one, load_data, table_alias_ref,
table_factor.
The default lock level is TL_READ_DEFAULT but it can be modified
with query options specific for a certain (sub-)SELECT.
*/
lex->current_select->
set_lock_for_tables(lex->current_select->lock_option);
lex->pop_context();
SELECT_LEX *child= lex->current_select;
@ -13776,8 +13764,8 @@ query_expression_option:
{
if (check_simple_select())
MYSQL_YYABORT;
Lex->lock_option= TL_READ_HIGH_PRIORITY;
Lex->current_select->lock_option= TL_READ_HIGH_PRIORITY;
YYPS->m_lock_type= TL_READ_HIGH_PRIORITY;
Select->options|= SELECT_HIGH_PRIORITY;
}
| DISTINCT { Select->options|= SELECT_DISTINCT; }
| SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; }

View File

@ -295,7 +295,7 @@ static bool binlog_format_check(sys_var *self, THD *thd, set_var *var)
/*
Make the session variable 'binlog_format' read-only inside a transaction.
*/
if (thd->active_transaction())
if (thd->in_active_multi_stmt_transaction())
{
my_error(ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT, MYF(0));
return true;
@ -348,7 +348,7 @@ static bool binlog_direct_check(sys_var *self, THD *thd, set_var *var)
Makes the session variable 'binlog_direct_non_transactional_updates'
read-only inside a transaction.
*/
if (thd->active_transaction())
if (thd->in_active_multi_stmt_transaction())
{
my_error(ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT, MYF(0));
return true;
@ -1428,7 +1428,7 @@ static my_bool read_only;
static bool check_read_only(sys_var *self, THD *thd, set_var *var)
{
/* Prevent self dead-lock */
if (thd->locked_tables_mode || thd->active_transaction())
if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
{
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
return true;
@ -2006,15 +2006,20 @@ static Sys_var_ulong Sys_thread_pool_size(
VALID_RANGE(1, 16384), DEFAULT(20), BLOCK_SIZE(0));
#endif
// Can't change the 'next' tx_isolation if we are already in a transaction
/**
Can't change the 'next' tx_isolation if we are already in a
transaction.
*/
static bool check_tx_isolation(sys_var *self, THD *thd, set_var *var)
{
if (var->type == OPT_DEFAULT && (thd->server_status & SERVER_STATUS_IN_TRANS))
if (var->type == OPT_DEFAULT && thd->in_active_multi_stmt_transaction())
{
DBUG_ASSERT(thd->in_multi_stmt_transaction_mode());
my_error(ER_CANT_CHANGE_TX_ISOLATION, MYF(0));
return true;
return TRUE;
}
return false;
return FALSE;
}
/*
@ -2027,6 +2032,7 @@ static bool fix_tx_isolation(sys_var *self, THD *thd, enum_var_type type)
thd->session_tx_isolation= (enum_tx_isolation)thd->variables.tx_isolation;
return false;
}
// NO_CMD_LINE - different name of the option
static Sys_var_enum Sys_tx_isolation(
"tx_isolation", "Default transaction isolation level",

View File

@ -169,7 +169,7 @@ bool trans_commit_implicit(THD *thd)
if (trans_check(thd))
DBUG_RETURN(TRUE);
if (thd->in_multi_stmt_transaction() ||
if (thd->in_multi_stmt_transaction_mode() ||
(thd->variables.option_bits & OPTION_TABLE_LOCK))
{
/* Safety if one did "drop table" on locked tables */
@ -305,7 +305,7 @@ bool trans_savepoint(THD *thd, LEX_STRING name)
SAVEPOINT **sv, *newsv;
DBUG_ENTER("trans_savepoint");
if (!(thd->in_multi_stmt_transaction() || thd->in_sub_stmt) ||
if (!(thd->in_multi_stmt_transaction_mode() || thd->in_sub_stmt) ||
!opt_using_transactions)
DBUG_RETURN(FALSE);
@ -467,7 +467,7 @@ bool trans_xa_start(THD *thd)
my_error(ER_XAER_INVAL, MYF(0));
else if (xa_state != XA_NOTR)
my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
else if (thd->locked_tables_mode || thd->active_transaction())
else if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
my_error(ER_XAER_OUTSIDE, MYF(0));
else if (xid_cache_search(thd->lex->xid))
my_error(ER_XAER_DUPID, MYF(0));