Initial import of WL#3726 "DDL locking for all metadata objects".

Backport of:
------------------------------------------------------------
revno: 2630.4.1
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w
timestamp: Fri 2008-05-23 17:54:03 +0400
message:
  WL#3726 "DDL locking for all metadata objects".

  After review fixes in progress.
------------------------------------------------------------

This is the first patch in series. It transforms the metadata 
locking subsystem to use a dedicated module (mdl.h,cc). No 
significant changes in the locking protocol. 
The import passes the test suite with the exception of 
deprecated/removed 6.0 features, and MERGE tables. The latter
are subject to a fix by WL#4144.
Unfortunately, the original changeset comments got lost in a merge,
thus this import has its own (largely insufficient) comments.

This patch fixes Bug#25144 "replication / binlog with view breaks".
Warning: this patch introduces an incompatible change:
Under LOCK TABLES, it's no longer possible to FLUSH a table that 
was not locked for WRITE.
Under LOCK TABLES, it's no longer possible to DROP a table or
VIEW that was not locked for WRITE.

******
Backport of:
------------------------------------------------------------
revno: 2630.4.2
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w
timestamp: Sat 2008-05-24 14:03:45 +0400
message:
  WL#3726 "DDL locking for all metadata objects".

  After review fixes in progress.

******
Backport of:
------------------------------------------------------------
revno: 2630.4.3
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w
timestamp: Sat 2008-05-24 14:08:51 +0400
message:
  WL#3726 "DDL locking for all metadata objects"

  Fixed failing Windows builds by adding mdl.cc to the lists
  of files needed to build server/libmysqld on Windows.

******
Backport of:
------------------------------------------------------------
revno: 2630.4.4
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w
timestamp: Sat 2008-05-24 21:57:58 +0400
message:
  WL#3726 "DDL locking for all metadata objects".

  Fix for assert failures in kill.test which occured when one
  tried to kill ALTER TABLE statement on merge table while it
  was waiting in wait_while_table_is_used() for other connections
  to close this table.

  These assert failures stemmed from the fact that cleanup code
  in this case assumed that temporary table representing new
  version of table was open with adding to THD::temporary_tables
  list while code which were opening this temporary table wasn't
  always fulfilling this.

  This patch changes code that opens new version of table to
  always do this linking in. It also streamlines cleanup process
  for cases when error occurs while we have new version of table
  open.

******
WL#3726 "DDL locking for all metadata objects"
Add libmysqld/mdl.cc to .bzrignore.
******
Backport of:
------------------------------------------------------------
revno: 2630.4.6
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w
timestamp: Sun 2008-05-25 00:33:22 +0400
message:
  WL#3726 "DDL locking for all metadata objects".

  Addition to the fix of assert failures in kill.test caused by
  changes for this worklog.


Make sure we close the new table only once.
This commit is contained in:
Konstantin Osipov 2009-11-30 18:55:03 +03:00
parent 0a9d4e675a
commit 69b9761f29
77 changed files with 4804 additions and 2135 deletions

View File

@ -3070,3 +3070,4 @@ libmysqld/rpl_handler.cc
libmysqld/debug_sync.cc
libmysqld/rpl_handler.cc
dbug/tests
libmysqld/mdl.cc

View File

@ -133,6 +133,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql/partition_info.cc ../sql/sql_connect.cc
../sql/scheduler.cc ../sql/event_parse_data.cc
../sql/sql_signal.cc ../sql/rpl_handler.cc
../sql/mdl.cc
${GEN_SOURCES}
${LIB_SOURCES})

View File

@ -79,7 +79,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
sql_tablespace.cc \
rpl_injector.cc my_user.c partition_info.cc \
sql_servers.cc event_parse_data.cc sql_signal.cc \
rpl_handler.cc
rpl_handler.cc mdl.cc
libmysqld_int_a_SOURCES= $(libmysqld_sources)
nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources)

View File

@ -518,12 +518,15 @@ connect (flush,localhost,root,,);
connection flush;
--echo connection: flush
--send flush tables;
connection default;
--echo connection: default
connect (waiter,localhost,root,,);
connection waiter;
--echo connection: waiter
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables";
--source include/wait_condition.inc
connection default;
--echo connection: default
handler t2 open;
handler t2 read first;
handler t1 read next;
@ -550,12 +553,14 @@ connect (flush,localhost,root,,);
connection flush;
--echo connection: flush
--send rename table t1 to t2;
connection default;
--echo connection: default
connection waiter;
--echo connection: waiter
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and info = "rename table t1 to t2";
--source include/wait_condition.inc
connection default;
--echo connection: default
handler t2 open;
handler t2 read first;
--error ER_NO_SUCH_TABLE
@ -566,7 +571,13 @@ connection flush;
reap;
connection default;
drop table t2;
connection flush;
disconnect flush;
--source include/wait_until_disconnected.inc
connection waiter;
disconnect waiter;
--source include/wait_until_disconnected.inc
connection default;
#
# Bug#30882 Dropping a temporary table inside a stored function may cause a server crash
@ -699,19 +710,24 @@ handler t1 read a next;
# Bug#41112: crash in mysql_ha_close_table/get_lock_data with alter table
#
connect(con1,localhost,root,,);
connect(con2,localhost,root,,);
connection default;
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (a int);
insert into t1 values (1);
handler t1 open;
connect(con1,localhost,root,,);
connection con1;
send alter table t1 engine=memory;
connection default;
connection con2;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "rename result table" and info = "alter table t1 engine=memory";
where state = "Waiting for table" and info = "alter table t1 engine=memory";
--source include/wait_condition.inc
connection default;
--error ER_ILLEGAL_HA
handler t1 read a next;
handler t1 close;
@ -720,6 +736,9 @@ connection con1;
drop table t1;
disconnect con1;
--source include/wait_until_disconnected.inc
connection con2;
disconnect con2;
--source include/wait_until_disconnected.inc
connection default;
#

View File

@ -785,7 +785,7 @@ drop table t1;
create table t1 select * from t2;
ERROR 42S02: Table 'test.t2' doesn't exist
create table t1 select * from t1;
ERROR HY000: You can't specify target table 't1' for update in FROM clause
ERROR 42S02: Table 'test.t1' doesn't exist
create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin);
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,EXPLICIT) and (latin1_bin,EXPLICIT) for operation 'coalesce'
create table t1 (primary key(a)) select "b" as b;
@ -805,6 +805,11 @@ Note 1050 Table 't1' already exists
select * from t1;
i
1
create table if not exists t1 select * from t1;
ERROR HY000: You can't specify target table 't1' for update in FROM clause
select * from t1;
i
1
create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin);
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,EXPLICIT) and (latin1_bin,EXPLICIT) for operation 'coalesce'
select * from t1;

View File

@ -33,6 +33,9 @@ flush tables with read lock;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
lock table t1 read;
flush tables with read lock;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables;
flush tables with read lock;
lock table t1 write;
ERROR HY000: Can't execute the query because you have a conflicting read lock
lock table t1 read;
@ -46,6 +49,7 @@ flush tables with read lock;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
lock table t1 read, t2 read, t3 read;
flush tables with read lock;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables;
drop table t1, t2, t3;
create table t1 (c1 int);
@ -69,6 +73,7 @@ ERROR HY000: Can't execute the given command because you have active locked tabl
unlock tables;
lock tables t1 read;
flush tables with read lock;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables;
drop table t1, t2;
set session low_priority_updates=default;

View File

@ -3,21 +3,14 @@ create table t1 (a int not null auto_increment primary key);
insert into t1 values(0);
lock table t1 read;
flush table t1;
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
unlock tables;
lock table t1 write;
flush table t1;
check table t1;
Table Op Msg_type Msg_text
test.t1 check status OK
unlock tables;
lock table t1 read;
lock table t1 read;
flush table t1;
select * from t1;
a
1
unlock tables;
select * from t1;
a
1
unlock tables;
lock table t1 write;
lock table t1 read;
flush table t1;
@ -26,7 +19,7 @@ a
1
unlock tables;
unlock tables;
lock table t1 read;
lock table t1 write;
lock table t1 write;
flush table t1;
select * from t1;

View File

@ -548,6 +548,7 @@ c1
1
connection: flush
flush tables;;
connection: waiter
connection: default
handler t2 open;
handler t2 read first;
@ -567,6 +568,7 @@ handler t1 read first;
c1
connection: flush
rename table t1 to t2;;
connection: waiter
connection: default
handler t2 open;
handler t2 read first;

View File

@ -547,6 +547,7 @@ c1
1
connection: flush
flush tables;;
connection: waiter
connection: default
handler t2 open;
handler t2 read first;
@ -566,6 +567,7 @@ handler t1 read first;
c1
connection: flush
rename table t1 to t2;;
connection: waiter
connection: default
handler t2 open;
handler t2 read first;

View File

@ -1644,6 +1644,57 @@ TEST_RESULT
OK
SET TIMESTAMP=DEFAULT;
End of 5.1 tests.
#
# Additional test for WL#3726 "DDL locking for all metadata objects"
# To avoid possible deadlocks process of filling of I_S tables should
# use high-priority metadata lock requests when opening tables.
# Below we just test that we really use high-priority lock request
# since reproducing a deadlock will require much more complex test.
#
drop tables if exists t1, t2, t3;
create table t1 (i int);
create table t2 (j int primary key auto_increment);
# Switching to connection 'con3726_1'
lock table t2 read;
# Switching to connection 'con3726_2'
# RENAME below will be blocked by 'lock table t2 read' above but
# will add two pending requests for exclusive metadata locks.
rename table t2 to t3;
# Switching to connection 'default'
# These statements should not be blocked by pending lock requests
select table_name, column_name, data_type from information_schema.columns
where table_schema = 'test' and table_name in ('t1', 't2');
table_name column_name data_type
t1 i int
t2 j int
select table_name, auto_increment from information_schema.tables
where table_schema = 'test' and table_name in ('t1', 't2');
table_name auto_increment
t1 NULL
t2 1
# Switching to connection 'con3726_1'
unlock tables;
# Switching to connection 'con3726_2'
# Switching to connection 'default'
drop tables t1, t3;
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE KEY_COLUMN_USAGE ALL NULL NULL NULL NULL NULL Open_full_table; Scanned all databases
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE PARTITIONS ALL NULL TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 1 database
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA='test';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE REFERENTIAL_CONSTRAINTS ALL NULL CONSTRAINT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE TABLE_CONSTRAINTS ALL NULL TABLE_SCHEMA,TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 0 databases
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS
WHERE EVENT_OBJECT_SCHEMA='test';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE TRIGGERS ALL NULL EVENT_OBJECT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database
create table information_schema.t1 (f1 INT);
ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema'
drop table information_schema.t1;
@ -1682,28 +1733,10 @@ ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_
LOCK TABLES t1 READ, information_schema.tables READ;
ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema'
DROP TABLE t1;
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE KEY_COLUMN_USAGE ALL NULL NULL NULL NULL NULL Open_full_table; Scanned all databases
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE PARTITIONS ALL NULL TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 1 database
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA='test';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE REFERENTIAL_CONSTRAINTS ALL NULL CONSTRAINT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE TABLE_CONSTRAINTS ALL NULL TABLE_SCHEMA,TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 0 databases
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS
WHERE EVENT_OBJECT_SCHEMA='test';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE TRIGGERS ALL NULL EVENT_OBJECT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
LEFT JOIN INFORMATION_SCHEMA.COLUMNS
USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME)
WHERE COLUMNS.TABLE_SCHEMA = 'test'
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
LEFT JOIN INFORMATION_SCHEMA.COLUMNS
USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME)
WHERE COLUMNS.TABLE_SCHEMA = 'test'
AND COLUMNS.TABLE_NAME = 't1';
TABLE_SCHEMA TABLE_NAME COLUMN_NAME CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME TABLE_CATALOG ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE COLUMN_KEY EXTRA PRIVILEGES COLUMN_COMMENT

View File

@ -138,4 +138,107 @@ KILL CONNECTION_ID();
# of close of the connection socket
SELECT 1;
Got one of the listed errors
#
# Additional test for WL#3726 "DDL locking for all metadata objects"
# Check that DDL and DML statements waiting for metadata locks can
# be killed. Note that we don't cover all situations here since it
# can be tricky to write test case for some of them (e.g. REPAIR or
# ALTER and other statements under LOCK TABLES).
#
drop tables if exists t1, t2, t3;
create table t1 (i int primary key);
# Test for RENAME TABLE
# Switching to connection 'blocker'
lock table t1 read;
# Switching to connection 'ddl'
rename table t1 to t2;
# Switching to connection 'default'
kill query ID;
# Switching to connection 'ddl'
ERROR 70100: Query execution was interrupted
# Test for DROP TABLE
drop table t1;
# Switching to connection 'default'
kill query ID;
# Switching to connection 'ddl'
ERROR 70100: Query execution was interrupted
# Test for CREATE TRIGGER
create trigger t1_bi before insert on t1 for each row set @a:=1;
# Switching to connection 'default'
kill query ID;
# Switching to connection 'ddl'
ERROR 70100: Query execution was interrupted
#
# Tests for various kinds of ALTER TABLE
#
# Full-blown ALTER which should copy table
alter table t1 add column j int;
# Switching to connection 'default'
kill query ID;
# Switching to connection 'ddl'
ERROR 70100: Query execution was interrupted
# Two kinds of simple ALTER
alter table t1 rename to t2;
# Switching to connection 'default'
kill query ID;
# Switching to connection 'ddl'
ERROR 70100: Query execution was interrupted
alter table t1 disable keys;
# Switching to connection 'default'
kill query ID;
# Switching to connection 'ddl'
ERROR 70100: Query execution was interrupted
# Fast ALTER
alter table t1 alter column i set default 100;
# Switching to connection 'default'
kill query ID;
# Switching to connection 'ddl'
ERROR 70100: Query execution was interrupted
# Special case which is triggered only for MERGE tables.
# Switching to connection 'blocker'
unlock tables;
create table t2 (i int primary key) engine=merge union=(t1);
lock tables t2 read;
# Switching to connection 'ddl'
alter table t2 alter column i set default 100;
# Switching to connection 'default'
kill query ID;
# Switching to connection 'ddl'
ERROR 70100: Query execution was interrupted
# Test for DML waiting for meta-data lock
# Switching to connection 'blocker'
unlock tables;
drop table t2;
create table t2 (k int);
lock tables t1 read;
# Switching to connection 'ddl'
rename tables t1 to t3, t2 to t1;
# Switching to connection 'dml'
insert into t2 values (1);
# Switching to connection 'default'
kill query ID2;
# Switching to connection 'dml'
ERROR 70100: Query execution was interrupted
# Switching to connection 'blocker'
unlock tables;
# Switching to connection 'ddl'
# Test for DML waiting for tables to be flushed
# Switching to connection 'blocker'
lock tables t1 read;
# Switching to connection 'ddl'
# Let us mark locked table t1 as old
flush tables;
# Switching to connection 'dml'
select * from t1;
# Switching to connection 'default'
kill query ID2;
# Switching to connection 'dml'
ERROR 70100: Query execution was interrupted
# Switching to connection 'blocker'
unlock tables;
# Switching to connection 'ddl'
# Cleanup.
# Switching to connection 'default'
drop table t3;
drop table t1;
set @@global.concurrent_insert= @old_concurrent_insert;

View File

@ -128,13 +128,14 @@ select * from v_bug5719;
1
1
drop view v_bug5719;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
sic: did not left LOCK TABLES mode automatically
select * from t1;
ERROR HY000: Table 't1' was not locked with LOCK TABLES
unlock tables;
create view v_bug5719 as select * from t1;
create or replace view v_bug5719 as select * from t1;
lock tables v_bug5719 write;
select * from v_bug5719;
a

View File

@ -56,11 +56,11 @@ insert into t1 values (2,5), (2,15), (2,25),
insert into t1 select * from t1;
explain partitions select * from t1 where a=2;
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 p01,p02,p03,p11 ALL NULL NULL NULL NULL 13 Using where
1 SIMPLE t1 p01,p02,p03,p11 ALL NULL NULL NULL NULL 8 Using where
explain partitions select * from t1 where a=4;
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 p11,p12,p13,p21 ALL NULL NULL NULL NULL 16 Using where
1 SIMPLE t1 p11,p12,p13,p21 ALL NULL NULL NULL NULL 14 Using where
explain partitions select * from t1 where a=2 and b < 22;
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE t1 p01,p02,p03 ALL NULL NULL NULL NULL 16 Using where
1 SIMPLE t1 p01,p02,p03 ALL NULL NULL NULL NULL 14 Using where
drop table t1;

View File

@ -1960,7 +1960,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
explain partitions
select * from t1 X, t1 Y where X.a = Y.a and (X.a=1 or X.a=2);
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE X p1,p2 ALL a NULL NULL NULL 4 Using where
1 SIMPLE X p1,p2 ALL a NULL NULL NULL 2 Using where
1 SIMPLE Y p1,p2 ref a a 4 test.X.a 2
drop table t1;
create table t1 (a int) partition by hash(a) partitions 20;

View File

@ -850,7 +850,7 @@ flush table t1;
execute stmt;
f1()
6
call p_verify_reprepare_count(0);
call p_verify_reprepare_count(1);
SUCCESS
execute stmt;
@ -1706,7 +1706,7 @@ SUCCESS
drop temporary table t2;
execute stmt;
call p_verify_reprepare_count(1);
call p_verify_reprepare_count(0);
SUCCESS
drop table t2;

View File

@ -1083,11 +1083,9 @@ select f0()|
f0()
100
select * from v0|
f0()
100
ERROR HY000: Table 'v0' was not locked with LOCK TABLES
select *, f0() from v0, (select 123) as d1|
f0() 123 f0()
100 123 100
ERROR HY000: Table 'v0' was not locked with LOCK TABLES
select id, f3() from t1|
ERROR HY000: Table 't1' was not locked with LOCK TABLES
select f4()|

View File

@ -1102,10 +1102,8 @@ select * from v1;
a
select * from t2;
ERROR HY000: Table 't2' was not locked with LOCK TABLES
drop view v1;
drop table t1, t2;
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
unlock tables;
drop view v1;
drop table t1, t2;
create table t1 (a int);
create view v1 as select * from t1 where a < 2 with check option;

View File

@ -779,9 +779,9 @@ GRANT CREATE VIEW ON db26813.v2 TO u26813@localhost;
GRANT DROP, CREATE VIEW ON db26813.v3 TO u26813@localhost;
GRANT SELECT ON db26813.t1 TO u26813@localhost;
ALTER VIEW v1 AS SELECT f2 FROM t1;
ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
ERROR 42000: CREATE VIEW command denied to user 'u26813'@'localhost' for table 'v1'
ALTER VIEW v2 AS SELECT f2 FROM t1;
ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
ERROR 42000: DROP command denied to user 'u26813'@'localhost' for table 'v2'
ALTER VIEW v3 AS SELECT f2 FROM t1;
ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
SHOW CREATE VIEW v3;

View File

@ -0,0 +1,48 @@
reset master;
create table t1 (i int);
create table t2 (i int);
create view v1 as select * from t1;
select get_lock("lock_bg25144", 1);
get_lock("lock_bg25144", 1)
1
insert into v1 values (get_lock("lock_bg25144", 100));;
drop view v1;;
select release_lock("lock_bg25144");
release_lock("lock_bg25144")
1
select release_lock("lock_bg25144");
release_lock("lock_bg25144")
1
select * from t1;
i
1
create view v1 as select * from t1;
select get_lock("lock_bg25144", 1);
get_lock("lock_bg25144", 1)
1
insert into v1 values (get_lock("lock_bg25144", 100));;
alter view v1 as select * from t2;;
select release_lock("lock_bg25144");
release_lock("lock_bg25144")
1
select release_lock("lock_bg25144");
release_lock("lock_bg25144")
1
select * from t1;
i
1
1
select * from t2;
i
show binlog events in 'master-bin.000001' from 107;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query 1 # use `test`; create table t1 (i int)
master-bin.000001 # Query 1 # use `test`; create table t2 (i int)
master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1
master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100))
master-bin.000001 # Query 1 # use `test`; drop view v1
master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1
master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100))
master-bin.000001 # Query 1 # use `test`; ALTER ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t2
drop table t1, t2;
drop view v1;

View File

@ -13,3 +13,6 @@
rpl_get_master_version_and_clock: # Bug#46931 2009-10-17 joro rpl.rpl_get_master_version_and_clock fails
rpl_cross_version : BUG#43913 2009-10-22 luis rpl_cross_version fails with symptom in described in bug report
rpl_spec_variables : BUG#47661 2009-10-27 jasonh rpl_spec_variables fails on PB2 hpux
rpl_empty_master_crash : BUG#48048
rpl_load_from_master : BUG#48048
rpl_load_table_from_master : BUG#48048

View File

@ -683,8 +683,8 @@ drop table t1;
# Error during open_and_lock_tables() of tables
--error ER_NO_SUCH_TABLE
create table t1 select * from t2;
# Rather special error which also caught during open tables pahse
--error ER_UPDATE_TABLE_USED
# A special case which is also caught during open tables pahse
--error ER_NO_SUCH_TABLE
create table t1 select * from t1;
# Error which happens before select_create::prepare()
--error ER_CANT_AGGREGATE_2COLLATIONS
@ -706,6 +706,10 @@ create table t1 (i int);
create table t1 select 1 as i;
create table if not exists t1 select 1 as i;
select * from t1;
# Error which is detected after successfull table open.
--error ER_UPDATE_TABLE_USED
create table if not exists t1 select * from t1;
select * from t1;
# Error before select_create::prepare()
--error ER_CANT_AGGREGATE_2COLLATIONS
create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin);

View File

@ -15,3 +15,4 @@ partition_innodb_builtin : Bug#32430 2009-09-25 mattiasj Waiting for push of Inn
partition_innodb_plugin : Bug#32430 2009-09-25 mattiasj Waiting for push of Innodb changes
innodb-autoinc : Bug#48482 2009-11-02 svoj innodb-autoinc.test fails with results difference
rpl_killed_ddl : Bug#45520: rpl_killed_ddl fails sporadically in pb2
merge : WL#4144

View File

@ -68,10 +68,13 @@ drop table t1;
create table t1 (c1 int);
lock table t1 write;
# Cannot get the global read lock with write locked tables.
--error 1192
--error ER_LOCK_OR_ACTIVE_TRANSACTION
flush tables with read lock;
lock table t1 read;
# Can get the global read lock with read locked tables.
# Cannot get the global read lock with read locked tables.
--error ER_LOCK_OR_ACTIVE_TRANSACTION
flush tables with read lock;
unlock tables;
flush tables with read lock;
--error 1223
lock table t1 write;
@ -84,12 +87,12 @@ create table t2 (c1 int);
create table t3 (c1 int);
lock table t1 read, t2 read, t3 write;
# Cannot get the global read lock with write locked tables.
--error 1192
--error ER_LOCK_OR_ACTIVE_TRANSACTION
flush tables with read lock;
lock table t1 read, t2 read, t3 read;
# Can get the global read lock with read locked tables.
# Cannot get the global read lock with read locked tables.
--error ER_LOCK_OR_ACTIVE_TRANSACTION
flush tables with read lock;
# Release all table locks and the global read lock.
unlock tables;
drop table t1, t2, t3;
@ -157,6 +160,7 @@ flush tables with read lock;
unlock tables;
lock tables t1 read;
--error ER_LOCK_OR_ACTIVE_TRANSACTION
flush tables with read lock;
unlock tables;

View File

@ -15,30 +15,21 @@ insert into t1 values(0);
# Test for with read lock + flush
lock table t1 read;
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
flush table t1;
unlock tables;
# Test for with write lock + flush
lock table t1 write;
flush table t1;
check table t1;
unlock tables;
# Test for with 2 read lock in different thread + flush
lock table t1 read;
connect (locker,localhost,root,,test);
connection locker;
lock table t1 read;
connection default;
send flush table t1;
connection locker;
--sleep 2
select * from t1;
unlock tables;
connection default;
reap;
select * from t1;
unlock tables;
# Test for with a write lock and a waiting read lock + flush
lock table t1 write;
connect (locker,localhost,root,,test);
connection locker;
send lock table t1 read;
connection default;
@ -51,9 +42,9 @@ reap;
unlock tables;
connection default;
# Test for with a read lock and a waiting write lock + flush
# Test for with a write lock and a waiting write lock + flush
lock table t1 read;
lock table t1 write;
connection locker;
send lock table t1 write;
connection default;

View File

@ -544,6 +544,7 @@ AND table_name not like 'ndb%' AND table_name not like 'innodb_%'
GROUP BY TABLE_SCHEMA;
#
# TRIGGERS table test
#
@ -914,8 +915,8 @@ DROP PROCEDURE p1;
DROP USER mysql_bug20230@localhost;
#
# Bug#2123 query with a simple non-correlated subquery over
# INFORMARTION_SCHEMA.TABLES
# Bug#21231 query with a simple non-correlated subquery over
# INFORMARTION_SCHEMA.TABLES
#
SELECT MAX(table_name) FROM information_schema.tables WHERE table_schema IN ('mysql', 'INFORMATION_SCHEMA', 'test');
@ -1391,9 +1392,66 @@ SET TIMESTAMP=DEFAULT;
--echo End of 5.1 tests.
--echo #
--echo # Additional test for WL#3726 "DDL locking for all metadata objects"
--echo # To avoid possible deadlocks process of filling of I_S tables should
--echo # use high-priority metadata lock requests when opening tables.
--echo # Below we just test that we really use high-priority lock request
--echo # since reproducing a deadlock will require much more complex test.
--echo #
--disable_warnings
drop tables if exists t1, t2, t3;
--enable_warnings
create table t1 (i int);
create table t2 (j int primary key auto_increment);
connect (con3726_1,localhost,root,,test);
--echo # Switching to connection 'con3726_1'
connection con3726_1;
lock table t2 read;
connect (con3726_2,localhost,root,,test);
--echo # Switching to connection 'con3726_2'
connection con3726_2;
--echo # RENAME below will be blocked by 'lock table t2 read' above but
--echo # will add two pending requests for exclusive metadata locks.
--send rename table t2 to t3
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and info like "rename table t2 to t3";
--source include/wait_condition.inc
--echo # These statements should not be blocked by pending lock requests
select table_name, column_name, data_type from information_schema.columns
where table_schema = 'test' and table_name in ('t1', 't2');
select table_name, auto_increment from information_schema.tables
where table_schema = 'test' and table_name in ('t1', 't2');
--echo # Switching to connection 'con3726_1'
connection con3726_1;
unlock tables;
--echo # Switching to connection 'con3726_2'
connection con3726_2;
--reap
--echo # Switching to connection 'default'
connection default;
disconnect con3726_1;
disconnect con3726_2;
drop tables t1, t3;
#
# Bug#39270 I_S optimization algorithm does not work properly in some cases
#
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE;
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1';
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA='test';
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test';
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS
WHERE EVENT_OBJECT_SCHEMA='test';
#
# Bug#24062 Incorrect error msg after execute DROP TABLE IF EXISTS on information_schema
#
#
--error ER_DBACCESS_DENIED_ERROR
create table information_schema.t1 (f1 INT);
--error ER_DBACCESS_DENIED_ERROR
@ -1429,27 +1487,18 @@ DROP TABLE t1, information_schema.tables;
LOCK TABLES t1 READ, information_schema.tables READ;
DROP TABLE t1;
#
# Bug#39270 I_S optimization algorithm does not work properly in some cases
#
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE;
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1';
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA='test';
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test';
EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS
WHERE EVENT_OBJECT_SCHEMA='test';
#
# Bug #43834 Assertion in Natural_join_column::db_name() on an I_S query
#
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
LEFT JOIN INFORMATION_SCHEMA.COLUMNS
USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME)
WHERE COLUMNS.TABLE_SCHEMA = 'test'
AND COLUMNS.TABLE_NAME = 't1';
# Wait till all disconnects are completed
--source include/wait_until_count_sessions.inc
#
# Bug #43834 Assertion in Natural_join_column::db_name() on an I_S query
#
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
LEFT JOIN INFORMATION_SCHEMA.COLUMNS
USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME)
WHERE COLUMNS.TABLE_SCHEMA = 'test'
AND COLUMNS.TABLE_NAME = 't1';

View File

@ -329,6 +329,232 @@ KILL CONNECTION_ID();
SELECT 1;
--connection default
--echo #
--echo # Additional test for WL#3726 "DDL locking for all metadata objects"
--echo # Check that DDL and DML statements waiting for metadata locks can
--echo # be killed. Note that we don't cover all situations here since it
--echo # can be tricky to write test case for some of them (e.g. REPAIR or
--echo # ALTER and other statements under LOCK TABLES).
--echo #
--disable_warnings
drop tables if exists t1, t2, t3;
--enable_warnings
create table t1 (i int primary key);
connect (blocker, localhost, root, , );
connect (dml, localhost, root, , );
connect (ddl, localhost, root, , );
--echo # Test for RENAME TABLE
--echo # Switching to connection 'blocker'
connection blocker;
lock table t1 read;
--echo # Switching to connection 'ddl'
connection ddl;
let $ID= `select connection_id()`;
--send rename table t1 to t2
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and info = "rename table t1 to t2";
--source include/wait_condition.inc
--replace_result $ID ID
eval kill query $ID;
--echo # Switching to connection 'ddl'
connection ddl;
--error ER_QUERY_INTERRUPTED
--reap
--echo # Test for DROP TABLE
--send drop table t1
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "drop table t1";
--replace_result $ID ID
eval kill query $ID;
--echo # Switching to connection 'ddl'
connection ddl;
--error ER_QUERY_INTERRUPTED
--reap
--echo # Test for CREATE TRIGGER
--send create trigger t1_bi before insert on t1 for each row set @a:=1
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "create trigger t1_bi before insert on t1 for each row set @a:=1";
--replace_result $ID ID
eval kill query $ID;
--echo # Switching to connection 'ddl'
connection ddl;
--error ER_QUERY_INTERRUPTED
--reap
--echo #
--echo # Tests for various kinds of ALTER TABLE
--echo #
--echo # Full-blown ALTER which should copy table
--send alter table t1 add column j int
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "alter table t1 add column j int";
--replace_result $ID ID
eval kill query $ID;
--echo # Switching to connection 'ddl'
connection ddl;
--error ER_QUERY_INTERRUPTED
--reap
--echo # Two kinds of simple ALTER
--send alter table t1 rename to t2
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "alter table t1 rename to t2";
--replace_result $ID ID
eval kill query $ID;
--echo # Switching to connection 'ddl'
connection ddl;
--error ER_QUERY_INTERRUPTED
--reap
--send alter table t1 disable keys
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "alter table t1 disable keys";
--replace_result $ID ID
eval kill query $ID;
--echo # Switching to connection 'ddl'
connection ddl;
--error ER_QUERY_INTERRUPTED
--reap
--echo # Fast ALTER
--send alter table t1 alter column i set default 100
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "alter table t1 alter column i set default 100";
--replace_result $ID ID
eval kill query $ID;
--echo # Switching to connection 'ddl'
connection ddl;
--error ER_QUERY_INTERRUPTED
--reap
--echo # Special case which is triggered only for MERGE tables.
--echo # Switching to connection 'blocker'
connection blocker;
unlock tables;
create table t2 (i int primary key) engine=merge union=(t1);
lock tables t2 read;
--echo # Switching to connection 'ddl'
connection ddl;
--send alter table t2 alter column i set default 100
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "alter table t2 alter column i set default 100";
--replace_result $ID ID
eval kill query $ID;
--echo # Switching to connection 'ddl'
connection ddl;
--error ER_QUERY_INTERRUPTED
--reap
--echo # Test for DML waiting for meta-data lock
--echo # Switching to connection 'blocker'
connection blocker;
unlock tables;
drop table t2;
create table t2 (k int);
lock tables t1 read;
--echo # Switching to connection 'ddl'
connection ddl;
# Let us add pending exclusive metadata lock on t2
--send rename tables t1 to t3, t2 to t1
--echo # Switching to connection 'dml'
connection dml;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "rename tables t1 to t3, t2 to t1";
let $ID2= `select connection_id()`;
--send insert into t2 values (1)
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "insert into t2 values (1)";
--replace_result $ID2 ID2
eval kill query $ID2;
--echo # Switching to connection 'dml'
connection dml;
--error ER_QUERY_INTERRUPTED
--reap
--echo # Switching to connection 'blocker'
connection blocker;
unlock tables;
--echo # Switching to connection 'ddl'
connection ddl;
--reap
--echo # Test for DML waiting for tables to be flushed
--echo # Switching to connection 'blocker'
connection blocker;
lock tables t1 read;
--echo # Switching to connection 'ddl'
connection ddl;
--echo # Let us mark locked table t1 as old
--send flush tables
--echo # Switching to connection 'dml'
connection dml;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables" and
info = "flush tables";
--send select * from t1
--echo # Switching to connection 'default'
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "select * from t1";
--replace_result $ID2 ID2
eval kill query $ID2;
--echo # Switching to connection 'dml'
connection dml;
--error ER_QUERY_INTERRUPTED
--reap
--echo # Switching to connection 'blocker'
connection blocker;
unlock tables;
--echo # Switching to connection 'ddl'
connection ddl;
--reap
--echo # Cleanup.
--echo # Switching to connection 'default'
connection default;
drop table t3;
drop table t1;
###########################################################################
# Restore global concurrent_insert value. Keep in the end of the test file.

View File

@ -178,6 +178,7 @@ select * from t2;
--error ER_TABLE_NOT_LOCKED
select * from t3;
select * from v_bug5719;
--error ER_LOCK_OR_ACTIVE_TRANSACTION
drop view v_bug5719;
--echo
--echo sic: did not left LOCK TABLES mode automatically
@ -185,7 +186,7 @@ drop view v_bug5719;
--error ER_TABLE_NOT_LOCKED
select * from t1;
unlock tables;
create view v_bug5719 as select * from t1;
create or replace view v_bug5719 as select * from t1;
lock tables v_bug5719 write;
select * from v_bug5719;
--echo

View File

@ -164,7 +164,7 @@ connection locker;
# Sleep a bit till the select of connection reader is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and info =
where state = "Locked" and info =
"SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1";
--source include/wait_condition.inc
# Make test case independent from earlier grants.
@ -196,7 +196,7 @@ connection writer;
# Sleep a bit till the flush of connection locker is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables" and info = "FLUSH TABLES WITH READ LOCK";
where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK";
--source include/wait_condition.inc
# This must not block.
CREATE TABLE t2 (c1 int);
@ -226,7 +226,7 @@ connection writer;
# Sleep a bit till the flush of connection locker is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables" and info = "FLUSH TABLES WITH READ LOCK";
where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK";
--source include/wait_condition.inc
--error ER_TABLE_NOT_LOCKED
CREATE TABLE t2 AS SELECT * FROM t1;
@ -337,10 +337,10 @@ connection con2;
send flush tables with read lock;
connection con5;
--echo # con5
let $show_statement= SHOW PROCESSLIST;
let $field= State;
let $condition= = 'Flushing tables';
--source include/wait_show_condition.inc
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and info = "flush tables with read lock";
--source include/wait_condition.inc
--echo # global read lock is taken
connection con3;
--echo # con3
@ -528,7 +528,7 @@ connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables";
where state = "Waiting for table";
--source include/wait_condition.inc
alter table t1 add column j int;
connect (insert,localhost,root,,test,,);
@ -536,7 +536,7 @@ connection insert;
--echo connection: insert
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables";
where state = "Waiting for table";
--source include/wait_condition.inc
--send insert into t1 values (1,2);
--echo connection: default
@ -586,18 +586,14 @@ connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables";
where state = "Waiting for table";
--source include/wait_condition.inc
flush tables;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables";
where state = "Waiting for table";
--source include/wait_condition.inc
unlock tables;
let $wait_condition=
select count(*) = 0 from information_schema.processlist
where state = "Flushing tables";
--source include/wait_condition.inc
connection flush;
--reap
connection default;
@ -656,18 +652,14 @@ connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables";
where state = "Waiting for table";
--source include/wait_condition.inc
flush tables;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables";
where state = "Waiting for table";
--source include/wait_condition.inc
drop table t1;
let $wait_condition=
select count(*) = 0 from information_schema.processlist
where state = "Flushing tables";
--source include/wait_condition.inc
connection flush;
--reap
connection default;

View File

@ -745,7 +745,7 @@ execute stmt;
call p_verify_reprepare_count(1);
flush table t1;
execute stmt;
call p_verify_reprepare_count(0);
call p_verify_reprepare_count(1);
execute stmt;
--echo # Test 18-c: dependent VIEW has changed
@ -1453,7 +1453,7 @@ execute stmt;
call p_verify_reprepare_count(0);
drop temporary table t2;
execute stmt;
call p_verify_reprepare_count(1);
call p_verify_reprepare_count(0);
drop table t2;
execute stmt;
call p_verify_reprepare_count(0);

View File

@ -1308,9 +1308,11 @@ select f3()|
select id, f3() from t1 as t11 order by id|
# Degenerate cases work too :)
select f0()|
# But these should not (particularly views should be locked explicitly).
--error ER_TABLE_NOT_LOCKED
select * from v0|
--error ER_TABLE_NOT_LOCKED
select *, f0() from v0, (select 123) as d1|
# But these should not !
--error ER_TABLE_NOT_LOCKED
select id, f3() from t1|
--error ER_TABLE_NOT_LOCKED

View File

@ -896,7 +896,7 @@ connection default;
--echo connection: default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Flushing tables";
where state = "Waiting for table";
--source include/wait_condition.inc
create trigger t1_bi before insert on t1 for each row begin end;
unlock tables;

View File

@ -1016,10 +1016,8 @@ lock tables t1 read, v1 read;
select * from v1;
-- error ER_TABLE_NOT_LOCKED
select * from t2;
drop view v1;
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
drop table t1, t2;
unlock tables;
drop view v1;
drop table t1, t2;
#

View File

@ -1047,9 +1047,9 @@ GRANT SELECT ON db26813.t1 TO u26813@localhost;
connect (u1,localhost,u26813,,db26813);
connection u1;
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
--error ER_TABLEACCESS_DENIED_ERROR
ALTER VIEW v1 AS SELECT f2 FROM t1;
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
--error ER_TABLEACCESS_DENIED_ERROR
ALTER VIEW v2 AS SELECT f2 FROM t1;
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
ALTER VIEW v3 AS SELECT f2 FROM t1;

View File

@ -0,0 +1,110 @@
#
# QQ: Should we find a better place for this test?
# May be binlog or rpl suites ?
#
--source include/have_log_bin.inc
--source include/have_binlog_format_mixed_or_statement.inc
#
# Bug #25144 "replication / binlog with view breaks".
# Statements that used views didn't ensure that view were not modified
# during their execution. Indeed this led to incorrect binary log with
# statement based logging.
#
--disable_parsing
drop table if not exists t1, t2;
drop view if exists v1;
--enable_parsing
# We are going to use binary log later to check that statements are
# logged in proper order, so it is good idea to reset it here.
reset master;
connect (addconn1,localhost,root,,);
connect (addconn2,localhost,root,,);
connection default;
create table t1 (i int);
create table t2 (i int);
create view v1 as select * from t1;
# First we try to concurrently execute statement that uses view
# and statement that drops it. We use "user" locks as means to
# suspend execution of first statement once it opens our view.
select get_lock("lock_bg25144", 1);
connection addconn1;
--send insert into v1 values (get_lock("lock_bg25144", 100));
connection addconn2;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "User lock" and info like "insert into v1 %lock_bg25144%";
--source include/wait_condition.inc
--send drop view v1;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and info = "drop view v1";
--source include/wait_condition.inc
select release_lock("lock_bg25144");
connection addconn1;
--reap
select release_lock("lock_bg25144");
connection addconn2;
--reap
connection default;
# Check that insertion through view did happen.
select * from t1;
# At the end of test we will check that statements were
# logged in proper order.
# Now we will repeat the test by trying concurrently execute
# statement that uses a view and statement that alters it.
create view v1 as select * from t1;
select get_lock("lock_bg25144", 1);
connection addconn1;
--send insert into v1 values (get_lock("lock_bg25144", 100));
connection addconn2;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "User lock" and info like "insert into v1 %lock_bg25144%";
--source include/wait_condition.inc
--send alter view v1 as select * from t2;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and
info = "alter view v1 as select * from t2";
--source include/wait_condition.inc
select release_lock("lock_bg25144");
connection addconn1;
--reap
select release_lock("lock_bg25144");
connection addconn2;
--reap
connection default;
# Second insertion should go to t1 as well.
select * from t1;
select * from t2;
# Now let us check that statements were logged in proper order
--replace_column 2 # 5 #
show binlog events in 'master-bin.000001' from 107;
drop table t1, t2;
drop view v1;

View File

@ -76,7 +76,7 @@ SET (SQL_SOURCE
rpl_rli.cc rpl_mi.cc sql_servers.cc
sql_connect.cc scheduler.cc
sql_profile.cc event_parse_data.cc
sql_signal.cc rpl_handler.cc
sql_signal.cc rpl_handler.cc mdl.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
${PROJECT_SOURCE_DIR}/include/mysqld_error.h

View File

@ -112,7 +112,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
event_data_objects.h event_scheduler.h \
sql_partition.h partition_info.h partition_element.h \
contributors.h sql_servers.h sql_signal.h records.h \
sql_prepare.h rpl_handler.h replication.h
sql_prepare.h rpl_handler.h replication.h mdl.h \
sql_plist.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
@ -158,7 +159,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
sql_plugin.cc sql_binlog.cc \
sql_builtin.cc sql_tablespace.cc partition_info.cc \
sql_servers.cc event_parse_data.cc sql_signal.cc \
rpl_handler.cc
rpl_handler.cc mdl.cc
nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c

View File

@ -555,6 +555,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
DBUG_ENTER("Event_db_repository::open_event_table");
tables.init_one_table("mysql", "event", lock_type);
alloc_mdl_locks(&tables, thd->mem_root);
if (simple_open_n_lock_tables(thd, &tables))
{
@ -1109,6 +1110,7 @@ Event_db_repository::check_system_tables(THD *thd)
/* Check mysql.db */
tables.init_one_table("mysql", "db", TL_READ);
alloc_mdl_locks(&tables, thd->mem_root);
if (simple_open_n_lock_tables(thd, &tables))
{
@ -1126,6 +1128,7 @@ Event_db_repository::check_system_tables(THD *thd)
}
/* Check mysql.user */
tables.init_one_table("mysql", "user", TL_READ);
alloc_mdl_locks(&tables, thd->mem_root);
if (simple_open_n_lock_tables(thd, &tables))
{
@ -1146,6 +1149,7 @@ Event_db_repository::check_system_tables(THD *thd)
}
/* Check mysql.event */
tables.init_one_table("mysql", "event", TL_READ);
alloc_mdl_locks(&tables, thd->mem_root);
if (simple_open_n_lock_tables(thd, &tables))
{

View File

@ -565,7 +565,7 @@ int ha_ndbcluster::ndb_err(NdbTransaction *trans)
bzero((char*) &table_list,sizeof(table_list));
table_list.db= m_dbname;
table_list.alias= table_list.table_name= m_tabname;
close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE);
close_cached_tables(thd, &table_list, FALSE, FALSE);
break;
}
default:
@ -7276,6 +7276,20 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
}
}
/*
ndbcluster_find_files() may be called from I_S code and ndbcluster_binlog
thread in situations when some tables are already open. This means that
code below will try to obtain exclusive metadata lock on some table
while holding shared meta-data lock on other tables. This might lead to
a deadlock, and therefore is disallowed by assertions of the metadata
locking subsystem. In order to temporarily make the code work, we must
reset and backup the open tables state, thus hide the existing locks
from MDL asserts. But in the essence this is violation of metadata
locking protocol which has to be closed ASAP.
*/
Open_tables_state open_tables_state_backup;
thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
if (!global_read_lock)
{
// Delete old files
@ -7299,8 +7313,11 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
}
}
thd->restore_backup_open_tables_state(&open_tables_state_backup);
/* Lock mutex before creating .FRM files. */
pthread_mutex_lock(&LOCK_open);
// Create new files
/* Create new files. */
List_iterator_fast<char> it2(create_list);
while ((file_name_str=it2++))
{
@ -8215,7 +8232,7 @@ int handle_trailing_share(NDB_SHARE *share)
table_list.db= share->db;
table_list.alias= table_list.table_name= share->table_name;
safe_mutex_assert_owner(&LOCK_open);
close_cached_tables(thd, &table_list, TRUE, FALSE, FALSE);
close_cached_tables(thd, &table_list, TRUE, FALSE);
pthread_mutex_lock(&ndbcluster_mutex);
/* ndb_share reference temporary free */

View File

@ -140,6 +140,8 @@ static Uint64 *p_latest_trans_gci= 0;
*/
static TABLE *ndb_binlog_index= 0;
static TABLE_LIST binlog_tables;
static MDL_LOCK binlog_mdl_lock;
static char binlog_mdlkey[MAX_DBKEY_LENGTH];
/*
Helper functions
@ -282,7 +284,13 @@ static void run_query(THD *thd, char *buf, char *end,
thd_ndb->m_error_code,
(int) thd->is_error(), thd->is_slave_error);
}
/*
After executing statement we should unlock and close tables open
by it as well as release meta-data locks obtained by it.
*/
close_thread_tables(thd);
/*
XXX: this code is broken. mysql_parse()/mysql_reset_thd_for_next_command()
can not be called from within a statement, and
@ -921,7 +929,7 @@ int ndbcluster_setup_binlog_table_shares(THD *thd)
ndb_binlog_tables_inited= TRUE;
if (ndb_extra_logging)
sql_print_information("NDB Binlog: ndb tables writable");
close_cached_tables(NULL, NULL, TRUE, FALSE, FALSE);
close_cached_tables(NULL, NULL, TRUE, FALSE);
pthread_mutex_unlock(&LOCK_open);
/* Signal injector thread that all is setup */
pthread_cond_signal(&injector_cond);
@ -1735,7 +1743,7 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp,
bzero((char*) &table_list,sizeof(table_list));
table_list.db= (char *)dbname;
table_list.alias= table_list.table_name= (char *)tabname;
close_cached_tables(thd, &table_list, TRUE, FALSE, FALSE);
close_cached_tables(thd, &table_list, TRUE, FALSE);
if ((error= ndbcluster_binlog_open_table(thd, share,
table_share, table, 1)))
@ -1841,7 +1849,7 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp,
bzero((char*) &table_list,sizeof(table_list));
table_list.db= (char *)dbname;
table_list.alias= table_list.table_name= (char *)tabname;
close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE);
close_cached_tables(thd, &table_list, FALSE, FALSE);
/* ndb_share reference create free */
DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u",
share->key, share->use_count));
@ -1962,7 +1970,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
bzero((char*) &table_list,sizeof(table_list));
table_list.db= schema->db;
table_list.alias= table_list.table_name= schema->name;
close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE);
close_cached_tables(thd, &table_list, FALSE, FALSE);
}
/* ndb_share reference temporary free */
if (share)
@ -2079,7 +2087,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
pthread_mutex_unlock(&ndb_schema_share_mutex);
/* end protect ndb_schema_share */
close_cached_tables(NULL, NULL, FALSE, FALSE, FALSE);
close_cached_tables(NULL, NULL, FALSE, FALSE);
// fall through
case NDBEVENT::TE_ALTER:
ndb_handle_schema_change(thd, ndb, pOp, tmp_share);
@ -2236,7 +2244,7 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd,
bzero((char*) &table_list,sizeof(table_list));
table_list.db= schema->db;
table_list.alias= table_list.table_name= schema->name;
close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE);
close_cached_tables(thd, &table_list, FALSE, FALSE);
}
if (schema_type != SOT_ALTER_TABLE)
break;
@ -2323,18 +2331,21 @@ struct ndb_binlog_index_row {
/*
Open the ndb_binlog_index table
*/
static int open_ndb_binlog_index(THD *thd, TABLE_LIST *tables,
TABLE **ndb_binlog_index)
static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index)
{
static char repdb[]= NDB_REP_DB;
static char reptable[]= NDB_REP_TABLE;
const char *save_proc_info= thd->proc_info;
TABLE_LIST *tables= &binlog_tables;
bzero((char*) tables, sizeof(*tables));
tables->db= repdb;
tables->alias= tables->table_name= reptable;
tables->lock_type= TL_WRITE;
thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE;
mdl_init_lock(&binlog_mdl_lock, binlog_mdlkey, 0, tables->db,
tables->table_name);
tables->mdl_lock= &binlog_mdl_lock;
tables->required_type= FRMTYPE_TABLE;
uint counter;
thd->clear_error();
@ -2374,7 +2385,7 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row)
for ( ; ; ) /* loop for need_reopen */
{
if (!ndb_binlog_index && open_ndb_binlog_index(thd, &binlog_tables, &ndb_binlog_index))
if (!ndb_binlog_index && open_ndb_binlog_index(thd, &ndb_binlog_index))
{
error= -1;
goto add_ndb_binlog_index_err;
@ -2385,7 +2396,7 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row)
if (need_reopen)
{
TABLE_LIST *p_binlog_tables= &binlog_tables;
close_tables_for_reopen(thd, &p_binlog_tables);
close_tables_for_reopen(thd, &p_binlog_tables, FALSE);
ndb_binlog_index= 0;
continue;
}
@ -3893,7 +3904,7 @@ restart:
static char db[]= "";
thd->db= db;
if (ndb_binlog_running)
open_ndb_binlog_index(thd, &binlog_tables, &ndb_binlog_index);
open_ndb_binlog_index(thd, &ndb_binlog_index);
thd->db= db;
}
do_ndbcluster_binlog_close_connection= BCCC_running;

View File

@ -2997,20 +2997,13 @@ static bool update_frm_version(TABLE *table)
if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0)
{
uchar version[4];
char *key= table->s->table_cache_key.str;
uint key_length= table->s->table_cache_key.length;
TABLE *entry;
HASH_SEARCH_STATE state;
int4store(version, MYSQL_VERSION_ID);
if ((result= my_pwrite(file,(uchar*) version,4,51L,MYF_RW)))
goto err;
for (entry=(TABLE*) my_hash_first(&open_cache,(uchar*) key,key_length, &state);
entry;
entry= (TABLE*) my_hash_next(&open_cache,(uchar*) key,key_length, &state))
entry->s->mysql_version= MYSQL_VERSION_ID;
table->s->mysql_version= MYSQL_VERSION_ID;
}
err:
if (file >= 0)

View File

@ -955,361 +955,56 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
*****************************************************************************/
/**
Lock and wait for the named lock.
Obtain exclusive metadata locks on the list of tables.
@param thd Thread handler
@param table_list Lock first table in this list
@param thd Thread handle
@param table_list List of tables to lock
@note This function assumes that no metadata locks were acquired
before calling it. Also it cannot be called while holding
LOCK_open mutex. Both these invariants are enforced by asserts
in mdl_acquire_exclusive_locks() functions.
@note
Works together with global read lock.
@retval
0 ok
@retval
1 error
*/
int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list)
{
int lock_retcode;
int error= -1;
DBUG_ENTER("lock_and_wait_for_table_name");
if (wait_if_global_read_lock(thd, 0, 1))
DBUG_RETURN(1);
pthread_mutex_lock(&LOCK_open);
if ((lock_retcode = lock_table_name(thd, table_list, TRUE)) < 0)
goto end;
if (lock_retcode && wait_for_locked_table_names(thd, table_list))
{
unlock_table_name(thd, table_list);
goto end;
}
error=0;
end:
pthread_mutex_unlock(&LOCK_open);
start_waiting_global_read_lock(thd);
DBUG_RETURN(error);
}
/**
Put a not open table with an old refresh version in the table cache.
@param thd Thread handler
@param table_list Lock first table in this list
@param check_in_use Do we need to check if table already in use by us
@note
One must have a lock on LOCK_open!
@warning
If you are going to update the table, you should use
lock_and_wait_for_table_name instead of this function as this works
together with 'FLUSH TABLES WITH READ LOCK'
@note
This will force any other threads that uses the table to release it
as soon as possible.
@return
< 0 error
@return
== 0 table locked
@return
> 0 table locked, but someone is using it
*/
int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
{
TABLE *table;
char key[MAX_DBKEY_LENGTH];
char *db= table_list->db;
uint key_length;
bool found_locked_table= FALSE;
HASH_SEARCH_STATE state;
DBUG_ENTER("lock_table_name");
DBUG_PRINT("enter",("db: %s name: %s", db, table_list->table_name));
key_length= create_table_def_key(thd, key, table_list, 0);
if (check_in_use)
{
/* Only insert the table if we haven't insert it already */
for (table=(TABLE*) my_hash_first(&open_cache, (uchar*)key,
key_length, &state);
table ;
table = (TABLE*) my_hash_next(&open_cache,(uchar*) key,
key_length, &state))
{
if (table->reginfo.lock_type < TL_WRITE)
{
if (table->in_use == thd)
found_locked_table= TRUE;
continue;
}
if (table->in_use == thd)
{
DBUG_PRINT("info", ("Table is in use"));
table->s->version= 0; // Ensure no one can use this
table->locked_by_name= 1;
DBUG_RETURN(0);
}
}
}
if (thd->locked_tables && thd->locked_tables->table_count &&
! find_temporary_table(thd, table_list->db, table_list->table_name))
{
if (found_locked_table)
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->alias);
else
my_error(ER_TABLE_NOT_LOCKED, MYF(0), table_list->alias);
DBUG_RETURN(-1);
}
if (!(table= table_cache_insert_placeholder(thd, key, key_length)))
DBUG_RETURN(-1);
table_list->table=table;
/* Return 1 if table is in use */
DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name,
check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG)));
}
void unlock_table_name(THD *thd, TABLE_LIST *table_list)
{
if (table_list->table)
{
my_hash_delete(&open_cache, (uchar*) table_list->table);
broadcast_refresh();
}
}
static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
{
for (; table_list ; table_list=table_list->next_local)
{
TABLE *table= table_list->table;
if (table)
{
TABLE *save_next= table->next;
bool result;
table->next= 0;
result= table_is_used(table_list->table, 0);
table->next= save_next;
if (result)
return 1;
}
}
return 0; // All tables are locked
}
bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list)
{
bool result=0;
DBUG_ENTER("wait_for_locked_table_names");
safe_mutex_assert_owner(&LOCK_open);
while (locked_named_table(thd,table_list))
{
if (thd->killed)
{
result=1;
break;
}
wait_for_condition(thd, &LOCK_open, &COND_refresh);
pthread_mutex_lock(&LOCK_open);
}
DBUG_RETURN(result);
}
/**
Lock all tables in list with a name lock.
REQUIREMENTS
- One must have a lock on LOCK_open when calling this
@param thd Thread handle
@param table_list Names of tables to lock
@note
If you are just locking one table, you should use
lock_and_wait_for_table_name().
@retval
0 ok
@retval
1 Fatal error (end of memory ?)
@retval FALSE Success.
@retval TRUE Failure (OOM or thread was killed).
*/
bool lock_table_names(THD *thd, TABLE_LIST *table_list)
{
bool got_all_locks=1;
TABLE_LIST *lock_table;
MDL_LOCK *mdl_lock;
for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
{
int got_lock;
if ((got_lock=lock_table_name(thd,lock_table, TRUE)) < 0)
goto end; // Fatal error
if (got_lock)
got_all_locks=0; // Someone is using table
if (!(mdl_lock= mdl_alloc_lock(0, lock_table->db, lock_table->table_name,
thd->mem_root)))
goto end;
mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE);
mdl_add_lock(&thd->mdl_context, mdl_lock);
}
/* If some table was in use, wait until we got the lock */
if (!got_all_locks && wait_for_locked_table_names(thd, table_list))
goto end;
if (mdl_acquire_exclusive_locks(&thd->mdl_context))
return 1;
return 0;
end:
unlock_table_names(thd, table_list, lock_table);
mdl_remove_all_locks(&thd->mdl_context);
return 1;
}
/**
Unlock all tables in list with a name lock.
Release all metadata locks previously obtained by lock_table_names().
@param thd Thread handle.
@param table_list Names of tables to lock.
@param thd Thread handle.
@note
This function needs to be protected by LOCK_open. If we're
under LOCK TABLES, this function does not work as advertised. Namely,
it does not exclude other threads from using this table and does not
put an exclusive name lock on this table into the table cache.
@see lock_table_names
@see unlock_table_names
@retval TRUE An error occured.
@retval FALSE Name lock successfully acquired.
@note Cannot be called while holding LOCK_open mutex.
*/
bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list)
{
if (lock_table_names(thd, table_list))
return TRUE;
/*
Upgrade the table name locks from semi-exclusive to exclusive locks.
*/
for (TABLE_LIST *table= table_list; table; table= table->next_global)
{
if (table->table)
table->table->open_placeholder= 1;
}
return FALSE;
}
/**
Test is 'table' is protected by an exclusive name lock.
@param[in] thd The current thread handler
@param[in] table_list Table container containing the single table to be
tested
@note Needs to be protected by LOCK_open mutex.
@return Error status code
@retval TRUE Table is protected
@retval FALSE Table is not protected
*/
bool
is_table_name_exclusively_locked_by_this_thread(THD *thd,
TABLE_LIST *table_list)
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
key_length= create_table_def_key(thd, key, table_list, 0);
return is_table_name_exclusively_locked_by_this_thread(thd, (uchar *)key,
key_length);
}
/**
Test is 'table key' is protected by an exclusive name lock.
@param[in] thd The current thread handler.
@param[in] key
@param[in] key_length
@note Needs to be protected by LOCK_open mutex
@retval TRUE Table is protected
@retval FALSE Table is not protected
*/
bool
is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key,
int key_length)
{
HASH_SEARCH_STATE state;
TABLE *table;
for (table= (TABLE*) my_hash_first(&open_cache, key,
key_length, &state);
table ;
table= (TABLE*) my_hash_next(&open_cache, key,
key_length, &state))
{
if (table->in_use == thd &&
table->open_placeholder == 1 &&
table->s->version == 0)
return TRUE;
}
return FALSE;
}
/**
Unlock all tables in list with a name lock.
@param
thd Thread handle
@param
table_list Names of tables to unlock
@param
last_table Don't unlock any tables after this one.
(default 0, which will unlock all tables)
@note
One must have a lock on LOCK_open when calling this.
@note
This function will broadcast refresh signals to inform other threads
that the name locks are removed.
@retval
0 ok
@retval
1 Fatal error (end of memory ?)
*/
void unlock_table_names(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *last_table)
void unlock_table_names(THD *thd)
{
DBUG_ENTER("unlock_table_names");
for (TABLE_LIST *table= table_list;
table != last_table;
table= table->next_local)
unlock_table_name(thd,table);
broadcast_refresh();
mdl_release_locks(&thd->mdl_context);
mdl_remove_all_locks(&thd->mdl_context);
DBUG_VOID_RETURN;
}
@ -1455,6 +1150,33 @@ bool lock_global_read_lock(THD *thd)
thd->global_read_lock= GOT_GLOBAL_READ_LOCK;
global_read_lock++;
thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
/*
When we perform FLUSH TABLES or ALTER TABLE under LOCK TABLES,
tables being reopened are protected only by meta-data locks at
some point. To avoid sneaking in with our global read lock at
this moment we have to take global shared meta data lock.
TODO: We should change this code to acquire global shared metadata
lock before acquiring global read lock. But in order to do
this we have to get rid of all those places in which
wait_if_global_read_lock() is called before acquiring
metadata locks first. Also long-term we should get rid of
redundancy between metadata locks, global read lock and DDL
blocker (see WL#4399 and WL#4400).
*/
if (mdl_acquire_global_shared_lock(&thd->mdl_context))
{
/* Our thread was killed -- return back to initial state. */
pthread_mutex_lock(&LOCK_global_read_lock);
if (!(--global_read_lock))
{
DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
pthread_cond_broadcast(&COND_global_read_lock);
}
pthread_mutex_unlock(&LOCK_global_read_lock);
thd->global_read_lock= 0;
DBUG_RETURN(1);
}
}
/*
We DON'T set global_read_lock_blocks_commit now, it will be set after
@ -1476,6 +1198,8 @@ void unlock_global_read_lock(THD *thd)
("global_read_lock: %u global_read_lock_blocks_commit: %u",
global_read_lock, global_read_lock_blocks_commit));
mdl_release_global_shared_lock(&thd->mdl_context);
pthread_mutex_lock(&LOCK_global_read_lock);
tmp= --global_read_lock;
if (thd->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT)

View File

@ -3036,7 +3036,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
}
else
{
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
}
/*
@ -7238,8 +7238,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
DBUG_ASSERT(get_flags(STMT_END_F));
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
close_thread_tables(thd);
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
thd->clear_error();
DBUG_RETURN(0);
}
@ -7322,7 +7321,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
"unexpected success or fatal error"));
thd->is_slave_error= 1;
}
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(actual_error);
}
@ -7347,7 +7346,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
mysql_unlock_tables(thd, thd->lock);
thd->lock= 0;
thd->is_slave_error= 1;
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(ERR_BAD_TABLE_DEF);
}
}
@ -7530,12 +7529,6 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
}
} // if (table)
/*
We need to delay this clear until here bacause unpack_current_row() uses
master-side table definitions stored in rli.
*/
if (rli->tables_to_lock && get_flags(STMT_END_F))
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
if (error)
{
@ -8064,7 +8057,8 @@ Table_map_log_event::~Table_map_log_event()
int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
{
RPL_TABLE_LIST *table_list;
char *db_mem, *tname_mem;
char *db_mem, *tname_mem, *mdlkey;
MDL_LOCK *mdl_lock;
size_t dummy_len;
void *memory;
DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)");
@ -8079,6 +8073,8 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
&table_list, (uint) sizeof(RPL_TABLE_LIST),
&db_mem, (uint) NAME_LEN + 1,
&tname_mem, (uint) NAME_LEN + 1,
&mdl_lock, sizeof(MDL_LOCK),
&mdlkey, MAX_DBKEY_LENGTH,
NullS)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
@ -8091,6 +8087,8 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
table_list->updating= 1;
strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
strmov(table_list->table_name, m_tblnam);
mdl_init_lock(mdl_lock, mdlkey, 0, table_list->db, table_list->table_name);
table_list->mdl_lock= mdl_lock;
int error= 0;

View File

@ -32,8 +32,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
*/
DBUG_ASSERT(ev->get_flags(Old_rows_log_event::STMT_END_F));
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
close_thread_tables(thd);
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
thd->clear_error();
DBUG_RETURN(0);
}
@ -91,7 +90,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
"unexpected success or fatal error"));
thd->is_slave_error= 1;
}
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(actual_error);
}
@ -109,10 +108,8 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
{
if (ptr->m_tabledef.compatible_with(rli, ptr->table))
{
mysql_unlock_tables(thd, thd->lock);
thd->lock= 0;
thd->is_slave_error= 1;
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(Old_rows_log_event::ERR_BAD_TABLE_DEF);
}
}
@ -236,13 +233,6 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
}
}
/*
We need to delay this clear until the table def is no longer needed.
The table def is needed in unpack_row().
*/
if (rli->tables_to_lock && ev->get_flags(Old_rows_log_event::STMT_END_F))
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
if (error)
{ /* error has occured during the transaction */
rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(),
@ -1440,8 +1430,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
DBUG_ASSERT(get_flags(STMT_END_F));
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
close_thread_tables(thd);
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
thd->clear_error();
DBUG_RETURN(0);
}
@ -1496,7 +1485,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
"Error in %s event: when locking tables",
get_type_str());
}
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(error);
}
@ -1515,7 +1504,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
thd->binlog_flush_pending_rows_event(false);
TABLE_LIST *tables= rli->tables_to_lock;
close_tables_for_reopen(thd, &tables);
close_tables_for_reopen(thd, &tables, FALSE);
uint tables_count= rli->tables_to_lock_count;
if ((error= open_tables(thd, &tables, &tables_count, 0)))
@ -1533,7 +1522,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
"unexpected success or fatal error"));
thd->is_slave_error= 1;
}
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(error);
}
}
@ -1552,10 +1541,8 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
{
if (ptr->m_tabledef.compatible_with(rli, ptr->table))
{
mysql_unlock_tables(thd, thd->lock);
thd->lock= 0;
thd->is_slave_error= 1;
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(ERR_BAD_TABLE_DEF);
}
}
@ -1726,13 +1713,6 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
}
} // if (table)
/*
We need to delay this clear until here bacause unpack_current_row() uses
master-side table definitions stored in rli.
*/
if (rli->tables_to_lock && get_flags(STMT_END_F))
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
if (error)
{ /* error has occured during the transaction */
rli->report(ERROR_LEVEL, thd->net.last_errno,

1342
sql/mdl.cc Normal file

File diff suppressed because it is too large Load Diff

260
sql/mdl.h Normal file
View File

@ -0,0 +1,260 @@
#ifndef MDL_H
#define MDL_H
/* Copyright (C) 2007-2008 MySQL AB
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "sql_plist.h"
#include <my_sys.h>
#include <m_string.h>
class THD;
struct MDL_LOCK;
struct MDL_LOCK_DATA;
struct MDL_CONTEXT;
/** Type of metadata lock request. */
enum enum_mdl_type {MDL_SHARED=0, MDL_EXCLUSIVE};
/** States which metadata lock request can have. */
enum enum_mdl_state {MDL_PENDING=0, MDL_ACQUIRED, MDL_PENDING_UPGRADE};
/**
Priority of metadata lock requests. High priority attribute is
applicable only to requests for shared locks and indicates that
such request should ignore pending requests for exclusive locks
and for upgrading of shared locks to exclusive.
*/
enum enum_mdl_prio {MDL_NORMAL_PRIO=0, MDL_HIGH_PRIO};
/**
A pending lock request or a granted metadata lock. A lock is requested
or granted based on a fully qualified name and type. E.g. for a table
the key consists of <0 (=table)>+<database name>+<table name>.
Later in this document this triple will be referred to simply as
"key" or "name".
*/
struct MDL_LOCK
{
char *key;
uint key_length;
enum enum_mdl_type type;
enum enum_mdl_state state;
enum enum_mdl_prio prio;
/**
TRUE -- if shared lock corresponding to this lock request at some
point might be upgraded to an exclusive lock and therefore conflicts
with global shared lock, FALSE -- otherwise.
*/
bool upgradable;
private:
/**
Pointers for participating in the list of lock requests for this context.
*/
MDL_LOCK *next_context;
MDL_LOCK **prev_context;
/**
Pointers for participating in the list of satisfied/pending requests
for the lock.
*/
MDL_LOCK *next_lock;
MDL_LOCK **prev_lock;
friend struct MDL_LOCK_context;
friend struct MDL_LOCK_lock;
public:
/*
Pointer to the lock object for this lock request. Valid only if this lock
request is satisified or is present in the list of pending lock requests
for particular lock.
*/
MDL_LOCK_DATA *lock_data;
MDL_CONTEXT *ctx;
};
/**
Helper class which specifies which members of MDL_LOCK are used for
participation in the list lock requests belonging to one context.
*/
struct MDL_LOCK_context
{
static inline MDL_LOCK **next_ptr(MDL_LOCK *l)
{
return &l->next_context;
}
static inline MDL_LOCK ***prev_ptr(MDL_LOCK *l)
{
return &l->prev_context;
}
};
/**
Helper class which specifies which members of MDL_LOCK are used for
participation in the list of satisfied/pending requests for the lock.
*/
struct MDL_LOCK_lock
{
static inline MDL_LOCK **next_ptr(MDL_LOCK *l)
{
return &l->next_lock;
}
static inline MDL_LOCK ***prev_ptr(MDL_LOCK *l)
{
return &l->prev_lock;
}
};
/**
Context of the owner of metadata locks. I.e. each server
connection has such a context.
*/
struct MDL_CONTEXT
{
I_P_List <MDL_LOCK, MDL_LOCK_context> locks;
bool has_global_shared_lock;
THD *thd;
};
void mdl_init();
void mdl_destroy();
void mdl_context_init(MDL_CONTEXT *context, THD *thd);
void mdl_context_destroy(MDL_CONTEXT *context);
void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup);
void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup);
void mdl_context_merge(MDL_CONTEXT *target, MDL_CONTEXT *source);
void mdl_init_lock(MDL_LOCK *mdl, char *key, int type, const char *db,
const char *name);
MDL_LOCK *mdl_alloc_lock(int type, const char *db, const char *name,
MEM_ROOT *root);
void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK *lock);
void mdl_remove_all_locks(MDL_CONTEXT *context);
/**
Set type of lock request. Can be only applied to pending locks.
*/
inline void mdl_set_lock_type(MDL_LOCK *lock, enum_mdl_type lock_type)
{
DBUG_ASSERT(lock->state == MDL_PENDING);
lock->type= lock_type;
}
/**
Set priority for lock request. High priority can be only set
for shared locks.
*/
inline void mdl_set_lock_priority(MDL_LOCK *lock, enum_mdl_prio prio)
{
DBUG_ASSERT(lock->type == MDL_SHARED && lock->state == MDL_PENDING);
lock->prio= prio;
}
/**
Mark request for shared lock as upgradable. Can be only applied
to pending locks.
*/
inline void mdl_set_upgradable(MDL_LOCK *lock)
{
DBUG_ASSERT(lock->type == MDL_SHARED && lock->state == MDL_PENDING);
lock->upgradable= TRUE;
}
bool mdl_acquire_shared_lock(MDL_LOCK *l, bool *retry);
bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context);
bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type,
const char *db, const char *name);
bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_LOCK *lock);
bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context);
bool mdl_wait_for_locks(MDL_CONTEXT *context);
void mdl_release_locks(MDL_CONTEXT *context);
void mdl_release_exclusive_locks(MDL_CONTEXT *context);
void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK *lock);
void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context);
void mdl_release_global_shared_lock(MDL_CONTEXT *context);
bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db,
const char *name);
bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db,
const char *name);
bool mdl_has_pending_conflicting_lock(MDL_LOCK *l);
inline bool mdl_has_locks(MDL_CONTEXT *context)
{
return !context->locks.is_empty();
}
/**
Get iterator for walking through all lock requests in the context.
*/
inline I_P_List_iterator<MDL_LOCK, MDL_LOCK_context> mdl_get_locks(MDL_CONTEXT *ctx)
{
I_P_List_iterator<MDL_LOCK, MDL_LOCK_context> result(ctx->locks);
return result;
}
/**
Give metadata lock request object for the table get table definition
cache key corresponding to it.
@param l [in] Lock request object for the table.
@param key [out] LEX_STRING object where table definition cache key
should be put.
@note This key will have the same life-time as this lock request object.
@note This is yet another place where border between MDL subsystem and the
rest of the server is broken. OTOH it allows to save some CPU cycles
and memory by avoiding generating these TDC keys from table list.
*/
inline void mdl_get_tdc_key(MDL_LOCK *l, LEX_STRING *key)
{
key->str= l->key + 4;
key->length= l->key_length - 4;
}
typedef void (* mdl_cached_object_release_hook)(void *);
void* mdl_get_cached_object(MDL_LOCK *l);
void mdl_set_cached_object(MDL_LOCK *l, void *cached_object,
mdl_cached_object_release_hook release_hook);
#endif

View File

@ -805,7 +805,7 @@ extern my_decimal decimal_zero;
void free_items(Item *item);
void cleanup_items(Item *item);
class THD;
void close_thread_tables(THD *thd);
void close_thread_tables(THD *thd, bool skip_mdl= 0);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables);
@ -993,7 +993,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
bool drop_temporary, bool drop_view, bool log_query);
bool quick_rm_table(handlerton *base,const char *db,
const char *table_name, uint flags);
void close_cached_table(THD *thd, TABLE *table);
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent);
bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
char *new_table_name, char *new_table_alias,
@ -1034,10 +1033,8 @@ bool check_dup(const char *db, const char *name, TABLE_LIST *tables);
bool compare_record(TABLE *table);
bool append_file_to_dir(THD *thd, const char **filename_ptr,
const char *table_name);
void wait_while_table_is_used(THD *thd, TABLE *table,
bool wait_while_table_is_used(THD *thd, TABLE *table,
enum ha_extra_function function);
bool table_cache_init(void);
void table_cache_free(void);
bool table_def_init(void);
void table_def_free(void);
void assign_new_table_id(TABLE_SHARE *share);
@ -1223,28 +1220,30 @@ void release_table_share(TABLE_SHARE *share, enum release_type type);
TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
uint lock_flags);
enum enum_open_table_action {OT_NO_ACTION= 0, OT_BACK_OFF_AND_RETRY,
OT_DISCOVER, OT_REPAIR};
TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
bool *refresh, uint flags);
enum_open_table_action *action, uint flags);
bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
char *cache_key, uint cache_key_length,
MEM_ROOT *mem_root, uint flags);
bool name_lock_locked_table(THD *thd, TABLE_LIST *tables);
bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in);
TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
uint key_length);
bool lock_table_name_if_not_cached(THD *thd, const char *db,
const char *table_name, TABLE **table);
TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list);
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);
void detach_merge_children(TABLE *table, bool clear_refs);
bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
TABLE_LIST *new_child_list, TABLE_LIST **new_last);
bool reopen_table(TABLE *table);
bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
bool reopen_tables(THD *thd, bool get_locks);
thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table);
void close_data_files_and_morph_locks(THD *thd, const char *db,
const char *table_name);
void close_handle_and_leave_table_as_lock(TABLE *table);
bool wait_for_tables(THD *thd);
bool table_is_used(TABLE *table, bool wait_for_name_lock);
TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name);
void abort_locked_tables(THD *thd,const char *db, const char *table_name);
void unlock_locked_tables(THD *thd);
void execute_init_command(THD *thd, sys_var_str *init_command_var,
rw_lock_t *var_mutex);
extern Field *not_found_field;
@ -1364,7 +1363,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
void mysql_ha_flush(THD *thd);
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked);
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables);
void mysql_ha_cleanup(THD *thd);
/* sql_base.cc */
@ -1501,7 +1500,7 @@ void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
bool close_thread_table(THD *thd, TABLE **table_ptr);
void close_temporary_tables(THD *thd);
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables);
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool skip_mdl);
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
TABLE_LIST *TABLE_LIST::*link,
const char *db_name,
@ -1550,6 +1549,9 @@ char *generate_partition_syntax(partition_info *part_info,
#define RTFC_CHECK_KILLED_FLAG 0x0004
bool remove_table_from_cache(THD *thd, const char *db, const char *table,
uint flags);
bool notify_thread_having_shared_lock(THD *thd, THD *in_use);
void expel_table_from_cache(THD *leave_thd, const char *db,
const char *table_name);
#define NORMAL_PART_NAME 0
#define TEMP_PART_NAME 1
@ -1681,7 +1683,7 @@ TABLE *open_performance_schema_table(THD *thd, TABLE_LIST *one_table,
void close_performance_schema_table(THD *thd, Open_tables_state *backup);
bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
bool wait_for_refresh, bool wait_for_placeholders);
bool wait_for_refresh);
bool close_cached_connection_tables(THD *thd, bool wait_for_refresh,
LEX_STRING *connect_string,
bool have_lock = FALSE);
@ -2001,8 +2003,9 @@ extern const char *opt_date_time_formats[];
extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
extern String null_string;
extern HASH open_cache, lock_db_cache;
extern HASH table_def_cache, lock_db_cache;
extern TABLE *unused_tables;
extern uint table_cache_count;
extern const char* any_db;
extern struct my_option my_long_options[];
extern const LEX_STRING view_type;
@ -2070,18 +2073,8 @@ void unset_protect_against_global_read_lock(void);
void broadcast_refresh(void);
/* Lock based on name */
int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list);
int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use);
void unlock_table_name(THD *thd, TABLE_LIST *table_list);
bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list);
bool lock_table_names(THD *thd, TABLE_LIST *table_list);
void unlock_table_names(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *last_table);
bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list);
bool is_table_name_exclusively_locked_by_this_thread(THD *thd,
TABLE_LIST *table_list);
bool is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key,
int key_length);
void unlock_table_names(THD *thd);
/* old unireg functions */

View File

@ -1301,9 +1301,9 @@ void clean_up(bool print_message)
grant_free();
#endif
query_cache_destroy();
table_cache_free();
table_def_free();
hostname_cache_free();
mdl_destroy();
item_user_lock_free();
lex_free(); /* Free some memory */
item_create_cleanup();
@ -3755,7 +3755,8 @@ static int init_server_components()
We need to call each of these following functions to ensure that
all things are initialized so that unireg_abort() doesn't fail
*/
if (table_cache_init() | table_def_init() | hostname_cache_init())
mdl_init();
if (table_def_init() | hostname_cache_init())
unireg_abort(1);
query_cache_result_size_limit(query_cache_limit);

View File

@ -1187,8 +1187,7 @@ void Relay_log_info::cleanup_context(THD *thd, bool error)
end_trans(thd, ROLLBACK); // if a "real transaction"
}
m_table_map.clear_tables();
close_thread_tables(thd);
clear_tables_to_lock();
slave_close_thread_tables(thd);
clear_flag(IN_STMT);
/*
Cleanup for the flags that have been set at do_apply_event.
@ -1200,6 +1199,13 @@ void Relay_log_info::cleanup_context(THD *thd, bool error)
void Relay_log_info::clear_tables_to_lock()
{
/*
Deallocating elements of table list below will also free memory where
meta-data locks are stored. So we want to be sure that we don't have
any references to this memory left.
*/
DBUG_ASSERT(!mdl_has_locks(&(current_thd->mdl_context)));
while (tables_to_lock)
{
uchar* to_free= reinterpret_cast<uchar*>(tables_to_lock);
@ -1216,4 +1222,15 @@ void Relay_log_info::clear_tables_to_lock()
DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0);
}
void Relay_log_info::slave_close_thread_tables(THD *thd)
{
/*
Since we use same memory chunks for allocation of metadata lock
objects for tables as we use for allocating corresponding elements
of 'tables_to_lock' list, we have to release metadata locks by
closing tables before calling clear_tables_to_lock().
*/
close_thread_tables(thd);
clear_tables_to_lock();
}
#endif

View File

@ -346,6 +346,7 @@ public:
bool cached_charset_compare(char *charset) const;
void cleanup_context(THD *, bool);
void slave_close_thread_tables(THD *);
void clear_tables_to_lock();
/*

View File

@ -4333,7 +4333,7 @@ bool sys_var_opt_readonly::update(THD *thd, set_var *var)
can cause to wait on a read lock, it's required for the client application
to unlock everything, and acceptable for the server to wait on all locks.
*/
if ((result= close_cached_tables(thd, NULL, FALSE, TRUE, TRUE)))
if ((result= close_cached_tables(thd, NULL, FALSE, TRUE)))
goto end_with_read_lock;
if ((result= make_global_read_lock_block_commit(thd)))

View File

@ -3984,6 +3984,9 @@ sp_head::add_used_tables_to_table_list(THD *thd,
table->prelocking_placeholder= 1;
table->belong_to_view= belong_to_view;
table->trg_event_map= stab->trg_event_map;
table->mdl_lock= mdl_alloc_lock(0, table->db, table->table_name,
thd->mdl_el_root ? thd->mdl_el_root :
thd->mem_root);
/* Everyting else should be zeroed */
@ -4025,7 +4028,9 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
table->lock_type= locktype;
table->select_lex= lex->current_select;
table->cacheable_table= 1;
table->mdl_lock= mdl_alloc_lock(0, table->db, table->table_name,
thd->mdl_el_root ? thd->mdl_el_root :
thd->mem_root);
lex->add_to_query_tables(table);
return table;
}

View File

@ -676,12 +676,7 @@ my_bool acl_reload(THD *thd)
my_bool return_val= 1;
DBUG_ENTER("acl_reload");
if (thd->locked_tables)
{ // Can't have locked tables here
thd->lock=thd->locked_tables;
thd->locked_tables=0;
close_thread_tables(thd);
}
unlock_locked_tables(thd); // Can't have locked tables here
/*
To avoid deadlocks we should obtain table locks before
@ -697,6 +692,7 @@ my_bool acl_reload(THD *thd)
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
tables[0].skip_temporary= tables[1].skip_temporary=
tables[2].skip_temporary= TRUE;
alloc_mdl_locks(tables, thd->mem_root);
if (simple_open_n_lock_tables(thd, tables))
{
@ -1601,6 +1597,7 @@ bool change_password(THD *thd, const char *host, const char *user,
bzero((char*) &tables, sizeof(tables));
tables.alias= tables.table_name= (char*) "user";
tables.db= (char*) "mysql";
alloc_mdl_locks(&tables, thd->mem_root);
#ifdef HAVE_REPLICATION
/*
@ -3053,6 +3050,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
? tables+2 : 0);
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE;
tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
alloc_mdl_locks(tables, thd->mem_root);
/*
This statement will be replicated as a statement, even when using
@ -3270,6 +3268,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
tables[0].next_local= tables[0].next_global= tables+1;
tables[0].lock_type=tables[1].lock_type=TL_WRITE;
tables[0].db=tables[1].db=(char*) "mysql";
alloc_mdl_locks(tables, thd->mem_root);
/*
This statement will be replicated as a statement, even when using
@ -3408,6 +3407,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
tables[0].next_local= tables[0].next_global= tables+1;
tables[0].lock_type=tables[1].lock_type=TL_WRITE;
tables[0].db=tables[1].db=(char*) "mysql";
alloc_mdl_locks(tables, thd->mem_root);
/*
This statement will be replicated as a statement, even when using
@ -3741,6 +3741,7 @@ static my_bool grant_reload_procs_priv(THD *thd)
table.db= (char *) "mysql";
table.lock_type= TL_READ;
table.skip_temporary= 1;
alloc_mdl_locks(&table, thd->mem_root);
if (simple_open_n_lock_tables(thd, &table))
{
@ -3807,6 +3808,8 @@ my_bool grant_reload(THD *thd)
tables[0].next_local= tables[0].next_global= tables+1;
tables[0].lock_type= tables[1].lock_type= TL_READ;
tables[0].skip_temporary= tables[1].skip_temporary= TRUE;
alloc_mdl_locks(tables, thd->mem_root);
/*
To avoid deadlocks we should obtain table locks before
obtaining LOCK_grant rwlock.
@ -5152,6 +5155,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
(tables+4)->lock_type= TL_WRITE;
tables->db= (tables+1)->db= (tables+2)->db=
(tables+3)->db= (tables+4)->db= (char*) "mysql";
alloc_mdl_locks(tables, thd->mem_root);
#ifdef HAVE_REPLICATION
/*

File diff suppressed because it is too large Load Diff

View File

@ -241,7 +241,7 @@ void mysql_client_binlog_statement(THD* thd)
my_ok(thd);
end:
rli->clear_tables_to_lock();
rli->slave_close_thread_tables(thd);
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
DBUG_VOID_RETURN;
}

View File

@ -202,10 +202,10 @@ bool foreign_key_prefix(Key *a, Key *b)
** Thread specific functions
****************************************************************************/
Open_tables_state::Open_tables_state(ulong version_arg)
Open_tables_state::Open_tables_state(THD *thd, ulong version_arg)
:version(version_arg), state_flags(0U)
{
reset_open_tables_state();
reset_open_tables_state(thd);
}
/*
@ -440,7 +440,7 @@ bool Drop_table_error_handler::handle_condition(THD *thd,
THD::THD()
:Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION,
/* statement id */ 0),
Open_tables_state(refresh_version), rli_fake(0),
Open_tables_state(this, refresh_version), rli_fake(0),
lock_id(&main_lock_id),
user_time(0), in_sub_stmt(0),
sql_log_bin_toplevel(false),
@ -468,7 +468,8 @@ THD::THD()
#if defined(ENABLED_DEBUG_SYNC)
debug_sync_control(0),
#endif /* defined(ENABLED_DEBUG_SYNC) */
main_warning_info(0)
main_warning_info(0),
mdl_el_root(NULL)
{
ulong tmp;
@ -573,6 +574,8 @@ THD::THD()
thr_lock_owner_init(&main_lock_id, &lock_info);
m_internal_handler= NULL;
init_sql_alloc(&locked_tables_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
}
@ -1051,6 +1054,9 @@ THD::~THD()
if (!cleanup_done)
cleanup();
mdl_context_destroy(&mdl_context);
mdl_context_destroy(&handler_mdl_context);
ha_close_connection(this);
plugin_thdvar_cleanup(this);
@ -1072,6 +1078,7 @@ THD::~THD()
#endif
free_root(&main_mem_root, MYF(0));
free_root(&locked_tables_root, MYF(0));
DBUG_VOID_RETURN;
}
@ -3014,7 +3021,7 @@ void THD::reset_n_backup_open_tables_state(Open_tables_state *backup)
{
DBUG_ENTER("reset_n_backup_open_tables_state");
backup->set_open_tables_state(this);
reset_open_tables_state();
reset_open_tables_state(this);
state_flags|= Open_tables_state::BACKUPS_AVAIL;
DBUG_VOID_RETURN;
}
@ -3032,6 +3039,9 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup)
lock == 0 && locked_tables == 0 &&
prelocked_mode == NON_PRELOCKED &&
m_reprepare_observer == NULL);
mdl_context_destroy(&mdl_context);
mdl_context_destroy(&handler_mdl_context);
set_open_tables_state(backup);
DBUG_VOID_RETURN;
}

View File

@ -25,6 +25,7 @@
#include "log.h"
#include "rpl_tblmap.h"
#include "mdl.h"
class Reprepare_observer;
@ -976,26 +977,31 @@ public:
*/
uint state_flags;
MDL_CONTEXT mdl_context;
MDL_CONTEXT handler_mdl_context;
/*
This constructor serves for creation of Open_tables_state instances
which are used as backup storage.
*/
Open_tables_state() : state_flags(0U) { }
Open_tables_state(ulong version_arg);
Open_tables_state(THD *thd, ulong version_arg);
void set_open_tables_state(Open_tables_state *state)
{
*this= *state;
}
void reset_open_tables_state()
void reset_open_tables_state(THD *thd)
{
open_tables= temporary_tables= handler_tables= derived_tables= 0;
extra_lock= lock= locked_tables= 0;
prelocked_mode= NON_PRELOCKED;
state_flags= 0U;
m_reprepare_observer= NULL;
mdl_context_init(&mdl_context, thd);
mdl_context_init(&handler_mdl_context, thd);
}
};
@ -1809,6 +1815,9 @@ public:
struct st_debug_sync_control *debug_sync_control;
#endif /* defined(ENABLED_DEBUG_SYNC) */
MEM_ROOT *mdl_el_root;
MEM_ROOT locked_tables_root;
THD();
~THD();

View File

@ -904,10 +904,6 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
}
else
{
pthread_mutex_lock(&LOCK_open);
remove_db_from_cache(db);
pthread_mutex_unlock(&LOCK_open);
Drop_table_error_handler err_handler(thd->get_internal_handler());
thd->push_internal_handler(&err_handler);

View File

@ -1089,12 +1089,13 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
TABLE *table;
bool error;
uint path_length;
MDL_LOCK *mdl_lock= 0;
DBUG_ENTER("mysql_truncate");
bzero((char*) &create_info,sizeof(create_info));
/* Remove tables from the HANDLER's hash. */
mysql_ha_rm_tables(thd, table_list, FALSE);
mysql_ha_rm_tables(thd, table_list);
/* If it is a temporary table, close and regenerate it */
if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
@ -1158,8 +1159,20 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
goto trunc_by_del;
if (lock_and_wait_for_table_name(thd, table_list))
/*
FIXME: Actually code of TRUNCATE breaks meta-data locking protocol since
tries to get table enging and therefore accesses table in some way
without holding any kind of meta-data lock.
*/
mdl_lock= mdl_alloc_lock(0, table_list->db, table_list->table_name,
thd->mem_root);
mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE);
mdl_add_lock(&thd->mdl_context, mdl_lock);
if (mdl_acquire_exclusive_locks(&thd->mdl_context))
DBUG_RETURN(TRUE);
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(0, table_list->db, table_list->table_name);
pthread_mutex_unlock(&LOCK_open);
}
// Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
@ -1184,15 +1197,13 @@ end:
write_bin_log(thd, TRUE, thd->query(), thd->query_length());
my_ok(thd); // This should return record count
}
pthread_mutex_lock(&LOCK_open);
unlock_table_name(thd, table_list);
pthread_mutex_unlock(&LOCK_open);
if (mdl_lock)
mdl_release_lock(&thd->mdl_context, mdl_lock);
}
else if (error)
{
pthread_mutex_lock(&LOCK_open);
unlock_table_name(thd, table_list);
pthread_mutex_unlock(&LOCK_open);
if (mdl_lock)
mdl_release_lock(&thd->mdl_context, mdl_lock);
}
DBUG_RETURN(error);

View File

@ -116,17 +116,16 @@ static void mysql_ha_hash_free(TABLE_LIST *tables)
@param thd Thread identifier.
@param tables A list of tables with the first entry to close.
@param is_locked If LOCK_open is locked.
@note Though this function takes a list of tables, only the first list entry
will be closed.
@note Broadcasts refresh if it closed a table with old version.
*/
static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
bool is_locked)
static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
{
TABLE **table_ptr;
MDL_LOCK *mdl_lock;
/*
Though we could take the table pointer from hash_tables->table,
@ -142,15 +141,15 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
if (*table_ptr)
{
(*table_ptr)->file->ha_index_or_rnd_end();
if (! is_locked)
pthread_mutex_lock(&LOCK_open);
mdl_lock= (*table_ptr)->mdl_lock;
pthread_mutex_lock(&LOCK_open);
if (close_thread_table(thd, table_ptr))
{
/* Tell threads waiting for refresh that something has happened */
broadcast_refresh();
}
if (! is_locked)
pthread_mutex_unlock(&LOCK_open);
pthread_mutex_unlock(&LOCK_open);
mdl_release_lock(&thd->handler_mdl_context, mdl_lock);
}
else if (tables->table)
{
@ -190,10 +189,12 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
{
TABLE_LIST *hash_tables = NULL;
char *db, *name, *alias;
MDL_LOCK *mdl_lock;
char *db, *name, *alias, *mdlkey;
uint dblen, namelen, aliaslen, counter;
int error;
TABLE *backup_open_tables;
MDL_CONTEXT backup_mdl_context;
DBUG_ENTER("mysql_ha_open");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
tables->db, tables->table_name, tables->alias,
@ -216,7 +217,10 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
HANDLER_TABLES_HASH_SIZE, 0, 0,
(my_hash_get_key) mysql_ha_hash_get_key,
(my_hash_free_key) mysql_ha_hash_free, 0))
goto err;
{
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
}
else if (! reopen) /* Otherwise we have 'tables' already. */
{
@ -224,11 +228,52 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
strlen(tables->alias) + 1))
{
DBUG_PRINT("info",("duplicate '%s'", tables->alias));
DBUG_PRINT("exit",("ERROR"));
my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias);
goto err;
DBUG_RETURN(TRUE);
}
}
if (! reopen)
{
/* copy the TABLE_LIST struct */
dblen= strlen(tables->db) + 1;
namelen= strlen(tables->table_name) + 1;
aliaslen= strlen(tables->alias) + 1;
if (!(my_multi_malloc(MYF(MY_WME),
&hash_tables, (uint) sizeof(*hash_tables),
&db, (uint) dblen,
&name, (uint) namelen,
&alias, (uint) aliaslen,
&mdl_lock, sizeof(MDL_LOCK),
&mdlkey, MAX_DBKEY_LENGTH,
NullS)))
{
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
/* structure copy */
*hash_tables= *tables;
hash_tables->db= db;
hash_tables->table_name= name;
hash_tables->alias= alias;
memcpy(hash_tables->db, tables->db, dblen);
memcpy(hash_tables->table_name, tables->table_name, namelen);
memcpy(hash_tables->alias, tables->alias, aliaslen);
mdl_init_lock(mdl_lock, mdlkey, 0, db, name);
hash_tables->mdl_lock= mdl_lock;
/* add to hash */
if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
{
my_free((char*) hash_tables, MYF(0));
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
}
else
hash_tables= tables;
/*
Save and reset the open_tables list so that open_tables() won't
be able to access (or know about) the previous list. And on return
@ -243,21 +288,22 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
*/
backup_open_tables= thd->open_tables;
thd->open_tables= NULL;
mdl_context_backup_and_reset(&thd->mdl_context, &backup_mdl_context);
/*
open_tables() will set 'tables->table' if successful.
open_tables() will set 'hash_tables->table' if successful.
It must be NULL for a real open when calling open_tables().
*/
DBUG_ASSERT(! tables->table);
DBUG_ASSERT(! hash_tables->table);
/* for now HANDLER can be used only for real TABLES */
tables->required_type= FRMTYPE_TABLE;
hash_tables->required_type= FRMTYPE_TABLE;
/*
We use open_tables() here, rather than, say,
open_ltable() or open_table() because we would like to be able
to open a temporary table.
*/
error= open_tables(thd, &tables, &counter, 0);
error= open_tables(thd, &hash_tables, &counter, 0);
if (thd->open_tables)
{
if (thd->open_tables->next)
@ -281,52 +327,26 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
thd->handler_tables= thd->open_tables;
}
}
mdl_context_merge(&thd->handler_mdl_context, &thd->mdl_context);
/* Restore the state. */
thd->open_tables= backup_open_tables;
mdl_context_restore(&thd->mdl_context, &backup_mdl_context);
if (error)
goto err;
/* There can be only one table in '*tables'. */
if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
if (! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
goto err;
}
if (! reopen)
{
/* copy the TABLE_LIST struct */
dblen= strlen(tables->db) + 1;
namelen= strlen(tables->table_name) + 1;
aliaslen= strlen(tables->alias) + 1;
if (!(my_multi_malloc(MYF(MY_WME),
&hash_tables, (uint) sizeof(*hash_tables),
&db, (uint) dblen,
&name, (uint) namelen,
&alias, (uint) aliaslen,
NullS)))
goto err;
/* structure copy */
*hash_tables= *tables;
hash_tables->db= db;
hash_tables->table_name= name;
hash_tables->alias= alias;
memcpy(hash_tables->db, tables->db, dblen);
memcpy(hash_tables->table_name, tables->table_name, namelen);
memcpy(hash_tables->alias, tables->alias, aliaslen);
/* add to hash */
if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
goto err;
}
/*
If it's a temp table, don't reset table->query_id as the table is
being used by this handler. Otherwise, no meaning at all.
*/
tables->table->open_by_handler= 1;
hash_tables->table->open_by_handler= 1;
if (! reopen)
my_ok(thd);
@ -334,10 +354,10 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
DBUG_RETURN(FALSE);
err:
if (hash_tables)
my_free((char*) hash_tables, MYF(0));
if (tables->table)
mysql_ha_close_table(thd, tables, FALSE);
if (hash_tables->table)
mysql_ha_close_table(thd, hash_tables);
if (!reopen)
my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
@ -371,7 +391,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
(uchar*) tables->alias,
strlen(tables->alias) + 1)))
{
mysql_ha_close_table(thd, hash_tables, FALSE);
mysql_ha_close_table(thd, hash_tables);
my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
}
else
@ -507,7 +527,7 @@ retry:
if (need_reopen)
{
mysql_ha_close_table(thd, hash_tables, FALSE);
mysql_ha_close_table(thd, hash_tables);
/*
The lock might have been aborted, we need to manually reset
thd->some_tables_deleted because handler's tables are closed
@ -734,12 +754,11 @@ static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
@param thd Thread identifier.
@param tables The list of tables to remove.
@param is_locked If LOCK_open is locked.
@note Broadcasts refresh if it closed a table with old version.
*/
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables)
{
TABLE_LIST *hash_tables, *next;
DBUG_ENTER("mysql_ha_rm_tables");
@ -752,7 +771,7 @@ void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
{
next= hash_tables->next_local;
if (hash_tables->table)
mysql_ha_close_table(thd, hash_tables, is_locked);
mysql_ha_close_table(thd, hash_tables);
my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
hash_tables= next;
}
@ -775,13 +794,16 @@ void mysql_ha_flush(THD *thd)
TABLE_LIST *hash_tables;
DBUG_ENTER("mysql_ha_flush");
safe_mutex_assert_owner(&LOCK_open);
safe_mutex_assert_not_owner(&LOCK_open);
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
mysql_ha_close_table(thd, hash_tables, TRUE);
if (hash_tables->table &&
(hash_tables->table->mdl_lock &&
mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock) ||
hash_tables->table->needs_reopen_or_name_lock()))
mysql_ha_close_table(thd, hash_tables);
}
DBUG_VOID_RETURN;
@ -805,7 +827,7 @@ void mysql_ha_cleanup(THD *thd)
{
hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table)
mysql_ha_close_table(thd, hash_tables, FALSE);
mysql_ha_close_table(thd, hash_tables);
}
my_hash_free(&thd->handler_tables_hash);

View File

@ -612,7 +612,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
upgrade the lock here instead?
*/
if (table_list->lock_type == TL_WRITE_DELAYED && thd->locked_tables &&
find_locked_table(thd, table_list->db, table_list->table_name))
find_locked_table(thd->open_tables, table_list->db,
table_list->table_name))
{
my_error(ER_DELAYED_INSERT_TABLE_LOCKED, MYF(0),
table_list->table_name);
@ -2351,6 +2352,8 @@ pthread_handler_t handle_delayed_insert(void *arg)
thd->lex->set_stmt_unsafe();
thd->set_current_stmt_binlog_row_based_if_mixed();
alloc_mdl_locks(&di->table_list, thd->mem_root);
/* Open table */
if (!(di->table= open_n_lock_single_table(thd, &di->table_list,
TL_WRITE_DELAYED)))
@ -3495,7 +3498,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
pthread_mutex_lock(&LOCK_open);
if (reopen_name_locked_table(thd, create_table, FALSE))
if (reopen_name_locked_table(thd, create_table))
{
quick_rm_table(create_info->db_type, create_table->db,
table_case_name(create_info, create_table->table_name),
@ -3507,7 +3510,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
}
else
{
if (!(table= open_table(thd, create_table, thd->mem_root, (bool*) 0,
if (!(table= open_table(thd, create_table, thd->mem_root,
(enum_open_table_action*) 0,
MYSQL_OPEN_TEMPORARY_ONLY)) &&
!create_info->table_existed)
{
@ -3621,8 +3625,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000););
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
(create_table->table && create_table->table->db_stat))
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) && create_table->table)
{
/* Table already exists and was open at open_and_lock_tables() stage. */
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)

View File

@ -47,6 +47,7 @@
"FUNCTION" : "PROCEDURE")
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
static void adjust_mdl_locks_upgradability(TABLE_LIST *tables);
const char *any_db="*any*"; // Special symbol for check_access
@ -143,17 +144,6 @@ static bool xa_trans_rollback(THD *thd)
return status;
}
static void unlock_locked_tables(THD *thd)
{
if (thd->locked_tables)
{
thd->lock=thd->locked_tables;
thd->locked_tables=0; // Will be automatically closed
close_thread_tables(thd); // Free tables
}
}
bool end_active_trans(THD *thd)
{
int error=0;
@ -194,12 +184,9 @@ bool begin_trans(THD *thd)
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
return 1;
}
if (thd->locked_tables)
{
thd->lock=thd->locked_tables;
thd->locked_tables=0; // Will be automatically closed
close_thread_tables(thd); // Free tables
}
unlock_locked_tables(thd);
if (end_active_trans(thd))
error= -1;
else
@ -1342,6 +1329,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
select_lex.table_list.link_in_list((uchar*) &table_list,
(uchar**) &table_list.next_local);
thd->lex->add_to_query_tables(&table_list);
alloc_mdl_locks(&table_list, thd->mem_root);
/* switch on VIEW optimisation: do not fill temporary tables */
thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
@ -2643,7 +2631,7 @@ case SQLCOM_PREPARE:
if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
{
lex->link_first_table_back(create_table, link_to_local);
create_table->create= TRUE;
create_table->open_table_type= TABLE_LIST::OPEN_OR_CREATE;
}
if (!(res= open_and_lock_tables(thd, lex->query_tables)))
@ -3618,6 +3606,9 @@ end_with_restore_list:
goto error;
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
alloc_mdl_locks(all_tables, &thd->locked_tables_root);
thd->mdl_el_root= &thd->locked_tables_root;
adjust_mdl_locks_upgradability(all_tables);
if (!(res= simple_open_n_lock_tables(thd, all_tables)))
{
@ -3641,6 +3632,7 @@ end_with_restore_list:
thd->options&= ~(OPTION_TABLE_LOCK);
}
thd->in_lock_tables=0;
thd->mdl_el_root= 0;
break;
case SQLCOM_CREATE_DB:
{
@ -6542,6 +6534,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->next_name_resolution_table= NULL;
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
ptr->mdl_lock= mdl_alloc_lock(0 , ptr->db, ptr->table_name,
thd->mdl_el_root ? thd->mdl_el_root :
thd->mem_root);
DBUG_RETURN(ptr);
}
@ -7079,23 +7074,15 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
if ((options & REFRESH_READ_LOCK) && thd)
{
/*
We must not try to aspire a global read lock if we have a write
locked table. This would lead to a deadlock when trying to
reopen (and re-lock) the table after the flush.
On the first hand we need write lock on the tables to be flushed,
on the other hand we must not try to aspire a global read lock
if we have a write locked table as this would lead to a deadlock
when trying to reopen (and re-lock) the table after the flush.
*/
if (thd->locked_tables)
{
THR_LOCK_DATA **lock_p= thd->locked_tables->locks;
THR_LOCK_DATA **end_p= lock_p + thd->locked_tables->lock_count;
for (; lock_p < end_p; lock_p++)
{
if ((*lock_p)->type >= TL_WRITE_ALLOW_WRITE)
{
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
return 1;
}
}
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
return 1;
}
/*
Writing to the binlog could cause deadlocks, as we don't log
@ -7105,7 +7092,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
if (lock_global_read_lock(thd))
return 1; // Killed
if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
FALSE : TRUE, TRUE))
FALSE : TRUE))
result= 1;
if (make_global_read_lock_block_commit(thd)) // Killed
@ -7117,8 +7104,35 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
}
else
{
if (thd && thd->locked_tables)
{
/*
If we are under LOCK TABLES we should have a write
lock on tables which we are going to flush.
*/
if (tables)
{
for (TABLE_LIST *t= tables; t; t= t->next_local)
if (!find_write_locked_table(thd->open_tables, t->db,
t->table_name))
return 1;
}
else
{
for (TABLE *tab= thd->open_tables; tab; tab= tab->next)
{
if (tab->reginfo.lock_type < TL_WRITE_ALLOW_WRITE)
{
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),
tab->s->table_name.str);
return 1;
}
}
}
}
if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
FALSE : TRUE, FALSE))
FALSE : TRUE))
result= 1;
}
my_dbopt_cleanup();
@ -8092,6 +8106,42 @@ bool parse_sql(THD *thd,
return ret_value;
}
/**
Auxiliary function which marks metadata locks for all tables
on which we plan to take write lock as upgradable.
*/
static void adjust_mdl_locks_upgradability(TABLE_LIST *tables)
{
TABLE_LIST *tab, *otab;
for (tab= tables; tab; tab= tab->next_global)
{
if (tab->lock_type >= TL_WRITE_ALLOW_WRITE)
tab->mdl_upgradable= TRUE;
else
{
/*
TODO: To get rid of this loop we need to change our code to do
metadata lock upgrade only for those instances of tables
which are write locked instead of doing such upgrade for
all instances of tables.
*/
for (otab= tables; otab; otab= otab->next_global)
if (otab->lock_type >= TL_WRITE_ALLOW_WRITE &&
otab->db_length == tab->db_length &&
otab->table_name_length == tab->table_name_length &&
!strcmp(otab->db, tab->db) &&
!strcmp(otab->table_name, tab->table_name))
{
tab->mdl_upgradable= TRUE;
break;
}
}
}
}
/**
@} (end of group Runtime_Environment)
*/

View File

@ -6225,7 +6225,7 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
*/
pthread_mutex_lock(&LOCK_open);
lpt->thd->in_lock_tables= 1;
err= reopen_tables(lpt->thd, 1, 1);
err= reopen_tables(lpt->thd, 1);
lpt->thd->in_lock_tables= 0;
if (err)
{
@ -6564,7 +6564,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
write_log_drop_partition(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_3") ||
(not_completed= FALSE) ||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
abort_and_upgrade_lock(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_4") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_5") ||
@ -6631,7 +6631,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_add_partition_2") ||
mysql_change_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_3") ||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
abort_and_upgrade_lock(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_4") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_5") ||
@ -6647,7 +6647,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_add_partition_8") ||
(write_log_completed(lpt, FALSE), FALSE) ||
ERROR_INJECT_CRASH("crash_add_partition_9") ||
(alter_partition_lock_handling(lpt), FALSE))
(alter_partition_lock_handling(lpt), FALSE))
{
handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
goto err;
@ -6721,7 +6721,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
write_log_final_change_partition(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_4") ||
(not_completed= FALSE) ||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
abort_and_upgrade_lock(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_5") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_6") ||

125
sql/sql_plist.h Normal file
View File

@ -0,0 +1,125 @@
#ifndef SQL_PLIST_H
#define SQL_PLIST_H
/* Copyright (C) 2008 MySQL AB
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
template <typename T, typename B> class I_P_List_iterator;
/**
Intrusive parameterized list.
Unlike I_List does not require its elements to be descendant of ilink
class and therefore allows them to participate in several such lists
simultaneously.
Unlike List is doubly-linked list and thus supports efficient deletion
of element without iterator.
@param T Type of elements which will belong to list.
@param B Class which via its methods specifies which members
of T should be used for participating in this list.
Here is typical layout of such class:
struct B
{
static inline T **next_ptr(T *el)
{
return &el->next;
}
static inline T ***prev_ptr(T *el)
{
return &el->prev;
}
};
*/
template <typename T, typename B>
class I_P_List
{
T *first;
/*
Do not prohibit copying of I_P_List object to simplify their usage in
backup/restore scenarios. Note that performing any operations on such
is a bad idea.
*/
public:
I_P_List() : first(NULL) { };
inline void empty() { first= NULL; }
inline bool is_empty() { return (first == NULL); }
inline void push_front(T* a)
{
*B::next_ptr(a)= first;
if (first)
*B::prev_ptr(first)= B::next_ptr(a);
first= a;
*B::prev_ptr(a)= &first;
}
inline void remove(T *a)
{
T *next= *B::next_ptr(a);
if (next)
*B::prev_ptr(next)= *B::prev_ptr(a);
**B::prev_ptr(a)= next;
}
inline T* head() { return first; }
void swap(I_P_List<T,B> &rhs)
{
swap_variables(T *, first, rhs.first);
if (first)
*B::prev_ptr(first)= &first;
if (rhs.first)
*B::prev_ptr(rhs.first)= &rhs.first;
}
#ifndef _lint
friend class I_P_List_iterator<T, B>;
#endif
};
/**
Iterator for I_P_List.
*/
template <typename T, typename B>
class I_P_List_iterator
{
I_P_List<T, B> *list;
T *current;
public:
I_P_List_iterator(I_P_List<T, B> &a) : list(&a), current(a.first) {}
inline void init(I_P_List<T, B> &a)
{
list= &a;
current= a.first;
}
inline T* operator++(int)
{
T *result= current;
if (result)
current= *B::next_ptr(current);
return result;
}
inline void rewind()
{
current= list->first;
}
};
#endif

View File

@ -1366,6 +1366,7 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
tables.alias= tables.table_name= (char*)"plugin";
tables.lock_type= TL_READ;
tables.db= new_thd->db;
alloc_mdl_locks(&tables, tmp_root);
#ifdef EMBEDDED_LIBRARY
/*
@ -1659,6 +1660,8 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl
if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
alloc_mdl_locks(&tables, thd->mem_root);
/* need to open before acquiring LOCK_plugin or it will deadlock */
if (! (table = open_ltable(thd, &tables, TL_WRITE, 0)))
DBUG_RETURN(TRUE);
@ -1732,6 +1735,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
bzero(&tables, sizeof(tables));
tables.db= (char *)"mysql";
tables.table_name= tables.alias= (char *)"plugin";
alloc_mdl_locks(&tables, thd->mem_root);
/* need to open before acquiring LOCK_plugin or it will deadlock */
if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))

View File

@ -1673,7 +1673,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
{
lex->link_first_table_back(create_table, link_to_local);
create_table->create= TRUE;
create_table->open_table_type= TABLE_LIST::OPEN_OR_CREATE;
}
if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0))

View File

@ -51,7 +51,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
DBUG_RETURN(1);
}
mysql_ha_rm_tables(thd, table_list, FALSE);
mysql_ha_rm_tables(thd, table_list);
if (wait_if_global_read_lock(thd,0,1))
DBUG_RETURN(1);
@ -133,12 +133,13 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
}
}
pthread_mutex_lock(&LOCK_open);
if (lock_table_names_exclusively(thd, table_list))
{
pthread_mutex_unlock(&LOCK_open);
if (lock_table_names(thd, table_list))
goto err;
}
pthread_mutex_lock(&LOCK_open);
for (ren_table= table_list; ren_table; ren_table= ren_table->next_local)
expel_table_from_cache(0, ren_table->db, ren_table->table_name);
error=0;
if ((ren_table=rename_tables(thd,table_list,0)))
@ -184,9 +185,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
if (!error)
query_cache_invalidate3(thd, table_list, 0);
pthread_mutex_lock(&LOCK_open);
unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
pthread_mutex_unlock(&LOCK_open);
unlock_table_names(thd);
err:
start_waiting_global_read_lock(thd);

View File

@ -224,12 +224,7 @@ bool servers_reload(THD *thd)
bool return_val= TRUE;
DBUG_ENTER("servers_reload");
if (thd->locked_tables)
{ // Can't have locked tables here
thd->lock=thd->locked_tables;
thd->locked_tables=0;
close_thread_tables(thd);
}
unlock_locked_tables(thd); // Can't have locked tables here
DBUG_PRINT("info", ("locking servers_cache"));
rw_wrlock(&THR_LOCK_servers);
@ -238,6 +233,7 @@ bool servers_reload(THD *thd)
tables[0].alias= tables[0].table_name= (char*) "servers";
tables[0].db= (char*) "mysql";
tables[0].lock_type= TL_READ;
alloc_mdl_locks(tables, thd->mem_root);
if (simple_open_n_lock_tables(thd, tables))
{
@ -368,6 +364,7 @@ insert_server(THD *thd, FOREIGN_SERVER *server)
bzero((char*) &tables, sizeof(tables));
tables.db= (char*) "mysql";
tables.alias= tables.table_name= (char*) "servers";
alloc_mdl_locks(&tables, thd->mem_root);
/* need to open before acquiring THR_LOCK_plugin or it will deadlock */
if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))
@ -586,6 +583,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
bzero((char*) &tables, sizeof(tables));
tables.db= (char*) "mysql";
tables.alias= tables.table_name= (char*) "servers";
alloc_mdl_locks(&tables, thd->mem_root);
rw_wrlock(&THR_LOCK_servers);
@ -710,6 +708,7 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered)
bzero((char*) &tables, sizeof(tables));
tables.db= (char*)"mysql";
tables.alias= tables.table_name= (char*)"servers";
alloc_mdl_locks(&tables, thd->mem_root);
if (!(table= open_ltable(thd, &tables, TL_WRITE, 0)))
{

View File

@ -2963,7 +2963,7 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables,
table, res, db_name,
table_name));
thd->temporary_tables= 0;
close_tables_for_reopen(thd, &show_table_list);
close_tables_for_reopen(thd, &show_table_list, FALSE);
DBUG_RETURN(error);
}
@ -3106,6 +3106,9 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
char key[MAX_DBKEY_LENGTH];
uint key_length;
char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
MDL_LOCK mdl_lock;
char mdlkey[MAX_DBKEY_LENGTH];
bool retry;
bzero((char*) &table_list, sizeof(TABLE_LIST));
bzero((char*) &tbl, sizeof(TABLE));
@ -3130,6 +3133,34 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
table_list.db= db_name->str;
}
mdl_init_lock(&mdl_lock, mdlkey, 0, db_name->str, table_name->str);
table_list.mdl_lock= &mdl_lock;
mdl_add_lock(&thd->mdl_context, &mdl_lock);
mdl_set_lock_priority(&mdl_lock, MDL_HIGH_PRIO);
/*
TODO: investigate if in this particular situation we can get by
simply obtaining internal lock of data-dictionary (ATM it
is LOCK_open) instead of obtaning full-blown metadata lock.
*/
while (1)
{
if (mdl_acquire_shared_lock(&mdl_lock, &retry))
{
if (!retry || mdl_wait_for_locks(&thd->mdl_context))
{
/*
Some error occured or we have been killed while waiting
for conflicting locks to go away, let the caller to handle
the situation.
*/
return 1;
}
continue;
}
break;
}
key_length= create_table_def_key(thd, key, &table_list, 0);
pthread_mutex_lock(&LOCK_open);
share= get_table_share(thd, &table_list, key,
@ -3137,7 +3168,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
if (!share)
{
res= 0;
goto err;
goto err_unlock;
}
if (share->is_view)
@ -3146,7 +3177,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
{
/* skip view processing */
res= 0;
goto err1;
goto err_share;
}
else if (schema_table->i_s_requested_object & OPEN_VIEW_FULL)
{
@ -3155,7 +3186,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
open_normal_and_derived_tables()
*/
res= 1;
goto err1;
goto err_share;
}
}
@ -3171,14 +3202,17 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
res= schema_table->process_table(thd, &table_list, table,
res, db_name, table_name);
closefrm(&tbl, true);
goto err;
goto err_unlock;
}
err1:
err_share:
release_table_share(share, RELEASE_NORMAL);
err:
err_unlock:
pthread_mutex_unlock(&LOCK_open);
err:
mdl_release_lock(&thd->mdl_context, &mdl_lock);
thd->clear_error();
return res;
}
@ -3425,7 +3459,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
res= schema_table->process_table(thd, show_table_list, table,
res, &orig_db_name,
&tmp_lex_string);
close_tables_for_reopen(thd, &show_table_list);
close_tables_for_reopen(thd, &show_table_list, FALSE);
}
DBUG_ASSERT(!lex->query_tables_own_last);
if (res)
@ -7273,6 +7307,8 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name)
uint num_tables; /* NOTE: unused, only to pass to open_tables(). */
alloc_mdl_locks(lst, thd->mem_root);
if (open_tables(thd, &lst, &num_tables, 0))
{
my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0),

View File

@ -53,6 +53,7 @@ static bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
HA_CREATE_INFO *create_info,
Alter_info *alter_info);
static bool close_cached_table(THD *thd, TABLE *table);
#ifndef DBUG_OFF
@ -1791,11 +1792,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
DBUG_RETURN(TRUE);
}
/*
Acquire LOCK_open after wait_if_global_read_lock(). If we would hold
LOCK_open during wait_if_global_read_lock(), other threads could not
close their tables. This would make a pretty deadlock.
*/
thd->push_internal_handler(&err_handler);
error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
thd->pop_internal_handler();
@ -1867,16 +1863,14 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
built_query.append("DROP TABLE ");
}
mysql_ha_rm_tables(thd, tables, FALSE);
pthread_mutex_lock(&LOCK_open);
mysql_ha_rm_tables(thd, tables);
/*
If we have the table in the definition cache, we don't have to check the
.frm file to find if the table is a normal table (not view) and what
engine to use.
*/
pthread_mutex_lock(&LOCK_open);
for (table= tables; table; table= table->next_local)
{
TABLE_SHARE *share;
@ -1889,16 +1883,32 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
check_if_log_table(table->db_length, table->db,
table->table_name_length, table->table_name, 1))
{
my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
pthread_mutex_unlock(&LOCK_open);
my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
DBUG_RETURN(1);
}
}
pthread_mutex_unlock(&LOCK_open);
if (!drop_temporary && lock_table_names_exclusively(thd, tables))
if (!drop_temporary)
{
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(1);
if (!thd->locked_tables)
{
if (lock_table_names(thd, tables))
DBUG_RETURN(1);
pthread_mutex_lock(&LOCK_open);
for (table= tables; table; table= table->next_local)
expel_table_from_cache(0, table->db, table->table_name);
pthread_mutex_unlock(&LOCK_open);
}
else if (thd->locked_tables)
{
for (table= tables; table; table= table->next_local)
if (!find_temporary_table(thd, table->db, table->table_name) &&
!find_write_locked_table(thd->open_tables, table->db,
table->table_name))
DBUG_RETURN(1);
}
}
for (table= tables; table; table= table->next_local)
@ -1973,17 +1983,21 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
table_type= table->db_type;
if (!drop_temporary)
{
TABLE *locked_table;
abort_locked_tables(thd, db, table->table_name);
remove_table_from_cache(thd, db, table->table_name,
RTFC_WAIT_OTHER_THREAD_FLAG |
RTFC_CHECK_KILLED_FLAG);
/*
If the table was used in lock tables, remember it so that
unlock_table_names can free it
*/
if ((locked_table= drop_locked_tables(thd, db, table->table_name)))
table->table= locked_table;
if (thd->locked_tables)
{
TABLE *tab= find_locked_table(thd->open_tables, db, table->table_name);
if (close_cached_table(thd, tab))
{
error= -1;
goto err_with_placeholders;
}
/*
Leave LOCK TABLES mode if we managed to drop all tables
which were locked.
*/
if (thd->locked_tables->table_count == 0)
unlock_locked_tables(thd);
}
if (thd->killed)
{
@ -1997,6 +2011,11 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
table->internal_tmp_table ?
FN_IS_TMP : 0);
}
/*
TODO: Investigate what should be done to remove this lock completely.
Is exclusive meta-data lock enough ?
*/
pthread_mutex_lock(&LOCK_open);
if (drop_temporary ||
((table_type == NULL &&
access(path, F_OK) &&
@ -2049,6 +2068,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
error|= new_error;
}
}
pthread_mutex_unlock(&LOCK_open);
if (error)
{
if (wrong_tables.length())
@ -2064,11 +2084,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
table->table_name););
}
/*
It's safe to unlock LOCK_open: we have an exclusive lock
on the table name.
*/
pthread_mutex_unlock(&LOCK_open);
thd->thread_specific_used|= tmp_table_deleted;
error= 0;
if (wrong_tables.length())
@ -2151,10 +2166,19 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
*/
}
}
pthread_mutex_lock(&LOCK_open);
err_with_placeholders:
unlock_table_names(thd, tables, (TABLE_LIST*) 0);
pthread_mutex_unlock(&LOCK_open);
if (!drop_temporary)
{
/*
Under LOCK TABLES we should release meta-data locks on the tables
which were dropped. Otherwise we can rely on close_thread_tables()
doing this. Unfortunately in this case we are likely to get more
false positives in lock_table_name_if_not_cached() function. So
it makes sense to remove exclusive meta-data locks in all cases.
*/
mdl_release_exclusive_locks(&thd->mdl_context);
}
DBUG_RETURN(error);
}
@ -4005,6 +4029,34 @@ warn:
}
/**
Auxiliary function which obtains exclusive meta-data lock on the
table if there are no shared or exclusive on it already.
See mdl_try_acquire_exclusive_lock() function for more info.
TODO: This function is here mostly to simplify current patch
and probably should be removed.
TODO: Investigate if it is kosher to leave lock request in the
context in the case when we fail to obtain the lock.
*/
static bool lock_table_name_if_not_cached(THD *thd, const char *db,
const char *table_name,
MDL_LOCK **lock)
{
if (!(*lock= mdl_alloc_lock(0, db, table_name, thd->mem_root)))
return TRUE;
mdl_set_lock_type(*lock, MDL_EXCLUSIVE);
mdl_add_lock(&thd->mdl_context, *lock);
if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock))
{
*lock= 0;
}
return FALSE;
}
/*
Database and name-locking aware wrapper for mysql_create_table_no_lock(),
*/
@ -4015,7 +4067,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
bool internal_tmp_table,
uint select_field_count)
{
TABLE *name_lock= 0;
MDL_LOCK *target_lock= 0;
bool result;
DBUG_ENTER("mysql_create_table");
@ -4038,12 +4090,12 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock))
{
result= TRUE;
goto unlock;
}
if (!name_lock)
if (!target_lock)
{
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
@ -4069,12 +4121,8 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
select_field_count);
unlock:
if (name_lock)
{
pthread_mutex_lock(&LOCK_open);
unlink_open_table(thd, name_lock, FALSE);
pthread_mutex_unlock(&LOCK_open);
}
if (target_lock)
mdl_release_exclusive_locks(&thd->mdl_context);
pthread_mutex_lock(&LOCK_lock_db);
if (!--creating_table && creating_database)
pthread_cond_signal(&COND_refresh);
@ -4212,80 +4260,83 @@ mysql_rename_table(handlerton *base, const char *old_db,
}
/*
Force all other threads to stop using the table
/**
Force all other threads to stop using the table by upgrading
metadata lock on it and remove unused TABLE instances from cache.
SYNOPSIS
wait_while_table_is_used()
thd Thread handler
table Table to remove from cache
function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted
HA_EXTRA_FORCE_REOPEN if table is not be used
HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed
NOTES
When returning, the table will be unusable for other threads until
the table is closed.
@param thd Thread handler
@param table Table to remove from cache
@param function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted
HA_EXTRA_FORCE_REOPEN if table is not be used
HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed
PREREQUISITES
Lock on LOCK_open
Win32 clients must also have a WRITE LOCK on the table !
@note When returning, the table will be unusable for other threads
until metadata lock is downgraded.
@retval FALSE Success.
@retval TRUE Failure (e.g. because thread was killed).
*/
void wait_while_table_is_used(THD *thd, TABLE *table,
bool wait_while_table_is_used(THD *thd, TABLE *table,
enum ha_extra_function function)
{
enum thr_lock_type old_lock_type;
DBUG_ENTER("wait_while_table_is_used");
DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu",
table->s->table_name.str, (ulong) table->s,
table->db_stat, table->s->version));
safe_mutex_assert_owner(&LOCK_open);
(void) table->file->extra(function);
/* Mark all tables that are in use as 'old' */
old_lock_type= table->reginfo.lock_type;
mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */
/* Wait until all there are no other threads that has this table open */
remove_table_from_cache(thd, table->s->db.str,
table->s->table_name.str,
RTFC_WAIT_OTHER_THREAD_FLAG);
DBUG_VOID_RETURN;
if (mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, 0,
table->s->db.str,
table->s->table_name.str))
{
mysql_lock_downgrade_write(thd, table, old_lock_type);
DBUG_RETURN(TRUE);
}
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(thd, table->s->db.str, table->s->table_name.str);
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(FALSE);
}
/*
Close a cached table
SYNOPSIS
close_cached_table()
thd Thread handler
table Table to remove from cache
/**
Upgrade metadata lock on the table and close all its instances.
NOTES
Function ends by signaling threads waiting for the table to try to
reopen the table.
@param thd Thread handler
@param table Table to remove from cache
PREREQUISITES
Lock on LOCK_open
Win32 clients must also have a WRITE LOCK on the table !
@retval FALSE Success.
@retval TRUE Failure (e.g. because thread was killed).
*/
void close_cached_table(THD *thd, TABLE *table)
static bool close_cached_table(THD *thd, TABLE *table)
{
DBUG_ENTER("close_cached_table");
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
/* FIXME: check if we pass proper parameters everywhere. */
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
DBUG_RETURN(TRUE);
/* Close lock if this is not got with LOCK TABLES */
if (thd->lock)
{
mysql_unlock_tables(thd, thd->lock);
thd->lock=0; // Start locked threads
}
pthread_mutex_lock(&LOCK_open);
/* Close all copies of 'table'. This also frees all LOCK TABLES lock */
unlink_open_table(thd, table, TRUE);
/* When lock on LOCK_open is freed other threads can continue */
broadcast_refresh();
DBUG_VOID_RETURN;
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(FALSE);
}
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
@ -4308,6 +4359,7 @@ static int send_check_errmsg(THD *thd, TABLE_LIST* table,
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
HA_CHECK_OPT *check_opt)
{
MDL_LOCK *mdl_lock= 0;
DBUG_ENTER("prepare_for_restore");
if (table->table) // do not overwrite existing tables on restore
@ -4331,22 +4383,25 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
build_table_filename(dst_path, sizeof(dst_path) - 1,
db, table_name, reg_ext, 0);
if (lock_and_wait_for_table_name(thd,table))
DBUG_RETURN(-1);
mdl_lock= mdl_alloc_lock(0, table->db, table->table_name,
thd->mem_root);
mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE);
mdl_add_lock(&thd->mdl_context, mdl_lock);
if (mdl_acquire_exclusive_locks(&thd->mdl_context))
DBUG_RETURN(TRUE);
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(0, table->db, table->table_name);
pthread_mutex_unlock(&LOCK_open);
if (my_copy(src_path, dst_path, MYF(MY_WME)))
{
pthread_mutex_lock(&LOCK_open);
unlock_table_name(thd, table);
pthread_mutex_unlock(&LOCK_open);
mdl_release_lock(&thd->mdl_context, mdl_lock);
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
"Failed copying .frm file"));
}
if (mysql_truncate(thd, table, 1))
{
pthread_mutex_lock(&LOCK_open);
unlock_table_name(thd, table);
pthread_mutex_unlock(&LOCK_open);
mdl_release_lock(&thd->mdl_context, mdl_lock);
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
"Failed generating table from .frm file"));
}
@ -4357,10 +4412,11 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
to finish the restore in the handler later on
*/
pthread_mutex_lock(&LOCK_open);
if (reopen_name_locked_table(thd, table, TRUE))
if (reopen_name_locked_table(thd, table))
{
unlock_table_name(thd, table);
pthread_mutex_unlock(&LOCK_open);
if (mdl_lock)
mdl_release_lock(&thd->mdl_context, mdl_lock);
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
"Failed to open partially restored table"));
}
@ -4380,6 +4436,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
char from[FN_REFLEN],tmp[FN_REFLEN+32];
const char **ext;
MY_STAT stat_info;
MDL_LOCK *mdl_lock;
DBUG_ENTER("prepare_for_repair");
if (!(check_opt->sql_flags & TT_USEFRM))
@ -4391,6 +4448,17 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
uint key_length;
key_length= create_table_def_key(thd, key, table_list, 0);
/*
TODO: Check that REPAIR's code also conforms to meta-data
locking protocol. Fix if it is not.
*/
mdl_lock= mdl_alloc_lock(0, table_list->db, table_list->table_name,
thd->mem_root);
mdl_set_lock_type(mdl_lock, MDL_EXCLUSIVE);
mdl_add_lock(&thd->mdl_context, mdl_lock);
if (mdl_acquire_exclusive_locks(&thd->mdl_context))
DBUG_RETURN(0);
pthread_mutex_lock(&LOCK_open);
if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
&error))))
@ -4457,41 +4525,29 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
from, current_pid, thd->thread_id);
/* If we could open the table, close it */
if (table_list->table)
{
pthread_mutex_lock(&LOCK_open);
close_cached_table(thd, table);
pthread_mutex_unlock(&LOCK_open);
}
if (lock_and_wait_for_table_name(thd,table_list))
{
error= -1;
goto end;
/* If we could open the table, close it */
if (close_cached_table(thd, table))
goto end;
table_list->table= 0;
}
// After this point we have X mdl lock in both cases
if (my_rename(from, tmp, MYF(MY_WME)))
{
pthread_mutex_lock(&LOCK_open);
unlock_table_name(thd, table_list);
pthread_mutex_unlock(&LOCK_open);
error= send_check_errmsg(thd, table_list, "repair",
"Failed renaming data file");
goto end;
}
if (mysql_truncate(thd, table_list, 1))
{
pthread_mutex_lock(&LOCK_open);
unlock_table_name(thd, table_list);
pthread_mutex_unlock(&LOCK_open);
error= send_check_errmsg(thd, table_list, "repair",
"Failed generating table from .frm file");
goto end;
}
if (my_rename(tmp, from, MYF(MY_WME)))
{
pthread_mutex_lock(&LOCK_open);
unlock_table_name(thd, table_list);
pthread_mutex_unlock(&LOCK_open);
error= send_check_errmsg(thd, table_list, "repair",
"Failed restoring .MYD file");
goto end;
@ -4502,9 +4558,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
to finish the repair in the handler later on.
*/
pthread_mutex_lock(&LOCK_open);
if (reopen_name_locked_table(thd, table_list, TRUE))
if (reopen_name_locked_table(thd, table_list))
{
unlock_table_name(thd, table_list);
pthread_mutex_unlock(&LOCK_open);
error= send_check_errmsg(thd, table_list, "repair",
"Failed to open partially repaired table");
@ -4519,6 +4574,8 @@ end:
closefrm(table, 1); // Free allocated memory
pthread_mutex_unlock(&LOCK_open);
}
if (error)
mdl_release_exclusive_locks(&thd->mdl_context);
DBUG_RETURN(error);
}
@ -4566,7 +4623,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
mysql_ha_rm_tables(thd, tables, FALSE);
mysql_ha_rm_tables(thd, tables);
for (table= tables; table; table= table->next_local)
{
@ -5063,6 +5120,7 @@ bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
{
DBUG_ENTER("mysql_repair_table");
set_all_mdl_upgradable(tables);
DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
"repair", TL_WRITE, 1,
test(check_opt->sql_flags & TT_USEFRM),
@ -5250,7 +5308,7 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table,
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
HA_CREATE_INFO *create_info)
{
TABLE *name_lock= 0;
MDL_LOCK *target_lock= 0;
char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1];
uint dst_path_length;
char *db= table->db;
@ -5307,9 +5365,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
}
else
{
if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock))
goto err;
if (!name_lock)
if (!target_lock)
goto table_exists;
dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1,
db, table_name, reg_ext, 0);
@ -5453,9 +5511,8 @@ binlog:
The table will be closed by unlink_open_table() at the end
of this function.
*/
table->table= name_lock;
pthread_mutex_lock(&LOCK_open);
if (reopen_name_locked_table(thd, table, FALSE))
if (reopen_name_locked_table(thd, table))
{
pthread_mutex_unlock(&LOCK_open);
goto err;
@ -5468,6 +5525,10 @@ binlog:
DBUG_ASSERT(result == 0); // store_create_info() always return 0
write_bin_log(thd, TRUE, query.ptr(), query.length());
pthread_mutex_lock(&LOCK_open);
unlink_open_table(thd, table->table, FALSE);
pthread_mutex_unlock(&LOCK_open);
}
else // Case 1
write_bin_log(thd, TRUE, thd->query(), thd->query_length());
@ -5482,12 +5543,8 @@ binlog:
res= FALSE;
err:
if (name_lock)
{
pthread_mutex_lock(&LOCK_open);
unlink_open_table(thd, name_lock, FALSE);
pthread_mutex_unlock(&LOCK_open);
}
if (target_lock)
mdl_release_exclusive_locks(&thd->mdl_context);
DBUG_RETURN(res);
}
@ -5570,7 +5627,7 @@ mysql_discard_or_import_tablespace(THD *thd,
err:
ha_autocommit_or_rollback(thd, error);
thd->tablespace_op=FALSE;
if (error == 0)
{
my_ok(thd);
@ -5578,7 +5635,7 @@ err:
}
table->file->print_error(error, MYF(0));
DBUG_RETURN(-1);
}
@ -6424,7 +6481,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
Alter_info *alter_info,
uint order_num, ORDER *order, bool ignore)
{
TABLE *table, *new_table= 0, *name_lock= 0;
TABLE *table, *new_table= 0;
MDL_LOCK *target_lock= 0;
int error= 0;
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
@ -6507,7 +6565,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0);
build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
mysql_ha_rm_tables(thd, table_list, FALSE);
mysql_ha_rm_tables(thd, table_list);
/* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
if (alter_info->tablespace_op != NO_TABLESPACE_OP)
@ -6563,13 +6621,14 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (wait_if_global_read_lock(thd,0,1))
DBUG_RETURN(TRUE);
pthread_mutex_lock(&LOCK_open);
if (lock_table_names(thd, table_list))
{
error= 1;
goto view_err;
}
pthread_mutex_lock(&LOCK_open);
if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
{
if (mysql_bin_log.is_open())
@ -6581,15 +6640,17 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
my_ok(thd);
}
pthread_mutex_unlock(&LOCK_open);
unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
unlock_table_names(thd);
view_err:
pthread_mutex_unlock(&LOCK_open);
start_waiting_global_read_lock(thd);
DBUG_RETURN(error);
}
table_list->mdl_upgradable= TRUE;
if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
DBUG_RETURN(TRUE);
table->use_all_columns();
@ -6644,9 +6705,9 @@ view_err:
}
else
{
if (lock_table_name_if_not_cached(thd, new_db, new_name, &name_lock))
if (lock_table_name_if_not_cached(thd, new_db, new_name, &target_lock))
DBUG_RETURN(TRUE);
if (!name_lock)
if (!target_lock)
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
DBUG_RETURN(TRUE);
@ -6737,26 +6798,15 @@ view_err:
case LEAVE_AS_IS:
break;
case ENABLE:
/*
wait_while_table_is_used() ensures that table being altered is
opened only by this thread and that TABLE::TABLE_SHARE::version
of TABLE object corresponding to this table is 0.
The latter guarantees that no DML statement will open this table
until ALTER TABLE finishes (i.e. until close_thread_tables())
while the fact that the table is still open gives us protection
from concurrent DDL statements.
*/
pthread_mutex_lock(&LOCK_open);
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
pthread_mutex_unlock(&LOCK_open);
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto err;
DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
/* COND_refresh will be signaled in close_thread_tables() */
break;
case DISABLE:
pthread_mutex_lock(&LOCK_open);
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
pthread_mutex_unlock(&LOCK_open);
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto err;
error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
/* COND_refresh will be signaled in close_thread_tables() */
break;
@ -6773,24 +6823,19 @@ view_err:
table->alias);
}
pthread_mutex_lock(&LOCK_open);
/*
Unlike to the above case close_cached_table() below will remove ALL
instances of TABLE from table cache (it will also remove table lock
held by this thread). So to make actual table renaming and writing
to binlog atomic we have to put them into the same critical section
protected by LOCK_open mutex. This also removes gap for races between
access() and mysql_rename_table() calls.
*/
if (!error && (new_name != table_name || new_db != db))
{
thd_proc_info(thd, "rename");
/*
Then do a 'simple' rename of the table. First we need to close all
instances of 'source' table.
Note that if close_cached_table() returns error here (i.e. if
this thread was killed) then it must be that previous step of
simple rename did nothing and therefore we can safely reture
without additional clean-up.
*/
close_cached_table(thd, table);
if (close_cached_table(thd, table))
goto err;
/*
Then, we want check once again that target table does not exist.
Actually the order of these two steps does not matter since
@ -6807,6 +6852,7 @@ view_err:
else
{
*fn_ext(new_name)=0;
pthread_mutex_lock(&LOCK_open);
if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
error= -1;
else if (Table_triggers_list::change_table_name(thd, db, table_name,
@ -6816,6 +6862,7 @@ view_err:
table_name, 0);
error= -1;
}
pthread_mutex_unlock(&LOCK_open);
}
}
@ -6837,11 +6884,24 @@ view_err:
table->file->print_error(error, MYF(0));
error= -1;
}
if (name_lock)
unlink_open_table(thd, name_lock, FALSE);
pthread_mutex_unlock(&LOCK_open);
table_list->table= NULL; // For query cache
query_cache_invalidate3(thd, table_list, 0);
if (thd->locked_tables)
{
/*
Under LOCK TABLES we should adjust meta-data locks before finishing
statement. Otherwise we can rely on close_thread_tables() releasing
them.
TODO: Investigate what should be done with upgraded table-level
lock here...
*/
if (new_name != table_name || new_db != db)
mdl_release_exclusive_locks(&thd->mdl_context);
else
mdl_downgrade_exclusive_locks(&thd->mdl_context);
}
DBUG_RETURN(error);
}
@ -7073,7 +7133,7 @@ view_err:
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (fast_alter_partition)
{
DBUG_ASSERT(!name_lock);
DBUG_ASSERT(!target_lock);
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
create_info, table_list,
db, table_name,
@ -7158,7 +7218,7 @@ view_err:
tbl.db= new_db;
tbl.table_name= tbl.alias= tmp_name;
/* Table is in thd->temporary_tables */
new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
new_table= open_table(thd, &tbl, thd->mem_root, (enum_open_table_action*) 0,
MYSQL_LOCK_IGNORE_FLUSH);
}
else
@ -7168,10 +7228,10 @@ view_err:
build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "",
FN_IS_TMP);
/* Open our intermediate table */
new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
new_table= open_temporary_table(thd, path, new_db, tmp_name, 1);
}
if (!new_table)
goto err1;
goto err_new_table_cleanup;
/*
Note: In case of MERGE table, we do not attach children. We do not
copy data for MERGE tables. Only the children have data.
@ -7200,9 +7260,8 @@ view_err:
}
else
{
pthread_mutex_lock(&LOCK_open);
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
pthread_mutex_unlock(&LOCK_open);
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto err_new_table_cleanup;
thd_proc_info(thd, "manage keys");
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
alter_info->keys_onoff);
@ -7253,7 +7312,7 @@ view_err:
table->key_info= key_info;
table->file->print_error(error, MYF(0));
table->key_info= save_key_info;
goto err1;
goto err_new_table_cleanup;
}
}
/*end of if (index_add_count)*/
@ -7276,14 +7335,14 @@ view_err:
index_drop_count)))
{
table->file->print_error(error, MYF(0));
goto err1;
goto err_new_table_cleanup;
}
/* Tell the handler to finally drop the indexes. */
if ((error= table->file->final_drop_index(table)))
{
table->file->print_error(error, MYF(0));
goto err1;
goto err_new_table_cleanup;
}
}
/*end of if (index_drop_count)*/
@ -7296,16 +7355,16 @@ view_err:
/* Need to commit before a table is unlocked (NDB requirement). */
DBUG_PRINT("info", ("Committing before unlocking table"));
if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd))
goto err1;
goto err_new_table_cleanup;
committed= 1;
}
/*end of if (! new_table) for add/drop index*/
if (error)
goto err_new_table_cleanup;
if (table->s->tmp_table != NO_TMP_TABLE)
{
/* We changed a temporary table */
if (error)
goto err1;
/* Close lock if this is a transactional table */
if (thd->lock)
{
@ -7316,28 +7375,22 @@ view_err:
close_temporary_table(thd, table, 1, 1);
/* Should pass the 'new_name' as we store table name in the cache */
if (rename_temporary_table(thd, new_table, new_db, new_name))
goto err1;
goto err_new_table_cleanup;
/* We don't replicate alter table statement on temporary tables */
if (!thd->current_stmt_binlog_row_based)
write_bin_log(thd, TRUE, thd->query(), thd->query_length());
goto end_temporary;
}
/*
Close the intermediate table that will be the new table, but do
not delete it! Even altough MERGE tables do not have their children
attached here it is safe to call close_temporary_table().
*/
if (new_table)
{
/*
Close the intermediate table that will be the new table.
Note that MERGE tables do not have their children attached here.
*/
intern_close_table(new_table);
my_free(new_table,MYF(0));
}
pthread_mutex_lock(&LOCK_open);
if (error)
{
(void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP);
pthread_mutex_unlock(&LOCK_open);
goto err;
close_temporary_table(thd, new_table, 1, 0);
new_table= 0;
}
/*
@ -7362,7 +7415,11 @@ view_err:
if (lower_case_table_names)
my_casedn_str(files_charset_info, old_name);
wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME);
if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
goto err_new_table_cleanup;
pthread_mutex_lock(&LOCK_open);
close_data_files_and_morph_locks(thd, db, table_name);
error=0;
@ -7431,8 +7488,7 @@ view_err:
table_list->table_name_length= strlen(new_name);
table_list->db= new_db;
table_list->db_length= strlen(new_db);
table_list->table= name_lock;
if (reopen_name_locked_table(thd, table_list, FALSE))
if (reopen_name_locked_table(thd, table_list))
goto err_with_placeholders;
t_table= table_list->table;
}
@ -7446,16 +7502,24 @@ view_err:
if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG,
create_info))
goto err_with_placeholders;
if (thd->locked_tables && new_name == table_name && new_db == db)
if (thd->locked_tables)
{
/*
We are going to reopen table down on the road, so we have to restore
state of the TABLE object which we used for obtaining of handler
object to make it suitable for reopening.
*/
DBUG_ASSERT(t_table == table);
table->open_placeholder= 1;
close_handle_and_leave_table_as_lock(table);
if (new_name == table_name && new_db == db)
{
/*
We are going to reopen table down on the road, so we have to restore
state of the TABLE object which we used for obtaining of handler
object to make it suitable for reopening.
*/
DBUG_ASSERT(t_table == table);
table->open_placeholder= 1;
close_handle_and_leave_table_as_lock(table);
}
else
{
/* Unlink the new name from the list of locked tables. */
unlink_open_table(thd, t_table, FALSE);
}
}
}
@ -7464,7 +7528,7 @@ view_err:
if (thd->locked_tables && new_name == table_name && new_db == db)
{
thd->in_lock_tables= 1;
error= reopen_tables(thd, 1, 1);
error= reopen_tables(thd, 1);
thd->in_lock_tables= 0;
if (error)
goto err_with_placeholders;
@ -7508,18 +7572,17 @@ view_err:
table_list->table=0; // For query cache
query_cache_invalidate3(thd, table_list, 0);
if (thd->locked_tables && (new_name != table_name || new_db != db))
if (thd->locked_tables)
{
/*
If are we under LOCK TABLES and did ALTER TABLE with RENAME we need
to remove placeholders for the old table and for the target table
from the list of open tables and table cache. If we are not under
LOCK TABLES we can rely on close_thread_tables() doing this job.
*/
pthread_mutex_lock(&LOCK_open);
unlink_open_table(thd, table, FALSE);
unlink_open_table(thd, name_lock, FALSE);
pthread_mutex_unlock(&LOCK_open);
if ((new_name != table_name || new_db != db))
{
pthread_mutex_lock(&LOCK_open);
unlink_open_table(thd, table, FALSE);
pthread_mutex_unlock(&LOCK_open);
mdl_release_exclusive_locks(&thd->mdl_context);
}
else
mdl_downgrade_exclusive_locks(&thd->mdl_context);
}
end_temporary:
@ -7530,7 +7593,7 @@ end_temporary:
thd->some_tables_deleted=0;
DBUG_RETURN(FALSE);
err1:
err_new_table_cleanup:
if (new_table)
{
/* close_temporary_table() frees the new_table pointer. */
@ -7574,12 +7637,8 @@ err:
alter_info->datetime_field->field_name);
thd->abort_on_warning= save_abort_on_warning;
}
if (name_lock)
{
pthread_mutex_lock(&LOCK_open);
unlink_open_table(thd, name_lock, FALSE);
pthread_mutex_unlock(&LOCK_open);
}
if (target_lock)
mdl_release_exclusive_locks(&thd->mdl_context);
DBUG_RETURN(TRUE);
err_with_placeholders:
@ -7589,9 +7648,8 @@ err_with_placeholders:
from list of open tables list and table cache.
*/
unlink_open_table(thd, table, FALSE);
if (name_lock)
unlink_open_table(thd, name_lock, FALSE);
pthread_mutex_unlock(&LOCK_open);
mdl_release_exclusive_locks(&thd->mdl_context);
DBUG_RETURN(TRUE);
}
/* mysql_alter_table */

View File

@ -75,7 +75,8 @@ print_where(COND *cond,const char *info, enum_query_type query_type)
void print_cached_tables(void)
{
uint idx,count,unused;
TABLE *start_link,*lnk;
TABLE_SHARE *share;
TABLE *start_link, *lnk, *entry;
compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions));
@ -83,16 +84,26 @@ void print_cached_tables(void)
pthread_mutex_lock(&LOCK_open);
puts("DB Table Version Thread Open Lock");
for (idx=unused=0 ; idx < open_cache.records ; idx++)
for (idx=unused=0 ; idx < table_def_cache.records ; idx++)
{
TABLE *entry=(TABLE*) my_hash_element(&open_cache,idx);
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str, entry->s->version,
entry->in_use ? entry->in_use->thread_id : 0L,
entry->db_stat ? 1 : 0,
entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : "Not in use");
if (!entry->in_use)
share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
while ((entry= it++))
{
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str, entry->s->version,
entry->in_use->thread_id, entry->db_stat ? 1 : 0,
lock_descriptions[(int)entry->reginfo.lock_type]);
}
it.init(share->free_tables);
while ((entry= it++))
{
unused++;
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str, entry->s->version,
0L, entry->db_stat ? 1 : 0, "Not in use");
}
}
count=0;
if ((start_link=lnk=unused_tables))
@ -104,17 +115,18 @@ void print_cached_tables(void)
printf("unused_links isn't linked properly\n");
return;
}
} while (count++ < open_cache.records && (lnk=lnk->next) != start_link);
} while (count++ < table_cache_count && (lnk=lnk->next) != start_link);
if (lnk != start_link)
{
printf("Unused_links aren't connected\n");
}
}
if (count != unused)
printf("Unused_links (%d) doesn't match open_cache: %d\n", count,unused);
printf("Unused_links (%d) doesn't match table_def_cache: %d\n", count,
unused);
printf("\nCurrent refresh version: %ld\n",refresh_version);
if (my_hash_check(&open_cache))
printf("Error: File hash table is corrupted\n");
if (my_hash_check(&table_def_cache))
printf("Error: Table definition hash table is corrupted\n");
fflush(stdout);
pthread_mutex_unlock(&LOCK_open);
/* purecov: end */
@ -380,7 +392,7 @@ static void display_table_locks(void)
LIST *list;
DYNAMIC_ARRAY saved_table_locks;
(void) my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO),open_cache.records + 20,50);
(void) my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO), table_cache_count + 20,50);
pthread_mutex_lock(&THR_LOCK_lock);
for (list= thr_lock_thread_list; list; list= list_rest(list))
{

View File

@ -387,8 +387,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
DBUG_RETURN(TRUE);
pthread_mutex_lock(&LOCK_open);
if (!create)
{
bool if_exists= thd->lex->drop_if_exists;
@ -444,26 +442,28 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
tables->required_type= FRMTYPE_TABLE;
/* Keep consistent with respect to other DDL statements */
mysql_ha_rm_tables(thd, tables, TRUE);
mysql_ha_rm_tables(thd, tables);
if (thd->locked_tables)
{
/* Table must be write locked */
if (name_lock_locked_table(thd, tables))
goto end;
pthread_mutex_lock(&LOCK_open);
}
else
{
/* Grab the name lock and insert the placeholder*/
/*
Obtain exlusive meta-data lock on the table and remove TABLE
instances from cache.
*/
if (lock_table_names(thd, tables))
goto end;
/* Convert the placeholder to a real table */
if (reopen_name_locked_table(thd, tables, TRUE))
{
unlock_table_name(thd, tables);
goto end;
}
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(0, tables->db, tables->table_name);
if (reopen_name_locked_table(thd, tables))
goto end_unlock;
}
table= tables->table;
@ -472,11 +472,11 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
if (!create)
{
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
goto end;
goto end_unlock;
}
if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table)))
goto end;
goto end_unlock;
}
result= (create ?
@ -489,7 +489,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
/* Make table suitable for reopening */
close_data_files_and_morph_locks(thd, tables->db, tables->table_name);
thd->in_lock_tables= 1;
if (reopen_tables(thd, 1, 1))
if (reopen_tables(thd, 1))
{
/* To be safe remove this table from the set of LOCKED TABLES */
unlink_open_table(thd, tables->table, FALSE);
@ -503,14 +503,22 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
thd->in_lock_tables= 0;
}
end:
end_unlock:
pthread_mutex_unlock(&LOCK_open);
end:
if (!result)
{
write_bin_log(thd, TRUE, stmt_query.ptr(), stmt_query.length());
}
pthread_mutex_unlock(&LOCK_open);
/*
If we are under LOCK TABLES we should restore original state of meta-data
locks. Otherwise call to close_thread_tables() will take care about both
TABLE instance created by reopen_name_locked_table() and meta-data lock.
*/
if (thd->locked_tables)
mdl_downgrade_exclusive_locks(&thd->mdl_context);
if (need_start_waiting)
start_waiting_global_read_lock(thd);
@ -1879,11 +1887,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
In the future, only an exclusive table name lock will be enough.
*/
#ifndef DBUG_OFF
uchar key[MAX_DBKEY_LENGTH];
uint key_length= (uint) (strmov(strmov((char*)&key[0], db)+1,
old_table)-(char*)&key[0])+1;
if (!is_table_name_exclusively_locked_by_this_thread(thd, key, key_length))
if (mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, db, old_table))
safe_mutex_assert_owner(&LOCK_open);
#endif

View File

@ -142,6 +142,7 @@ void udf_init()
tables.alias= tables.table_name= (char*) "func";
tables.lock_type = TL_READ;
tables.db= db;
alloc_mdl_locks(&tables, new_thd->mem_root);
if (simple_open_n_lock_tables(new_thd, &tables))
{
@ -485,6 +486,7 @@ int mysql_create_function(THD *thd,udf_func *udf)
bzero((char*) &tables,sizeof(tables));
tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*) "func";
alloc_mdl_locks(&tables, thd->mem_root);
/* Allow creation of functions even if we can't open func table */
if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
goto err;
@ -563,6 +565,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
bzero((char*) &tables,sizeof(tables));
tables.db=(char*) "mysql";
tables.table_name= tables.alias= (char*) "func";
alloc_mdl_locks(&tables, thd->mem_root);
if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
goto err;
table->use_all_columns();

View File

@ -226,7 +226,7 @@ int mysql_update(THD *thd,
break;
if (!need_reopen)
DBUG_RETURN(1);
close_tables_for_reopen(thd, &table_list);
close_tables_for_reopen(thd, &table_list, FALSE);
}
if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
@ -1149,7 +1149,7 @@ reopen_tables:
*/
cleanup_items(thd->free_list);
close_tables_for_reopen(thd, &table_list);
close_tables_for_reopen(thd, &table_list, FALSE);
goto reopen_tables;
}

View File

@ -178,40 +178,17 @@ err:
static bool
fill_defined_view_parts (THD *thd, TABLE_LIST *view)
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
LEX *lex= thd->lex;
bool not_used;
TABLE_LIST decoy;
memcpy (&decoy, view, sizeof (TABLE_LIST));
key_length= create_table_def_key(thd, key, view, 0);
/*
Let's reset decoy.view before calling open_table(): when we start
supporting ALTER VIEW in PS/SP that may save us from a crash.
*/
decoy.view= NULL;
/*
open_table() will return NULL if 'decoy' is idenitifying a view *and*
there is no TABLE object for that view in the table cache. However,
decoy.view will be set to 1.
If there is a TABLE-instance for the oject identified by 'decoy',
open_table() will return that instance no matter if it is a table or
a view.
Thus, there is no need to check for the return value of open_table(),
since the return value itself does not mean anything.
*/
open_table(thd, &decoy, thd->mem_root, &not_used, OPEN_VIEW_NO_PARSE);
if (!decoy.view)
{
/* It's a table. */
my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW");
if (tdc_open_view(thd, &decoy, decoy.alias, key, key_length,
thd->mem_root, OPEN_VIEW_NO_PARSE))
return TRUE;
}
if (!lex->definer)
{
@ -400,6 +377,35 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
DBUG_ASSERT(!lex->proc_list.first && !lex->result &&
!lex->param_list.elements);
/*
We can't allow taking exclusive meta-data locks of unlocked view under
LOCK TABLES since this might lead to deadlock. Since at the moment we
can't really lock view with LOCK TABLES we simply prohibit creation/
alteration of views under LOCK TABLES.
*/
if (thd->locked_tables)
{
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
res= TRUE;
goto err;
}
if ((res= create_view_precheck(thd, tables, view, mode)))
goto err;
lex->link_first_table_back(view, link_to_local);
view->open_table_type= TABLE_LIST::TAKE_EXCLUSIVE_MDL;
if (open_and_lock_tables(thd, lex->query_tables))
{
view= lex->unlink_first_table(&link_to_local);
res= TRUE;
goto err;
}
view= lex->unlink_first_table(&link_to_local);
if (mode != VIEW_CREATE_NEW)
{
if (mode == VIEW_ALTER &&
@ -464,16 +470,6 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
}
#endif
if ((res= create_view_precheck(thd, tables, view, mode)))
goto err;
if (open_and_lock_tables(thd, tables))
{
res= TRUE;
goto err;
}
/*
check that tables are not temporary and this VIEW do not used in query
(it is possible with ALTERing VIEW).
@ -615,11 +611,13 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
#endif
if (wait_if_global_read_lock(thd, 0, 0))
{
res= TRUE;
goto err;
}
pthread_mutex_lock(&LOCK_open);
res= mysql_register_view(thd, view, mode);
@ -1331,7 +1329,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
anyway.
*/
for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
{
tbl->lock_type= table->lock_type;
tbl->mdl_upgradable= table->mdl_upgradable;
}
/*
If the view is mergeable, we might want to
INSERT/UPDATE/DELETE into tables of this view. Preserve the
@ -1579,6 +1580,21 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
bool something_wrong= FALSE;
DBUG_ENTER("mysql_drop_view");
/*
We can't allow dropping of unlocked view under LOCK TABLES since this
might lead to deadlock. But since we can't really lock view with LOCK
TABLES we have to simply prohibit dropping of views.
*/
if (thd->locked_tables)
{
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
DBUG_RETURN(TRUE);
}
if (lock_table_names(thd, views))
DBUG_RETURN(TRUE);
pthread_mutex_lock(&LOCK_open);
for (view= views; view; view= view->next_local)
{

View File

@ -316,6 +316,9 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
share->table_map_id= ~0UL;
share->cached_row_logging_check= -1;
share->used_tables.empty();
share->free_tables.empty();
memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root));
pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
pthread_cond_init(&share->cond, NULL);
@ -382,6 +385,9 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
*/
share->table_map_id= (ulong) thd->query_id;
share->used_tables.empty();
share->free_tables.empty();
DBUG_VOID_RETURN;
}
@ -4832,6 +4838,20 @@ size_t max_row_length(TABLE *table, const uchar *data)
return length;
}
/**
Helper function which allows to allocate metadata lock request
objects for all elements of table list.
*/
void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root)
{
for ( ; table_list ; table_list= table_list->next_global)
table_list->mdl_lock= mdl_alloc_lock(0, table_list->db,
table_list->table_name, root);
}
/*****************************************************************************
** Instansiate templates
*****************************************************************************/

View File

@ -17,6 +17,8 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "sql_plist.h"
/* Structs that defines the TABLE */
class Item; /* Needed by ORDER */
@ -28,6 +30,7 @@ class st_select_lex;
class partition_info;
class COND_EQUAL;
class Security_context;
struct MDL_LOCK;
/*************************************************************************/
@ -288,6 +291,9 @@ typedef enum enum_table_category TABLE_CATEGORY;
TABLE_CATEGORY get_table_category(const LEX_STRING *db,
const LEX_STRING *name);
struct TABLE_share;
/*
This structure is shared between different table objects. There is one
instance of table share per one table in the database.
@ -310,6 +316,13 @@ struct TABLE_SHARE
pthread_cond_t cond; /* To signal that share is ready */
TABLE_SHARE *next, **prev; /* Link to unused shares */
/*
Doubly-linked (back-linked) lists of used and unused TABLE objects
for this share.
*/
I_P_List <TABLE, TABLE_share> used_tables;
I_P_List <TABLE, TABLE_share> free_tables;
/* The following is copied to each TABLE on OPEN */
Field **field;
Field **found_next_number_field;
@ -613,6 +626,19 @@ struct TABLE
handler *file;
TABLE *next, *prev;
private:
/**
Links for the lists of used/unused TABLE objects for this share.
Declared as private to avoid direct manipulation with those objects.
One should use methods of I_P_List template instead.
*/
TABLE *share_next, **share_prev;
friend struct TABLE_share;
friend bool reopen_table(TABLE *table);
public:
/* For the below MERGE related members see top comment in ha_myisammrg.cc */
TABLE *parent; /* Set in MERGE child. Ptr to parent */
TABLE_LIST *child_l; /* Set in MERGE parent. List of children */
@ -814,6 +840,7 @@ struct TABLE
partition_info *part_info; /* Partition related information */
bool no_partitions_used; /* If true, all partitions have been pruned away */
#endif
MDL_LOCK *mdl_lock;
bool fill_item_list(List<Item> *item_list) const;
void reset_item_list(List<Item> *item_list) const;
@ -859,6 +886,25 @@ struct TABLE
bool is_children_attached(void);
};
/**
Helper class which specifies which members of TABLE are used for
participation in the list of used/unused TABLE objects for the share.
*/
struct TABLE_share
{
static inline TABLE **next_ptr(TABLE *l)
{
return &l->share_next;
}
static inline TABLE ***prev_ptr(TABLE *l)
{
return &l->share_prev;
}
};
enum enum_schema_table_state
{
NOT_PROCESSED= 0,
@ -1326,11 +1372,18 @@ struct TABLE_LIST
*/
bool prelocking_placeholder;
/*
This TABLE_LIST object corresponds to the table to be created
so it is possible that it does not exist (used in CREATE TABLE
... SELECT implementation).
This TABLE_LIST object corresponds to the table/view which requires
special handling/meta-data locking. For example this is a target
table in CREATE TABLE ... SELECT so it is possible that it does not
exist and we should take exclusive meta-data lock on it in this
case.
*/
bool create;
enum {NORMAL_OPEN= 0, OPEN_OR_CREATE, TAKE_EXCLUSIVE_MDL} open_table_type;
/**
Indicates that for this table/view we need to take shared metadata
lock which should be upgradable to exclusive metadata lock.
*/
bool mdl_upgradable;
bool internal_tmp_table;
/** TRUE if an alias for this table was specified in the SQL. */
bool is_alias;
@ -1379,6 +1432,9 @@ struct TABLE_LIST
bool has_table_lookup_value;
uint table_open_method;
enum enum_schema_table_state schema_table_state;
MDL_LOCK *mdl_lock;
void calc_md5(char *buffer);
void set_underlying_merge();
int view_check_option(THD *thd, bool ignore_failure);
@ -1386,8 +1442,7 @@ struct TABLE_LIST
void cleanup_items();
bool placeholder()
{
return derived || view || schema_table || (create && !table->db_stat) ||
!table;
return derived || view || schema_table || !table;
}
void print(THD *thd, String *str, enum_query_type query_type);
bool check_single_table(TABLE_LIST **table, table_map map,
@ -1746,4 +1801,17 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set,
size_t max_row_length(TABLE *table, const uchar *data);
void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root);
/**
Helper function which allows to mark all elements in table list
as requiring upgradable metadata locks.
*/
inline void set_all_mdl_upgradable(TABLE_LIST *tables)
{
for (; tables; tables= tables->next_global)
tables->mdl_upgradable= TRUE;
}
#endif /* TABLE_INCLUDED */

View File

@ -270,6 +270,12 @@ static int myisammrg_parent_open_callback(void *callback_param,
/* Set alias. */
child_l->alias= child_l->table_name;
/*
FIXME: Actually we should use some other mem-root here.
To be fixed once Ingo pushes his patch for WL4144.
*/
alloc_mdl_locks(child_l, &parent->mem_root);
/* Initialize table map to 'undefined'. */
child_l->init_child_def_version();