A 5.5 version of the fix for Bug #54360 "Deadlock DROP/ALTER/CREATE
DATABASE with open HANDLER" Remove LOCK_create_db, database name locks, and use metadata locks instead. This exposes CREATE/DROP/ALTER DATABASE statements to the graph-based deadlock detector in MDL, and paves the way for a safe, deadlock-free implementation of RENAME DATABASE. Database DDL statements will now take exclusive metadata locks on the database name, while table/view/routine DDL statements take intention exclusive locks on the database name. This prevents race conditions between database DDL and table/view/routine DDL. (e.g. DROP DATABASE with concurrent CREATE/ALTER/DROP TABLE) By adding database name locks, this patch implements WL#4450 "DDL locking: CREATE/DROP DATABASE must use database locks" and WL#4985 "DDL locking: namespace/hierarchical locks". The patch also changes code to use init_one_table() where appropriate. The new lock_table_names() function requires TABLE_LIST::db_length to be set correctly, and this is taken care of by init_one_table(). This patch also adds a simple template to help work with the mysys HASH data structure. Most of the patch was written by Konstantin Osipov.
This commit is contained in:
parent
34e409fafa
commit
41a3dfe490
@ -2527,3 +2527,240 @@ SET DEBUG_SYNC= "now SIGNAL completed";
|
||||
Field Type Collation Null Key Default Extra Privileges Comment
|
||||
a char(255) latin1_swedish_ci YES NULL #
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Tests for schema-scope locks
|
||||
#
|
||||
DROP DATABASE IF EXISTS db1;
|
||||
DROP DATABASE IF EXISTS db2;
|
||||
# Test 1:
|
||||
# CREATE DATABASE blocks database DDL on the same database, but
|
||||
# not database DDL on different databases. Tests X vs X lock.
|
||||
#
|
||||
# Connection default
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
CREATE DATABASE db1;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
CREATE DATABASE db1;
|
||||
# Connection con3
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: CREATE DATABASE db1
|
||||
# Connection con2
|
||||
# Reaping: CREATE DATABASE db1
|
||||
ERROR HY000: Can't create database 'db1'; database exists
|
||||
# Test 2:
|
||||
# ALTER DATABASE blocks database DDL on the same database, but
|
||||
# not database DDL on different databases. Tests X vs X lock.
|
||||
#
|
||||
# Connection default
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
ALTER DATABASE db1 DEFAULT CHARACTER SET utf8;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
ALTER DATABASE db1 DEFAULT CHARACTER SET utf8;
|
||||
# Connection con3
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
# Connection con2
|
||||
# Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
# Connection default
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
ALTER DATABASE db1 DEFAULT CHARACTER SET utf8;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection con3
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
# Connection con2
|
||||
# Reaping: DROP DATABASE db1
|
||||
CREATE DATABASE db1;
|
||||
# Test 3:
|
||||
# Two ALTER..UPGRADE of the same database are mutually exclusive, but
|
||||
# two ALTER..UPGRADE of different databases are not. Tests X vs X lock.
|
||||
#
|
||||
# Connection default
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME;
|
||||
# Connection con3
|
||||
ALTER DATABASE `#mysql50#a-b-c-d` UPGRADE DATA DIRECTORY NAME;
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: ALTER DATABASE '#mysql50#a-b-c' UPGRADE DATA DIRECTORY NAME
|
||||
# Connection con2
|
||||
# Reaping: ALTER DATABASE '#mysql50#a-b-c' UPGRADE DATA DIRECTORY NAME
|
||||
ERROR 42000: Unknown database '#mysql50#a-b-c'
|
||||
DROP DATABASE `a-b-c`;
|
||||
DROP DATABASE `a-b-c-d`;
|
||||
# Test 4:
|
||||
# DROP DATABASE blocks database DDL on the same database, but
|
||||
# not database DDL on different databases. Tests X vs X lock.
|
||||
#
|
||||
# Connection default
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection con3
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: DROP DATABASE db1
|
||||
# Connection con2
|
||||
# Reaping: DROP DATABASE db1
|
||||
ERROR HY000: Can't drop database 'db1'; database doesn't exist
|
||||
# Connection default
|
||||
CREATE DATABASE db1;
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
ALTER DATABASE db1 DEFAULT CHARACTER SET utf8;
|
||||
# Connection con3
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: DROP DATABASE db1
|
||||
# Connection con2
|
||||
# Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
ERROR HY000: Can't create/write to file './db1/db.opt' (Errcode: 2)
|
||||
# Test 5:
|
||||
# Locked database name prevents CREATE of tables in that database.
|
||||
# Tests X vs IX lock.
|
||||
#
|
||||
# Connection default
|
||||
CREATE DATABASE db1;
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
CREATE TABLE db1.t1 (a INT);
|
||||
# Connection con3
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: DROP DATABASE db1
|
||||
# Connection con2
|
||||
# Reaping: CREATE TABLE db1.t1 (a INT)
|
||||
ERROR 42000: Unknown database 'db1'
|
||||
# Test 6:
|
||||
# Locked database name prevents RENAME of tables to/from that database.
|
||||
# Tests X vs IX lock.
|
||||
#
|
||||
# Connection default
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1 (a INT);
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
RENAME TABLE db1.t1 TO test.t1;
|
||||
# Connection con3
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: DROP DATABASE db1
|
||||
# Connection con2
|
||||
# Reaping: RENAME TABLE db1.t1 TO test.t1
|
||||
ERROR HY000: Can't find file: './db1/t1.frm' (errno: 2)
|
||||
# Connection default
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE test.t2 (a INT);
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
RENAME TABLE test.t2 TO db1.t2;
|
||||
# Connection con3
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: DROP DATABASE db1
|
||||
# Connection con2
|
||||
# Reaping: RENAME TABLE test.t2 TO db1.t2
|
||||
ERROR HY000: Error on rename of './test/t2.MYI' to './db1/t2.MYI' (Errcode: 2)
|
||||
DROP TABLE test.t2;
|
||||
# Test 7:
|
||||
# Locked database name prevents DROP of tables in that database.
|
||||
# Tests X vs IX lock.
|
||||
#
|
||||
# Connection default
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1 (a INT);
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection con2
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
# Sending:
|
||||
DROP TABLE db1.t1;
|
||||
# Connection con3
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
# Connection default
|
||||
# Reaping: DROP DATABASE db1
|
||||
# Connection con2
|
||||
# Reaping: DROP TABLE db1.t1
|
||||
ERROR 42S02: Unknown table 't1'
|
||||
# Connection default
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
#
|
||||
# End of tests for schema-scope locks
|
||||
#
|
||||
#
|
||||
# Tests of granted global S lock (FLUSH TABLE WITH READ LOCK)
|
||||
#
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1(a INT);
|
||||
# Connection default
|
||||
FLUSH TABLE WITH READ LOCK;
|
||||
# Connection con2
|
||||
CREATE TABLE db1.t2(a INT);
|
||||
# Connection default
|
||||
UNLOCK TABLES;
|
||||
# Connection con2
|
||||
# Reaping CREATE TABLE db1.t2(a INT)
|
||||
# Connection default
|
||||
FLUSH TABLE WITH READ LOCK;
|
||||
# Connection con2
|
||||
ALTER DATABASE db1 DEFAULT CHARACTER SET utf8;
|
||||
# Connection default
|
||||
UNLOCK TABLES;
|
||||
# Connection con2
|
||||
# Reaping ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
# Connection default
|
||||
FLUSH TABLE WITH READ LOCK;
|
||||
# Connection con2
|
||||
FLUSH TABLE WITH READ LOCK;
|
||||
UNLOCK TABLES;
|
||||
# Connection default
|
||||
UNLOCK TABLES;
|
||||
DROP DATABASE db1;
|
||||
|
@ -47,7 +47,7 @@ ENGINE = MYISAM
|
||||
PARTITION p1 VALUES LESS THAN (20),
|
||||
PARTITION p2 VALUES LESS THAN (100),
|
||||
PARTITION p3 VALUES LESS THAN MAXVALUE ) */;
|
||||
SET DEBUG_SYNC= 'open_tables_acquire_upgradable_mdl SIGNAL removing_partitions WAIT_FOR waiting_for_alter';
|
||||
SET DEBUG_SYNC= 'alter_table_before_open_tables SIGNAL removing_partitions WAIT_FOR waiting_for_alter';
|
||||
SET DEBUG_SYNC= 'alter_table_before_rename_result_table WAIT_FOR delete_done';
|
||||
ALTER TABLE t2 REMOVE PARTITIONING;
|
||||
# Con default
|
||||
|
@ -16,19 +16,21 @@ drop schema foo;
|
||||
# Bug #48940 MDL deadlocks against mysql_rm_db
|
||||
#
|
||||
DROP SCHEMA IF EXISTS schema1;
|
||||
DROP SCHEMA IF EXISTS schema2;
|
||||
# Connection default
|
||||
CREATE SCHEMA schema1;
|
||||
CREATE SCHEMA schema2;
|
||||
CREATE TABLE schema1.t1 (a INT);
|
||||
SET autocommit= FALSE;
|
||||
INSERT INTO schema1.t1 VALUES (1);
|
||||
# Connection 2
|
||||
DROP SCHEMA schema1;
|
||||
# Connection default
|
||||
ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8;
|
||||
Got one of the listed errors
|
||||
ALTER SCHEMA schema2 DEFAULT CHARACTER SET utf8;
|
||||
SET autocommit= TRUE;
|
||||
# Connection 2
|
||||
# Connection default
|
||||
DROP SCHEMA schema2;
|
||||
#
|
||||
# Bug #49988 MDL deadlocks with mysql_create_db, reload_acl_and_cache
|
||||
#
|
||||
@ -48,3 +50,47 @@ ERROR HY000: Can't execute the given command because you have active locked tabl
|
||||
UNLOCK TABLES;
|
||||
# Connection con2
|
||||
# Connection default
|
||||
#
|
||||
# Bug#54360 Deadlock DROP/ALTER/CREATE DATABASE with open HANDLER
|
||||
#
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1 (a INT);
|
||||
INSERT INTO db1.t1 VALUES (1), (2);
|
||||
# Connection con1
|
||||
HANDLER db1.t1 OPEN;
|
||||
# Connection default
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection con2
|
||||
# Connection con1
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
# Connection default
|
||||
# Reaping: DROP DATABASE db1
|
||||
#
|
||||
# Tests for increased CREATE/ALTER/DROP DATABASE concurrency with
|
||||
# database name locks.
|
||||
#
|
||||
DROP DATABASE IF EXISTS db1;
|
||||
DROP DATABASE IF EXISTS db2;
|
||||
# Connection default
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1 (id INT);
|
||||
START TRANSACTION;
|
||||
INSERT INTO db1.t1 VALUES (1);
|
||||
# Connection 2
|
||||
# DROP DATABASE should block due to the active transaction
|
||||
# Sending:
|
||||
DROP DATABASE db1;
|
||||
# Connection 3
|
||||
# But it should still be possible to CREATE/ALTER/DROP other databases.
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
# Connection default
|
||||
# End the transaction so DROP DATABASE db1 can continue
|
||||
COMMIT;
|
||||
# Connection 2
|
||||
# Reaping: DROP DATABASE db1
|
||||
# Connection default;
|
||||
|
@ -40,18 +40,10 @@ where name like "wait/synch/cond/mysys/THR_COND_threads";
|
||||
count(name)
|
||||
1
|
||||
select count(name) from MUTEX_INSTANCES
|
||||
where name like "wait/synch/mutex/sql/LOCK_mysql_create_db";
|
||||
count(name)
|
||||
1
|
||||
select count(name) from MUTEX_INSTANCES
|
||||
where name like "wait/synch/mutex/sql/LOCK_open";
|
||||
count(name)
|
||||
1
|
||||
select count(name) from MUTEX_INSTANCES
|
||||
where name like "wait/synch/mutex/sql/LOCK_lock_db";
|
||||
count(name)
|
||||
1
|
||||
select count(name) from MUTEX_INSTANCES
|
||||
where name like "wait/synch/mutex/sql/LOCK_thread_count";
|
||||
count(name)
|
||||
1
|
||||
|
@ -68,15 +68,9 @@ select count(name) from COND_INSTANCES
|
||||
|
||||
# Verify that these global mutexes have been properly initilized in sql
|
||||
|
||||
select count(name) from MUTEX_INSTANCES
|
||||
where name like "wait/synch/mutex/sql/LOCK_mysql_create_db";
|
||||
|
||||
select count(name) from MUTEX_INSTANCES
|
||||
where name like "wait/synch/mutex/sql/LOCK_open";
|
||||
|
||||
select count(name) from MUTEX_INSTANCES
|
||||
where name like "wait/synch/mutex/sql/LOCK_lock_db";
|
||||
|
||||
select count(name) from MUTEX_INSTANCES
|
||||
where name like "wait/synch/mutex/sql/LOCK_thread_count";
|
||||
|
||||
|
@ -3705,6 +3705,481 @@ DROP TABLE t1;
|
||||
disconnect con1;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Tests for schema-scope locks
|
||||
--echo #
|
||||
|
||||
--disable_warnings
|
||||
DROP DATABASE IF EXISTS db1;
|
||||
DROP DATABASE IF EXISTS db2;
|
||||
--enable_warnings
|
||||
|
||||
connect (con2, localhost, root);
|
||||
connect (con3, localhost, root);
|
||||
|
||||
--echo # Test 1:
|
||||
--echo # CREATE DATABASE blocks database DDL on the same database, but
|
||||
--echo # not database DDL on different databases. Tests X vs X lock.
|
||||
--echo #
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send CREATE DATABASE db1
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should block.
|
||||
--send CREATE DATABASE db1
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table' AND info='CREATE DATABASE db1';
|
||||
--source include/wait_condition.inc
|
||||
# This should not block.
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: CREATE DATABASE db1
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: CREATE DATABASE db1
|
||||
--error ER_DB_CREATE_EXISTS
|
||||
--reap
|
||||
|
||||
--echo # Test 2:
|
||||
--echo # ALTER DATABASE blocks database DDL on the same database, but
|
||||
--echo # not database DDL on different databases. Tests X vs X lock.
|
||||
--echo #
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should block.
|
||||
--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table'
|
||||
AND info='ALTER DATABASE db1 DEFAULT CHARACTER SET utf8';
|
||||
--source include/wait_condition.inc
|
||||
# This should not block.
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
--reap
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should also block.
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table' AND info='DROP DATABASE db1';
|
||||
--source include/wait_condition.inc
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--reap
|
||||
# Recreate the database
|
||||
CREATE DATABASE db1;
|
||||
|
||||
--echo # Test 3:
|
||||
--echo # Two ALTER..UPGRADE of the same database are mutually exclusive, but
|
||||
--echo # two ALTER..UPGRADE of different databases are not. Tests X vs X lock.
|
||||
--echo #
|
||||
|
||||
let $MYSQLD_DATADIR= `select @@datadir`;
|
||||
# Manually make a 5.0 database from the template
|
||||
--mkdir $MYSQLD_DATADIR/a-b-c
|
||||
--copy_file $MYSQLD_DATADIR/db1/db.opt $MYSQLD_DATADIR/a-b-c/db.opt
|
||||
--mkdir $MYSQLD_DATADIR/a-b-c-d
|
||||
--copy_file $MYSQLD_DATADIR/db1/db.opt $MYSQLD_DATADIR/a-b-c-d/db.opt
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should block.
|
||||
--send ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table'
|
||||
AND info='ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME';
|
||||
--source include/wait_condition.inc
|
||||
# This should not block.
|
||||
ALTER DATABASE `#mysql50#a-b-c-d` UPGRADE DATA DIRECTORY NAME;
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: ALTER DATABASE '#mysql50#a-b-c' UPGRADE DATA DIRECTORY NAME
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: ALTER DATABASE '#mysql50#a-b-c' UPGRADE DATA DIRECTORY NAME
|
||||
--error ER_BAD_DB_ERROR
|
||||
--reap
|
||||
DROP DATABASE `a-b-c`;
|
||||
DROP DATABASE `a-b-c-d`;
|
||||
|
||||
--echo # Test 4:
|
||||
--echo # DROP DATABASE blocks database DDL on the same database, but
|
||||
--echo # not database DDL on different databases. Tests X vs X lock.
|
||||
--echo #
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should block.
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table' AND info='DROP DATABASE db1';
|
||||
--source include/wait_condition.inc
|
||||
# This should not block.
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--error ER_DB_DROP_EXISTS
|
||||
--reap
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
CREATE DATABASE db1;
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should also block.
|
||||
--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table'
|
||||
AND info='ALTER DATABASE db1 DEFAULT CHARACTER SET utf8';
|
||||
--source include/wait_condition.inc
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
--error 1 # Wrong error pending followup patch for bug#54360
|
||||
--reap
|
||||
|
||||
|
||||
--echo # Test 5:
|
||||
--echo # Locked database name prevents CREATE of tables in that database.
|
||||
--echo # Tests X vs IX lock.
|
||||
--echo #
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
CREATE DATABASE db1;
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should block.
|
||||
--send CREATE TABLE db1.t1 (a INT)
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table' AND info='CREATE TABLE db1.t1 (a INT)';
|
||||
--source include/wait_condition.inc
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: CREATE TABLE db1.t1 (a INT)
|
||||
--error ER_BAD_DB_ERROR
|
||||
--reap
|
||||
|
||||
--echo # Test 6:
|
||||
--echo # Locked database name prevents RENAME of tables to/from that database.
|
||||
--echo # Tests X vs IX lock.
|
||||
--echo #
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1 (a INT);
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should block.
|
||||
--send RENAME TABLE db1.t1 TO test.t1
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table' AND info='RENAME TABLE db1.t1 TO test.t1';
|
||||
--source include/wait_condition.inc
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: RENAME TABLE db1.t1 TO test.t1
|
||||
--error ER_FILE_NOT_FOUND
|
||||
--reap
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE test.t2 (a INT);
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should block.
|
||||
--send RENAME TABLE test.t2 TO db1.t2
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table' AND info='RENAME TABLE test.t2 TO db1.t2';
|
||||
--source include/wait_condition.inc
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: RENAME TABLE test.t2 TO db1.t2
|
||||
--error 7 # Wrong error pending followup patch for bug#54360
|
||||
--reap
|
||||
DROP TABLE test.t2;
|
||||
|
||||
|
||||
--echo # Test 7:
|
||||
--echo # Locked database name prevents DROP of tables in that database.
|
||||
--echo # Tests X vs IX lock.
|
||||
--echo #
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1 (a INT);
|
||||
SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked';
|
||||
--echo # Sending:
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR locked';
|
||||
--echo # Sending:
|
||||
# This should block.
|
||||
--send DROP TABLE db1.t1
|
||||
|
||||
--echo # Connection con3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table' AND info='DROP TABLE db1.t1';
|
||||
--source include/wait_condition.inc
|
||||
SET DEBUG_SYNC= 'now SIGNAL blocked';
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--reap
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping: DROP TABLE db1.t1
|
||||
--error ER_BAD_TABLE_ERROR
|
||||
--reap
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
disconnect con2;
|
||||
disconnect con3;
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
|
||||
--echo #
|
||||
--echo # End of tests for schema-scope locks
|
||||
--echo #
|
||||
|
||||
--echo #
|
||||
--echo # Tests of granted global S lock (FLUSH TABLE WITH READ LOCK)
|
||||
--echo #
|
||||
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1(a INT);
|
||||
connect(con2, localhost, root);
|
||||
connect(con3, localhost, root);
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
FLUSH TABLE WITH READ LOCK;
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
# IX global lock should block
|
||||
--send CREATE TABLE db1.t2(a INT)
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for release of readlock'
|
||||
AND info='CREATE TABLE db1.t2(a INT)';
|
||||
--source include/wait_condition.inc
|
||||
UNLOCK TABLES;
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping CREATE TABLE db1.t2(a INT)
|
||||
--reap
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
FLUSH TABLE WITH READ LOCK;
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
# X global lock should block
|
||||
--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for release of readlock'
|
||||
AND info='ALTER DATABASE db1 DEFAULT CHARACTER SET utf8';
|
||||
--source include/wait_condition.inc
|
||||
UNLOCK TABLES;
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
--echo # Reaping ALTER DATABASE db1 DEFAULT CHARACTER SET utf8
|
||||
--reap
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
FLUSH TABLE WITH READ LOCK;
|
||||
|
||||
--echo # Connection con2
|
||||
connection con2;
|
||||
# S global lock should not block
|
||||
FLUSH TABLE WITH READ LOCK;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
UNLOCK TABLES;
|
||||
DROP DATABASE db1;
|
||||
disconnect con2;
|
||||
disconnect con3;
|
||||
|
||||
|
||||
# Check that all connections opened by test cases in this file are really
|
||||
# gone so execution of other tests won't be affected by their presence.
|
||||
--source include/wait_until_count_sessions.inc
|
||||
|
@ -65,7 +65,7 @@ ENGINE = MYISAM
|
||||
PARTITION p1 VALUES LESS THAN (20),
|
||||
PARTITION p2 VALUES LESS THAN (100),
|
||||
PARTITION p3 VALUES LESS THAN MAXVALUE ) */;
|
||||
SET DEBUG_SYNC= 'open_tables_acquire_upgradable_mdl SIGNAL removing_partitions WAIT_FOR waiting_for_alter';
|
||||
SET DEBUG_SYNC= 'alter_table_before_open_tables SIGNAL removing_partitions WAIT_FOR waiting_for_alter';
|
||||
SET DEBUG_SYNC= 'alter_table_before_rename_result_table WAIT_FOR delete_done';
|
||||
--send ALTER TABLE t2 REMOVE PARTITIONING
|
||||
connection default;
|
||||
|
@ -23,6 +23,7 @@ drop schema foo;
|
||||
|
||||
--disable_warnings
|
||||
DROP SCHEMA IF EXISTS schema1;
|
||||
DROP SCHEMA IF EXISTS schema2;
|
||||
--enable_warnings
|
||||
|
||||
connect(con2, localhost, root);
|
||||
@ -31,6 +32,7 @@ connect(con2, localhost, root);
|
||||
connection default;
|
||||
|
||||
CREATE SCHEMA schema1;
|
||||
CREATE SCHEMA schema2;
|
||||
CREATE TABLE schema1.t1 (a INT);
|
||||
|
||||
SET autocommit= FALSE;
|
||||
@ -46,9 +48,7 @@ let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist
|
||||
WHERE state= 'Waiting for table'
|
||||
AND info='DROP SCHEMA schema1';
|
||||
--source include/wait_condition.inc
|
||||
# Listing the error twice to prevent result diffences based on filename
|
||||
--error 1,1
|
||||
ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8;
|
||||
ALTER SCHEMA schema2 DEFAULT CHARACTER SET utf8;
|
||||
SET autocommit= TRUE;
|
||||
|
||||
--echo # Connection 2
|
||||
@ -57,6 +57,7 @@ connection con2;
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
DROP SCHEMA schema2;
|
||||
disconnect con2;
|
||||
|
||||
|
||||
@ -102,6 +103,97 @@ connection con2;
|
||||
connection default;
|
||||
disconnect con2;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Bug#54360 Deadlock DROP/ALTER/CREATE DATABASE with open HANDLER
|
||||
--echo #
|
||||
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1 (a INT);
|
||||
INSERT INTO db1.t1 VALUES (1), (2);
|
||||
|
||||
--echo # Connection con1
|
||||
connect (con1, localhost, root);
|
||||
HANDLER db1.t1 OPEN;
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Sending:
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection con2
|
||||
connect (con2, localhost, root);
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table' AND info='DROP DATABASE db1';
|
||||
--source include/wait_condition.inc
|
||||
|
||||
--echo # Connection con1
|
||||
connection con1;
|
||||
# All these statements before resulted in deadlock.
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--reap
|
||||
disconnect con1;
|
||||
disconnect con2;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Tests for increased CREATE/ALTER/DROP DATABASE concurrency with
|
||||
--echo # database name locks.
|
||||
--echo #
|
||||
|
||||
--disable_warnings
|
||||
DROP DATABASE IF EXISTS db1;
|
||||
DROP DATABASE IF EXISTS db2;
|
||||
--enable_warnings
|
||||
|
||||
connect (con2, localhost, root);
|
||||
connect (con3, localhost, root);
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
CREATE DATABASE db1;
|
||||
CREATE TABLE db1.t1 (id INT);
|
||||
START TRANSACTION;
|
||||
INSERT INTO db1.t1 VALUES (1);
|
||||
|
||||
--echo # Connection 2
|
||||
connection con2;
|
||||
--echo # DROP DATABASE should block due to the active transaction
|
||||
--echo # Sending:
|
||||
--send DROP DATABASE db1
|
||||
|
||||
--echo # Connection 3
|
||||
connection con3;
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table' and info='DROP DATABASE db1';
|
||||
--source include/wait_condition.inc
|
||||
--echo # But it should still be possible to CREATE/ALTER/DROP other databases.
|
||||
CREATE DATABASE db2;
|
||||
ALTER DATABASE db2 DEFAULT CHARACTER SET utf8;
|
||||
DROP DATABASE db2;
|
||||
|
||||
--echo # Connection default
|
||||
connection default;
|
||||
--echo # End the transaction so DROP DATABASE db1 can continue
|
||||
COMMIT;
|
||||
|
||||
--echo # Connection 2
|
||||
connection con2;
|
||||
--echo # Reaping: DROP DATABASE db1
|
||||
--reap
|
||||
|
||||
--echo # Connection default;
|
||||
connection default;
|
||||
disconnect con2;
|
||||
disconnect con3;
|
||||
|
||||
|
||||
# Check that all connections opened by test cases in this file are really
|
||||
# gone so execution of other tests won't be affected by their presence.
|
||||
--source include/wait_until_count_sessions.inc
|
||||
|
65
sql/lock.cc
65
sql/lock.cc
@ -747,62 +747,48 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
Lock table based on the name.
|
||||
This is used when we need total access to a closed, not open table
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
Obtain exclusive metadata locks on the list of tables.
|
||||
Obtain an exclusive metadata lock on a schema name.
|
||||
|
||||
@param thd Thread handle
|
||||
@param table_list List of tables to lock
|
||||
@param thd Thread handle.
|
||||
@param db The database name.
|
||||
|
||||
@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_context::acquire_locks().
|
||||
@note Initialization of MDL_request members of TABLE_LIST elements
|
||||
is a responsibility of the caller.
|
||||
This function cannot be called while holding LOCK_open mutex.
|
||||
To avoid deadlocks, we do not try to obtain exclusive metadata
|
||||
locks in LOCK TABLES mode, since in this mode there may be
|
||||
other metadata locks already taken by the current connection,
|
||||
and we must not wait for MDL locks while holding locks.
|
||||
|
||||
@retval FALSE Success.
|
||||
@retval TRUE Failure (OOM or thread was killed).
|
||||
@retval TRUE Failure: we're in LOCK TABLES mode, or out of memory,
|
||||
or this connection was killed.
|
||||
*/
|
||||
|
||||
bool lock_table_names(THD *thd, TABLE_LIST *table_list)
|
||||
bool lock_schema_name(THD *thd, const char *db)
|
||||
{
|
||||
MDL_request_list mdl_requests;
|
||||
MDL_request global_request;
|
||||
TABLE_LIST *lock_table;
|
||||
MDL_request mdl_request;
|
||||
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
||||
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
|
||||
mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE);
|
||||
|
||||
for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
|
||||
mdl_requests.push_front(&lock_table->mdl_request);
|
||||
|
||||
mdl_requests.push_front(&mdl_request);
|
||||
mdl_requests.push_front(&global_request);
|
||||
|
||||
if (thd->mdl_context.acquire_locks(&mdl_requests,
|
||||
thd->variables.lock_wait_timeout))
|
||||
return 1;
|
||||
return TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Release all metadata locks previously obtained by lock_table_names().
|
||||
|
||||
@param thd Thread handle.
|
||||
|
||||
@note Cannot be called while holding LOCK_open mutex.
|
||||
*/
|
||||
|
||||
void unlock_table_names(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("unlock_table_names");
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
DBUG_VOID_RETURN;
|
||||
DEBUG_SYNC(thd, "after_wait_locked_schema_name");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
@ -837,6 +823,7 @@ bool lock_routine_name(THD *thd, bool is_function,
|
||||
MDL_key::PROCEDURE);
|
||||
MDL_request_list mdl_requests;
|
||||
MDL_request global_request;
|
||||
MDL_request schema_request;
|
||||
MDL_request mdl_request;
|
||||
|
||||
if (thd->locked_tables_mode)
|
||||
@ -850,9 +837,11 @@ bool lock_routine_name(THD *thd, bool is_function,
|
||||
DEBUG_SYNC(thd, "before_wait_locked_pname");
|
||||
|
||||
global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
|
||||
schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE);
|
||||
mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE);
|
||||
|
||||
mdl_requests.push_front(&mdl_request);
|
||||
mdl_requests.push_front(&schema_request);
|
||||
mdl_requests.push_front(&global_request);
|
||||
|
||||
if (thd->mdl_context.acquire_locks(&mdl_requests,
|
||||
|
@ -62,8 +62,7 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
|
||||
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
|
||||
void broadcast_refresh(void);
|
||||
/* Lock based on name */
|
||||
bool lock_table_names(THD *thd, TABLE_LIST *table_list);
|
||||
void unlock_table_names(THD *thd);
|
||||
bool lock_schema_name(THD *thd, const char *db);
|
||||
/* Lock based on stored routine name */
|
||||
bool lock_routine_name(THD *thd, bool is_function, const char *db,
|
||||
const char *name);
|
||||
|
72
sql/mdl.cc
72
sql/mdl.cc
@ -406,14 +406,15 @@ public:
|
||||
|
||||
|
||||
/**
|
||||
An implementation of the global metadata lock. The only locking modes
|
||||
which are supported at the moment are SHARED and INTENTION EXCLUSIVE.
|
||||
An implementation of the scoped metadata lock. The only locking modes
|
||||
which are supported at the moment are SHARED and INTENTION EXCLUSIVE
|
||||
and EXCLUSIVE
|
||||
*/
|
||||
|
||||
class MDL_global_lock : public MDL_lock
|
||||
class MDL_scoped_lock : public MDL_lock
|
||||
{
|
||||
public:
|
||||
MDL_global_lock(const MDL_key *key_arg)
|
||||
MDL_scoped_lock(const MDL_key *key_arg)
|
||||
: MDL_lock(key_arg)
|
||||
{ }
|
||||
|
||||
@ -857,7 +858,8 @@ inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key)
|
||||
switch (mdl_key->mdl_namespace())
|
||||
{
|
||||
case MDL_key::GLOBAL:
|
||||
return new MDL_global_lock(mdl_key);
|
||||
case MDL_key::SCHEMA:
|
||||
return new MDL_scoped_lock(mdl_key);
|
||||
case MDL_key::TABLE:
|
||||
return new MDL_table_lock(mdl_key);
|
||||
default:
|
||||
@ -1217,61 +1219,66 @@ void MDL_lock::reschedule_waiters()
|
||||
|
||||
|
||||
/**
|
||||
Compatibility (or rather "incompatibility") matrices for global metadata
|
||||
Compatibility (or rather "incompatibility") matrices for scoped metadata
|
||||
lock. Arrays of bitmaps which elements specify which granted/waiting locks
|
||||
are incompatible with type of lock being requested.
|
||||
|
||||
Here is how types of individual locks are translated to type of global lock:
|
||||
Here is how types of individual locks are translated to type of scoped lock:
|
||||
|
||||
----------------+-------------+
|
||||
Type of request | Correspond. |
|
||||
for indiv. lock | global lock |
|
||||
for indiv. lock | scoped lock |
|
||||
----------------+-------------+
|
||||
S, SH, SR, SW | IS |
|
||||
SNW, SNRW, X | IX |
|
||||
SNW, SNRW -> X | IX (*) |
|
||||
|
||||
The first array specifies if particular type of request can be satisfied
|
||||
if there is granted global lock of certain type.
|
||||
if there is granted scoped lock of certain type.
|
||||
|
||||
| Type of active |
|
||||
Request | global lock |
|
||||
type | IS(**) IX S |
|
||||
---------+----------------+
|
||||
IS | + + + |
|
||||
IX | + + - |
|
||||
S | + - + |
|
||||
Request | scoped lock |
|
||||
type | IS(**) IX S X |
|
||||
---------+------------------+
|
||||
IS | + + + + |
|
||||
IX | + + - - |
|
||||
S | + - + - |
|
||||
X | + - - - |
|
||||
|
||||
The second array specifies if particular type of request can be satisfied
|
||||
if there is already waiting request for the global lock of certain type.
|
||||
if there is already waiting request for the scoped lock of certain type.
|
||||
I.e. it specifies what is the priority of different lock types.
|
||||
|
||||
| Pending |
|
||||
Request | global lock |
|
||||
type | IS(**) IX S |
|
||||
---------+--------------+
|
||||
IS | + + + |
|
||||
IX | + + - |
|
||||
S | + + + |
|
||||
Request | scoped lock |
|
||||
type | IS(**) IX S X |
|
||||
---------+-----------------+
|
||||
IS | + + + + |
|
||||
IX | + + - - |
|
||||
S | + + + - |
|
||||
X | + + + + |
|
||||
|
||||
Here: "+" -- means that request can be satisfied
|
||||
"-" -- means that request can't be satisfied and should wait
|
||||
|
||||
(*) Since for upgradable locks we always take intention exclusive global
|
||||
(*) Since for upgradable locks we always take intention exclusive scoped
|
||||
lock at the same time when obtaining the shared lock, there is no
|
||||
need to obtain such lock during the upgrade itself.
|
||||
(**) Since intention shared global locks are compatible with all other
|
||||
(**) Since intention shared scoped locks are compatible with all other
|
||||
type of locks we don't even have any accounting for them.
|
||||
*/
|
||||
|
||||
const MDL_lock::bitmap_t MDL_global_lock::m_granted_incompatible[MDL_TYPE_END] =
|
||||
const MDL_lock::bitmap_t MDL_scoped_lock::m_granted_incompatible[MDL_TYPE_END] =
|
||||
{
|
||||
MDL_BIT(MDL_SHARED), MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0, 0
|
||||
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
|
||||
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0,
|
||||
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED) | MDL_BIT(MDL_INTENTION_EXCLUSIVE)
|
||||
};
|
||||
|
||||
const MDL_lock::bitmap_t MDL_global_lock::m_waiting_incompatible[MDL_TYPE_END] =
|
||||
const MDL_lock::bitmap_t MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END] =
|
||||
{
|
||||
MDL_BIT(MDL_SHARED), 0, 0, 0, 0, 0, 0, 0
|
||||
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
|
||||
MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
|
||||
@ -1912,7 +1919,7 @@ extern "C" int mdl_request_ptr_cmp(const void* ptr1, const void* ptr2)
|
||||
@note The list of requests should not contain non-exclusive lock requests.
|
||||
There should not be any acquired locks in the context.
|
||||
|
||||
@note Assumes that one already owns global intention exclusive lock.
|
||||
@note Assumes that one already owns scoped intention exclusive lock.
|
||||
|
||||
@retval FALSE Success
|
||||
@retval TRUE Failure
|
||||
@ -1929,13 +1936,6 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests,
|
||||
if (req_count == 0)
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
To reduce deadlocks, the server acquires all exclusive
|
||||
locks at once. For shared locks, try_acquire_lock() is
|
||||
used instead.
|
||||
*/
|
||||
DBUG_ASSERT(m_tickets.is_empty() || m_tickets.front() == m_trans_sentinel);
|
||||
|
||||
/* Sort requests according to MDL_key. */
|
||||
if (! (sort_buf= (MDL_request **)my_malloc(req_count *
|
||||
sizeof(MDL_request*),
|
||||
|
10
sql/mdl.h
10
sql/mdl.h
@ -40,15 +40,16 @@ class Deadlock_detection_visitor;
|
||||
Type of metadata lock request.
|
||||
|
||||
@sa Comments for MDL_object_lock::can_grant_lock() and
|
||||
MDL_global_lock::can_grant_lock() for details.
|
||||
MDL_scoped_lock::can_grant_lock() for details.
|
||||
*/
|
||||
|
||||
enum enum_mdl_type {
|
||||
/*
|
||||
An intention exclusive metadata lock. Used only for global locks.
|
||||
An intention exclusive metadata lock. Used only for scoped locks.
|
||||
Owner of this type of lock can acquire upgradable exclusive locks on
|
||||
individual objects.
|
||||
Compatible with other IX locks, but is incompatible with global S lock.
|
||||
Compatible with other IX locks, but is incompatible with scoped S and
|
||||
X locks.
|
||||
*/
|
||||
MDL_INTENTION_EXCLUSIVE= 0,
|
||||
/*
|
||||
@ -179,6 +180,7 @@ public:
|
||||
MDL_key is also used outside of the MDL subsystem.
|
||||
*/
|
||||
enum enum_mdl_namespace { GLOBAL=0,
|
||||
SCHEMA,
|
||||
TABLE,
|
||||
FUNCTION,
|
||||
PROCEDURE,
|
||||
@ -646,6 +648,8 @@ private:
|
||||
closes all open HANDLERs.
|
||||
However, one can open a few HANDLERs after entering the
|
||||
read only mode.
|
||||
* LOCK TABLES locks include intention exclusive locks on
|
||||
involved schemas.
|
||||
*/
|
||||
Ticket_list m_tickets;
|
||||
/**
|
||||
|
@ -27,8 +27,8 @@
|
||||
// reset_status_vars
|
||||
#include "strfunc.h" // find_set_from_flags
|
||||
#include "parse_file.h" // File_parser_dummy_hook
|
||||
#include "sql_db.h" // my_database_names_free,
|
||||
// my_database_names_init
|
||||
#include "sql_db.h" // my_dboptions_cache_free
|
||||
// my_dboptions_cache_init
|
||||
#include "sql_table.h" // release_ddl_log, execute_ddl_log_recovery
|
||||
#include "sql_connect.h" // free_max_user_conn, init_max_user_conn,
|
||||
// handle_one_connection
|
||||
@ -653,7 +653,7 @@ SHOW_COMP_OPTION have_profiling;
|
||||
pthread_key(MEM_ROOT**,THR_MALLOC);
|
||||
pthread_key(THD*, THR_THD);
|
||||
mysql_mutex_t LOCK_thread_count;
|
||||
mysql_mutex_t LOCK_mysql_create_db, LOCK_open,
|
||||
mysql_mutex_t LOCK_open,
|
||||
LOCK_mapped_file, LOCK_status, LOCK_global_read_lock,
|
||||
LOCK_error_log, LOCK_uuid_generator,
|
||||
LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
|
||||
@ -1488,7 +1488,7 @@ void clean_up(bool print_message)
|
||||
bitmap_free(&slave_error_mask);
|
||||
#endif
|
||||
my_tz_free();
|
||||
my_database_names_free();
|
||||
my_dboptions_cache_free();
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
servers_free(1);
|
||||
acl_free(1);
|
||||
@ -1597,8 +1597,6 @@ static void wait_for_signal_thread_to_end()
|
||||
|
||||
static void clean_up_mutexes()
|
||||
{
|
||||
mysql_mutex_destroy(&LOCK_mysql_create_db);
|
||||
mysql_mutex_destroy(&LOCK_lock_db);
|
||||
mysql_rwlock_destroy(&LOCK_grant);
|
||||
mysql_mutex_destroy(&LOCK_open);
|
||||
mysql_mutex_destroy(&LOCK_thread_count);
|
||||
@ -3730,7 +3728,7 @@ static int init_common_variables()
|
||||
use_temp_pool= 0;
|
||||
#endif
|
||||
|
||||
if (my_database_names_init())
|
||||
if (my_dboptions_cache_init())
|
||||
return 1;
|
||||
|
||||
/*
|
||||
@ -3787,9 +3785,6 @@ You should consider changing lower_case_table_names to 1 or 2",
|
||||
|
||||
static int init_thread_environment()
|
||||
{
|
||||
mysql_mutex_init(key_LOCK_mysql_create_db,
|
||||
&LOCK_mysql_create_db, MY_MUTEX_INIT_SLOW);
|
||||
mysql_mutex_init(key_LOCK_lock_db, &LOCK_lock_db, MY_MUTEX_INIT_SLOW);
|
||||
mysql_mutex_init(key_LOCK_open, &LOCK_open, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_mapped_file, &LOCK_mapped_file, MY_MUTEX_INIT_SLOW);
|
||||
@ -8007,8 +8002,8 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
|
||||
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
|
||||
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
|
||||
key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
|
||||
key_LOCK_lock_db, key_LOCK_manager, key_LOCK_mapped_file,
|
||||
key_LOCK_mysql_create_db, key_LOCK_open, key_LOCK_prepared_stmt_count,
|
||||
key_LOCK_manager, key_LOCK_mapped_file,
|
||||
key_LOCK_open, key_LOCK_prepared_stmt_count,
|
||||
key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
|
||||
key_LOCK_system_variables_hash, key_LOCK_table_share, key_LOCK_thd_data,
|
||||
key_LOCK_user_conn, key_LOCK_uuid_generator, key_LOG_LOCK_log,
|
||||
@ -8046,10 +8041,8 @@ static PSI_mutex_info all_server_mutexes[]=
|
||||
{ &key_LOCK_gdl, "LOCK_gdl", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_global_read_lock, "LOCK_global_read_lock", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_lock_db, "LOCK_lock_db", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_manager, "LOCK_manager", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_mapped_file, "LOCK_mapped_file", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_mysql_create_db, "LOCK_mysql_create_db", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_open, "LOCK_open", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_prepared_stmt_count, "LOCK_prepared_stmt_count", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_rpl_status, "LOCK_rpl_status", PSI_FLAG_GLOBAL},
|
||||
|
@ -234,8 +234,8 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
|
||||
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
|
||||
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
|
||||
key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
|
||||
key_LOCK_lock_db, key_LOCK_logger, key_LOCK_manager, key_LOCK_mapped_file,
|
||||
key_LOCK_mysql_create_db, key_LOCK_open, key_LOCK_prepared_stmt_count,
|
||||
key_LOCK_logger, key_LOCK_manager, key_LOCK_mapped_file,
|
||||
key_LOCK_open, key_LOCK_prepared_stmt_count,
|
||||
key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
|
||||
key_LOCK_table_share, key_LOCK_thd_data,
|
||||
key_LOCK_user_conn, key_LOCK_uuid_generator, key_LOG_LOCK_log,
|
||||
@ -323,7 +323,7 @@ extern MYSQL_PLUGIN_IMPORT key_map key_map_full; /* Should be threaded
|
||||
/*
|
||||
Server mutex locks and condition variables.
|
||||
*/
|
||||
extern mysql_mutex_t LOCK_mysql_create_db, LOCK_open, LOCK_lock_db,
|
||||
extern mysql_mutex_t LOCK_open,
|
||||
LOCK_mapped_file, LOCK_user_locks, LOCK_status,
|
||||
LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator,
|
||||
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
|
||||
|
@ -677,16 +677,15 @@ my_bool acl_reload(THD *thd)
|
||||
To avoid deadlocks we should obtain table locks before
|
||||
obtaining acl_cache->lock mutex.
|
||||
*/
|
||||
bzero((char*) tables, sizeof(tables));
|
||||
tables[0].alias= tables[0].table_name= (char*) "host";
|
||||
tables[1].alias= tables[1].table_name= (char*) "user";
|
||||
tables[2].alias= tables[2].table_name= (char*) "db";
|
||||
tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
|
||||
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("host"), "host", TL_READ);
|
||||
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("user"), "user", TL_READ);
|
||||
tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("db"), "db", TL_READ);
|
||||
tables[0].next_local= tables[0].next_global= tables+1;
|
||||
tables[1].next_local= tables[1].next_global= tables+2;
|
||||
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
|
||||
tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
|
||||
init_mdl_requests(tables);
|
||||
|
||||
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
|
||||
{
|
||||
@ -1925,9 +1924,8 @@ static bool test_if_create_new_users(THD *thd)
|
||||
{
|
||||
TABLE_LIST tl;
|
||||
ulong db_access;
|
||||
bzero((char*) &tl,sizeof(tl));
|
||||
tl.db= (char*) "mysql";
|
||||
tl.table_name= (char*) "user";
|
||||
tl.init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
|
||||
create_new_users= 1;
|
||||
|
||||
db_access=acl_get(sctx->host, sctx->ip,
|
||||
@ -3107,20 +3105,18 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
|
||||
|
||||
/* open the mysql.tables_priv and mysql.columns_priv tables */
|
||||
|
||||
bzero((char*) &tables,sizeof(tables));
|
||||
tables[0].alias=tables[0].table_name= (char*) "user";
|
||||
tables[1].alias=tables[1].table_name= (char*) "tables_priv";
|
||||
tables[2].alias=tables[2].table_name= (char*) "columns_priv";
|
||||
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
|
||||
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("tables_priv"),
|
||||
"tables_priv", TL_WRITE);
|
||||
tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("columns_priv"),
|
||||
"columns_priv", TL_WRITE);
|
||||
tables[0].next_local= tables[0].next_global= tables+1;
|
||||
/* Don't open column table if we don't need it ! */
|
||||
tables[1].next_local=
|
||||
tables[1].next_global= ((column_priv ||
|
||||
(revoke_grant &&
|
||||
((rights & COL_ACLS) || columns.elements)))
|
||||
? 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";
|
||||
init_mdl_requests(tables);
|
||||
if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
|
||||
tables[1].next_local= tables[1].next_global= tables+2;
|
||||
|
||||
/*
|
||||
This statement will be replicated as a statement, even when using
|
||||
@ -3354,13 +3350,11 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
|
||||
|
||||
/* open the mysql.user and mysql.procs_priv tables */
|
||||
|
||||
bzero((char*) &tables,sizeof(tables));
|
||||
tables[0].alias=tables[0].table_name= (char*) "user";
|
||||
tables[1].alias=tables[1].table_name= (char*) "procs_priv";
|
||||
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
|
||||
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
|
||||
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";
|
||||
init_mdl_requests(tables);
|
||||
|
||||
/*
|
||||
This statement will be replicated as a statement, even when using
|
||||
@ -3511,13 +3505,11 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
|
||||
}
|
||||
|
||||
/* open the mysql.user and mysql.db tables */
|
||||
bzero((char*) &tables,sizeof(tables));
|
||||
tables[0].alias=tables[0].table_name=(char*) "user";
|
||||
tables[1].alias=tables[1].table_name=(char*) "db";
|
||||
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
|
||||
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("db"), "db", TL_WRITE);
|
||||
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";
|
||||
init_mdl_requests(tables);
|
||||
|
||||
/*
|
||||
This statement will be replicated as a statement, even when using
|
||||
@ -3930,14 +3922,14 @@ my_bool grant_reload(THD *thd)
|
||||
if (!initialized)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
bzero((char*) tables, sizeof(tables));
|
||||
tables[0].alias= tables[0].table_name= (char*) "tables_priv";
|
||||
tables[1].alias= tables[1].table_name= (char*) "columns_priv";
|
||||
tables[0].db= tables[1].db= (char *) "mysql";
|
||||
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("tables_priv"),
|
||||
"tables_priv", TL_READ);
|
||||
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("columns_priv"),
|
||||
"columns_priv", TL_READ);
|
||||
tables[0].next_local= tables[0].next_global= tables+1;
|
||||
tables[0].lock_type= tables[1].lock_type= TL_READ;
|
||||
tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
|
||||
init_mdl_requests(tables);
|
||||
|
||||
/*
|
||||
To avoid deadlocks we should obtain table locks before
|
||||
@ -5209,22 +5201,23 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
|
||||
tables->alias= tables->table_name= (char*) "user";
|
||||
(tables+1)->alias= (tables+1)->table_name= (char*) "db";
|
||||
(tables+2)->alias= (tables+2)->table_name= (char*) "tables_priv";
|
||||
(tables+3)->alias= (tables+3)->table_name= (char*) "columns_priv";
|
||||
(tables+4)->alias= (tables+4)->table_name= (char*) "procs_priv";
|
||||
tables->init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
|
||||
(tables+1)->init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("db"), "db", TL_WRITE);
|
||||
(tables+2)->init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("tables_priv"),
|
||||
"tables_priv", TL_WRITE);
|
||||
(tables+3)->init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("columns_priv"),
|
||||
"columns_priv", TL_WRITE);
|
||||
(tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("procs_priv"),
|
||||
"procs_priv", TL_WRITE);
|
||||
tables->next_local= tables->next_global= tables+1;
|
||||
(tables+1)->next_local= (tables+1)->next_global= tables+2;
|
||||
(tables+2)->next_local= (tables+2)->next_global= tables+3;
|
||||
(tables+3)->next_local= (tables+3)->next_global= tables+4;
|
||||
tables->lock_type= (tables+1)->lock_type=
|
||||
(tables+2)->lock_type= (tables+3)->lock_type=
|
||||
(tables+4)->lock_type= TL_WRITE;
|
||||
tables->db= (tables+1)->db= (tables+2)->db=
|
||||
(tables+3)->db= (tables+4)->db= (char*) "mysql";
|
||||
init_mdl_requests(tables);
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
/*
|
||||
|
153
sql/sql_base.cc
153
sql/sql_base.cc
@ -53,6 +53,7 @@
|
||||
#include "rpl_filter.h"
|
||||
#include "sql_table.h" // build_table_filename
|
||||
#include "datadict.h" // dd_frm_type()
|
||||
#include "sql_hset.h" // Hash_set
|
||||
#ifdef __WIN__
|
||||
#include <io.h>
|
||||
#endif
|
||||
@ -4029,7 +4030,6 @@ end_unlock:
|
||||
Open_table_context::Open_table_context(THD *thd, uint flags)
|
||||
:m_failed_table(NULL),
|
||||
m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()),
|
||||
m_global_mdl_request(NULL),
|
||||
m_timeout(flags & MYSQL_LOCK_IGNORE_TIMEOUT ?
|
||||
LONG_TIMEOUT : thd->variables.lock_wait_timeout),
|
||||
m_flags(flags),
|
||||
@ -4040,26 +4040,6 @@ Open_table_context::Open_table_context(THD *thd, uint flags)
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
Get MDL_request object for global intention exclusive lock which
|
||||
is acquired during opening tables for statements which take
|
||||
upgradable shared metadata locks.
|
||||
*/
|
||||
|
||||
MDL_request *Open_table_context::get_global_mdl_request(THD *thd)
|
||||
{
|
||||
if (! m_global_mdl_request)
|
||||
{
|
||||
if ((m_global_mdl_request= new (thd->mem_root) MDL_request()))
|
||||
{
|
||||
m_global_mdl_request->init(MDL_key::GLOBAL, "", "",
|
||||
MDL_INTENTION_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
return m_global_mdl_request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Check if we can back-off and set back off action if we can.
|
||||
Otherwise report and return error.
|
||||
@ -4108,13 +4088,23 @@ request_backoff_action(enum_open_table_action action_arg,
|
||||
my_error(ER_LOCK_DEADLOCK, MYF(0));
|
||||
return TRUE;
|
||||
}
|
||||
m_action= action_arg;
|
||||
/*
|
||||
If auto-repair or discovery are requested, a pointer to table
|
||||
list element must be provided.
|
||||
*/
|
||||
DBUG_ASSERT((m_action != OT_DISCOVER && m_action != OT_REPAIR) || table);
|
||||
m_failed_table= table;
|
||||
if (table)
|
||||
{
|
||||
DBUG_ASSERT(action_arg == OT_DISCOVER || action_arg == OT_REPAIR);
|
||||
m_failed_table= (TABLE_LIST*) current_thd->alloc(sizeof(TABLE_LIST));
|
||||
if (m_failed_table == NULL)
|
||||
return TRUE;
|
||||
m_failed_table->init_one_table(table->db, table->db_length,
|
||||
table->table_name,
|
||||
table->table_name_length,
|
||||
table->alias, TL_WRITE);
|
||||
m_failed_table->mdl_request.set_type(MDL_EXCLUSIVE);
|
||||
}
|
||||
m_action= action_arg;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -4136,11 +4126,6 @@ Open_table_context::
|
||||
recover_from_failed_open(THD *thd)
|
||||
{
|
||||
bool result= FALSE;
|
||||
/*
|
||||
Remove reference to released ticket from MDL_request.
|
||||
*/
|
||||
if (m_global_mdl_request)
|
||||
m_global_mdl_request->ticket= NULL;
|
||||
/* Execute the action. */
|
||||
switch (m_action)
|
||||
{
|
||||
@ -4152,19 +4137,9 @@ recover_from_failed_open(THD *thd)
|
||||
break;
|
||||
case OT_DISCOVER:
|
||||
{
|
||||
MDL_request mdl_global_request;
|
||||
MDL_request mdl_xlock_request(&m_failed_table->mdl_request);
|
||||
MDL_request_list mdl_requests;
|
||||
|
||||
mdl_global_request.init(MDL_key::GLOBAL, "", "",
|
||||
MDL_INTENTION_EXCLUSIVE);
|
||||
mdl_xlock_request.set_type(MDL_EXCLUSIVE);
|
||||
|
||||
mdl_requests.push_front(&mdl_xlock_request);
|
||||
mdl_requests.push_front(&mdl_global_request);
|
||||
|
||||
if ((result=
|
||||
thd->mdl_context.acquire_locks(&mdl_requests, get_timeout())))
|
||||
if ((result= lock_table_names(thd, m_failed_table, NULL,
|
||||
get_timeout(),
|
||||
MYSQL_OPEN_SKIP_TEMPORARY)))
|
||||
break;
|
||||
|
||||
mysql_mutex_lock(&LOCK_open);
|
||||
@ -4181,19 +4156,9 @@ recover_from_failed_open(THD *thd)
|
||||
}
|
||||
case OT_REPAIR:
|
||||
{
|
||||
MDL_request mdl_global_request;
|
||||
MDL_request mdl_xlock_request(&m_failed_table->mdl_request);
|
||||
MDL_request_list mdl_requests;
|
||||
|
||||
mdl_global_request.init(MDL_key::GLOBAL, "", "",
|
||||
MDL_INTENTION_EXCLUSIVE);
|
||||
mdl_xlock_request.set_type(MDL_EXCLUSIVE);
|
||||
|
||||
mdl_requests.push_front(&mdl_xlock_request);
|
||||
mdl_requests.push_front(&mdl_global_request);
|
||||
|
||||
if ((result=
|
||||
thd->mdl_context.acquire_locks(&mdl_requests, get_timeout())))
|
||||
if ((result= lock_table_names(thd, m_failed_table, NULL,
|
||||
get_timeout(),
|
||||
MYSQL_OPEN_SKIP_TEMPORARY)))
|
||||
break;
|
||||
|
||||
mysql_mutex_lock(&LOCK_open);
|
||||
@ -4688,17 +4653,24 @@ end:
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
extern "C" uchar *schema_set_get_key(const uchar *record, size_t *length,
|
||||
my_bool not_used __attribute__((unused)))
|
||||
{
|
||||
TABLE_LIST *table=(TABLE_LIST*) record;
|
||||
*length= table->db_length;
|
||||
return (uchar*) table->db;
|
||||
}
|
||||
|
||||
/**
|
||||
Acquire upgradable (SNW, SNRW) metadata locks on tables to be opened
|
||||
for LOCK TABLES or a DDL statement. Under LOCK TABLES, we can't take
|
||||
Acquire upgradable (SNW, SNRW) metadata locks on tables used by
|
||||
LOCK TABLES or by a DDL statement. Under LOCK TABLES, we can't take
|
||||
new locks, so use open_tables_check_upgradable_mdl() instead.
|
||||
|
||||
@param thd Thread context.
|
||||
@param tables_start Start of list of tables on which upgradable locks
|
||||
should be acquired.
|
||||
@param tables_end End of list of tables.
|
||||
@param ot_ctx Context of open_tables() operation.
|
||||
@param lock_wait_timeout Seconds to wait before timeout.
|
||||
@param flags Bitmap of flags to modify how the tables will be
|
||||
open, see open_table() description for details.
|
||||
|
||||
@ -4706,14 +4678,15 @@ end:
|
||||
@retval TRUE Failure (e.g. connection was killed)
|
||||
*/
|
||||
|
||||
static bool
|
||||
open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
|
||||
TABLE_LIST *tables_end,
|
||||
Open_table_context *ot_ctx,
|
||||
uint flags)
|
||||
bool
|
||||
lock_table_names(THD *thd,
|
||||
TABLE_LIST *tables_start, TABLE_LIST *tables_end,
|
||||
ulong lock_wait_timeout, uint flags)
|
||||
{
|
||||
MDL_request_list mdl_requests;
|
||||
TABLE_LIST *table;
|
||||
MDL_request global_request;
|
||||
Hash_set<TABLE_LIST, schema_set_get_key> schema_set;
|
||||
|
||||
DBUG_ASSERT(!thd->locked_tables_mode);
|
||||
|
||||
@ -4726,29 +4699,36 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
|
||||
(table->open_type != OT_BASE_ONLY &&
|
||||
! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
|
||||
find_temporary_table(thd, table))))
|
||||
{
|
||||
if (schema_set.insert(table))
|
||||
return TRUE;
|
||||
mdl_requests.push_front(&table->mdl_request);
|
||||
}
|
||||
}
|
||||
|
||||
if (! mdl_requests.is_empty())
|
||||
{
|
||||
DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl");
|
||||
|
||||
MDL_request *global_request= ot_ctx->get_global_mdl_request(thd);
|
||||
|
||||
if (global_request == NULL)
|
||||
return TRUE;
|
||||
mdl_requests.push_front(global_request);
|
||||
}
|
||||
|
||||
if (thd->mdl_context.acquire_locks(&mdl_requests, ot_ctx->get_timeout()))
|
||||
return TRUE;
|
||||
|
||||
for (table= tables_start; table && table != tables_end;
|
||||
table= table->next_global)
|
||||
/*
|
||||
Scoped locks: Take intention exclusive locks on all involved
|
||||
schemas.
|
||||
*/
|
||||
Hash_set<TABLE_LIST, schema_set_get_key>::Iterator it(schema_set);
|
||||
while ((table= it++))
|
||||
{
|
||||
if (table->mdl_request.type >= MDL_SHARED_NO_WRITE)
|
||||
table->mdl_request.ticket= NULL;
|
||||
MDL_request *schema_request= new (thd->mem_root) MDL_request;
|
||||
if (schema_request == NULL)
|
||||
return TRUE;
|
||||
schema_request->init(MDL_key::SCHEMA, table->db, "",
|
||||
MDL_INTENTION_EXCLUSIVE);
|
||||
mdl_requests.push_front(schema_request);
|
||||
}
|
||||
/* Take the global intention exclusive lock. */
|
||||
global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
|
||||
mdl_requests.push_front(&global_request);
|
||||
}
|
||||
|
||||
if (thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
@ -4921,13 +4901,22 @@ restart:
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else if (open_tables_acquire_upgradable_mdl(thd, *start,
|
||||
thd->lex->first_not_own_table(),
|
||||
&ot_ctx, flags))
|
||||
else
|
||||
{
|
||||
TABLE_LIST *table;
|
||||
if (lock_table_names(thd, *start, thd->lex->first_not_own_table(),
|
||||
ot_ctx.get_timeout(), flags))
|
||||
{
|
||||
error= TRUE;
|
||||
goto err;
|
||||
}
|
||||
for (table= *start; table && table != thd->lex->first_not_own_table();
|
||||
table= table->next_global)
|
||||
{
|
||||
if (table->mdl_request.type >= MDL_SHARED_NO_WRITE)
|
||||
table->mdl_request.ticket= NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -205,6 +205,9 @@ int setup_ftfuncs(SELECT_LEX* select);
|
||||
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
|
||||
void wait_for_condition(THD *thd, mysql_mutex_t *mutex,
|
||||
mysql_cond_t *cond);
|
||||
bool lock_table_names(THD *thd, TABLE_LIST *table_list,
|
||||
TABLE_LIST *table_list_end, ulong lock_wait_timeout,
|
||||
uint flags);
|
||||
bool open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags,
|
||||
Prelocking_strategy *prelocking_strategy);
|
||||
/* open_and_lock_tables with optional derived handling */
|
||||
@ -480,8 +483,6 @@ public:
|
||||
return m_start_of_statement_svp;
|
||||
}
|
||||
|
||||
MDL_request *get_global_mdl_request(THD *thd);
|
||||
|
||||
inline ulong get_timeout() const
|
||||
{
|
||||
return m_timeout;
|
||||
@ -498,11 +499,6 @@ private:
|
||||
*/
|
||||
TABLE_LIST *m_failed_table;
|
||||
MDL_ticket *m_start_of_statement_svp;
|
||||
/**
|
||||
Request object for global intention exclusive lock which is acquired during
|
||||
opening tables for statements which take upgradable shared metadata locks.
|
||||
*/
|
||||
MDL_request *m_global_mdl_request;
|
||||
/**
|
||||
Lock timeout in seconds. Initialized to LONG_TIMEOUT when opening system
|
||||
tables or to the "lock_wait_timeout" system variable for regular tables.
|
||||
|
316
sql/sql_db.cc
316
sql/sql_db.cc
@ -58,106 +58,6 @@ static void mysql_change_db_impl(THD *thd,
|
||||
CHARSET_INFO *new_db_charset);
|
||||
|
||||
|
||||
/* Database lock hash */
|
||||
HASH lock_db_cache;
|
||||
mysql_mutex_t LOCK_lock_db;
|
||||
int creating_database= 0; // how many database locks are made
|
||||
|
||||
|
||||
/* Structure for database lock */
|
||||
typedef struct my_dblock_st
|
||||
{
|
||||
char *name; /* Database name */
|
||||
uint name_length; /* Database length name */
|
||||
} my_dblock_t;
|
||||
|
||||
|
||||
/*
|
||||
lock_db key.
|
||||
*/
|
||||
|
||||
extern "C" uchar* lock_db_get_key(my_dblock_t *, size_t *, my_bool not_used);
|
||||
|
||||
uchar* lock_db_get_key(my_dblock_t *ptr, size_t *length,
|
||||
my_bool not_used __attribute__((unused)))
|
||||
{
|
||||
*length= ptr->name_length;
|
||||
return (uchar*) ptr->name;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Free lock_db hash element.
|
||||
*/
|
||||
|
||||
extern "C" void lock_db_free_element(void *ptr);
|
||||
|
||||
void lock_db_free_element(void *ptr)
|
||||
{
|
||||
my_free(ptr, MYF(0));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Put a database lock entry into the hash.
|
||||
|
||||
DESCRIPTION
|
||||
Insert a database lock entry into hash.
|
||||
LOCK_db_lock must be previously locked.
|
||||
|
||||
RETURN VALUES
|
||||
0 on success.
|
||||
1 on error.
|
||||
*/
|
||||
|
||||
static my_bool lock_db_insert(const char *dbname, uint length)
|
||||
{
|
||||
my_dblock_t *opt;
|
||||
my_bool error= 0;
|
||||
DBUG_ENTER("lock_db_insert");
|
||||
|
||||
mysql_mutex_assert_owner(&LOCK_lock_db);
|
||||
|
||||
if (!(opt= (my_dblock_t*) my_hash_search(&lock_db_cache,
|
||||
(uchar*) dbname, length)))
|
||||
{
|
||||
/* Db is not in the hash, insert it */
|
||||
char *tmp_name;
|
||||
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
|
||||
&opt, (uint) sizeof(*opt), &tmp_name, (uint) length+1,
|
||||
NullS))
|
||||
{
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
opt->name= tmp_name;
|
||||
strmov(opt->name, dbname);
|
||||
opt->name_length= length;
|
||||
|
||||
if ((error= my_hash_insert(&lock_db_cache, (uchar*) opt)))
|
||||
my_free(opt, MYF(0));
|
||||
}
|
||||
|
||||
end:
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Delete a database lock entry from hash.
|
||||
*/
|
||||
|
||||
void lock_db_delete(const char *name, uint length)
|
||||
{
|
||||
my_dblock_t *opt;
|
||||
mysql_mutex_assert_owner(&LOCK_lock_db);
|
||||
if ((opt= (my_dblock_t *)my_hash_search(&lock_db_cache,
|
||||
(const uchar*) name, length)))
|
||||
my_hash_delete(&lock_db_cache, (uchar*) opt);
|
||||
}
|
||||
|
||||
|
||||
/* Database options hash */
|
||||
static HASH dboptions;
|
||||
static my_bool dboptions_init= 0;
|
||||
@ -233,21 +133,16 @@ static void init_database_names_psi_keys(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Initialize database option hash and locked database hash.
|
||||
/**
|
||||
Initialize database option cache.
|
||||
|
||||
SYNOPSIS
|
||||
my_database_names()
|
||||
@note Must be called before any other database function is called.
|
||||
|
||||
NOTES
|
||||
Must be called before any other database function is called.
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
1 Fatal error
|
||||
@retval 0 ok
|
||||
@retval 1 Fatal error
|
||||
*/
|
||||
|
||||
bool my_database_names_init(void)
|
||||
bool my_dboptions_cache_init(void)
|
||||
{
|
||||
#ifdef HAVE_PSI_INTERFACE
|
||||
init_database_names_psi_keys();
|
||||
@ -261,36 +156,30 @@ bool my_database_names_init(void)
|
||||
error= my_hash_init(&dboptions, lower_case_table_names ?
|
||||
&my_charset_bin : system_charset_info,
|
||||
32, 0, 0, (my_hash_get_key) dboptions_get_key,
|
||||
free_dbopt,0) ||
|
||||
my_hash_init(&lock_db_cache, lower_case_table_names ?
|
||||
&my_charset_bin : system_charset_info,
|
||||
32, 0, 0, (my_hash_get_key) lock_db_get_key,
|
||||
lock_db_free_element,0);
|
||||
|
||||
free_dbopt,0);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
Free database option hash and locked databases hash.
|
||||
*/
|
||||
|
||||
void my_database_names_free(void)
|
||||
void my_dboptions_cache_free(void)
|
||||
{
|
||||
if (dboptions_init)
|
||||
{
|
||||
dboptions_init= 0;
|
||||
my_hash_free(&dboptions);
|
||||
mysql_rwlock_destroy(&LOCK_dboptions);
|
||||
my_hash_free(&lock_db_cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Cleanup cached options
|
||||
/**
|
||||
Cleanup cached options.
|
||||
*/
|
||||
|
||||
void my_dbopt_cleanup(void)
|
||||
@ -395,7 +284,7 @@ end:
|
||||
Deletes database options from the hash.
|
||||
*/
|
||||
|
||||
void del_dbopt(const char *path)
|
||||
static void del_dbopt(const char *path)
|
||||
{
|
||||
my_dbopt_t *opt;
|
||||
mysql_rwlock_wrlock(&LOCK_dboptions);
|
||||
@ -664,25 +553,8 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
Do not create database if another thread is holding read lock.
|
||||
Wait for global read lock before acquiring LOCK_mysql_create_db.
|
||||
After wait_if_global_read_lock() we have protection against another
|
||||
global read lock. If we would acquire LOCK_mysql_create_db first,
|
||||
another thread could step in and get the global read lock before we
|
||||
reach wait_if_global_read_lock(). If this thread tries the same as we
|
||||
(admin a db), it would then go and wait on LOCK_mysql_create_db...
|
||||
Furthermore wait_if_global_read_lock() checks if the current thread
|
||||
has the global read lock and refuses the operation with
|
||||
ER_CANT_UPDATE_WITH_READLOCK if applicable.
|
||||
*/
|
||||
if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
|
||||
{
|
||||
error= -1;
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
mysql_mutex_lock(&LOCK_mysql_create_db);
|
||||
if (lock_schema_name(thd, db))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* Check directory */
|
||||
path_len= build_table_filename(path, sizeof(path) - 1, db, "", "", 0);
|
||||
@ -786,7 +658,10 @@ not_silent:
|
||||
qinfo.db = db;
|
||||
qinfo.db_len = strlen(db);
|
||||
|
||||
/* These DDL methods and logging protected with LOCK_mysql_create_db */
|
||||
/*
|
||||
These DDL methods and logging are protected with the exclusive
|
||||
metadata lock on the schema
|
||||
*/
|
||||
if (mysql_bin_log.write(&qinfo))
|
||||
{
|
||||
error= -1;
|
||||
@ -797,9 +672,6 @@ not_silent:
|
||||
}
|
||||
|
||||
exit:
|
||||
mysql_mutex_unlock(&LOCK_mysql_create_db);
|
||||
thd->global_read_lock.start_waiting_global_read_lock(thd);
|
||||
exit2:
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
@ -813,22 +685,8 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
|
||||
int error= 0;
|
||||
DBUG_ENTER("mysql_alter_db");
|
||||
|
||||
/*
|
||||
Do not alter database if another thread is holding read lock.
|
||||
Wait for global read lock before acquiring LOCK_mysql_create_db.
|
||||
After wait_if_global_read_lock() we have protection against another
|
||||
global read lock. If we would acquire LOCK_mysql_create_db first,
|
||||
another thread could step in and get the global read lock before we
|
||||
reach wait_if_global_read_lock(). If this thread tries the same as we
|
||||
(admin a db), it would then go and wait on LOCK_mysql_create_db...
|
||||
Furthermore wait_if_global_read_lock() checks if the current thread
|
||||
has the global read lock and refuses the operation with
|
||||
ER_CANT_UPDATE_WITH_READLOCK if applicable.
|
||||
*/
|
||||
if ((error= thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE)))
|
||||
goto exit2;
|
||||
|
||||
mysql_mutex_lock(&LOCK_mysql_create_db);
|
||||
if (lock_schema_name(thd, db))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
/*
|
||||
Recreate db options file: /dbpath/.db.opt
|
||||
@ -866,16 +724,16 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
|
||||
qinfo.db = db;
|
||||
qinfo.db_len = strlen(db);
|
||||
|
||||
/* These DDL methods and logging protected with LOCK_mysql_create_db */
|
||||
/*
|
||||
These DDL methods and logging are protected with the exclusive
|
||||
metadata lock on the schema.
|
||||
*/
|
||||
if ((error= mysql_bin_log.write(&qinfo)))
|
||||
goto exit;
|
||||
}
|
||||
my_ok(thd, result);
|
||||
|
||||
exit:
|
||||
mysql_mutex_unlock(&LOCK_mysql_create_db);
|
||||
thd->global_read_lock.start_waiting_global_read_lock(thd);
|
||||
exit2:
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
@ -907,25 +765,9 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
|
||||
TABLE_LIST* dropped_tables= 0;
|
||||
DBUG_ENTER("mysql_rm_db");
|
||||
|
||||
/*
|
||||
Do not drop database if another thread is holding read lock.
|
||||
Wait for global read lock before acquiring LOCK_mysql_create_db.
|
||||
After wait_if_global_read_lock() we have protection against another
|
||||
global read lock. If we would acquire LOCK_mysql_create_db first,
|
||||
another thread could step in and get the global read lock before we
|
||||
reach wait_if_global_read_lock(). If this thread tries the same as we
|
||||
(admin a db), it would then go and wait on LOCK_mysql_create_db...
|
||||
Furthermore wait_if_global_read_lock() checks if the current thread
|
||||
has the global read lock and refuses the operation with
|
||||
ER_CANT_UPDATE_WITH_READLOCK if applicable.
|
||||
*/
|
||||
if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
|
||||
{
|
||||
error= -1;
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
mysql_mutex_lock(&LOCK_mysql_create_db);
|
||||
if (lock_schema_name(thd, db))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
length= build_table_filename(path, sizeof(path) - 1, db, "", "", 0);
|
||||
strmov(path+length, MY_DB_OPT_FILE); // Append db option file name
|
||||
@ -1013,7 +855,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
|
||||
qinfo.db = db;
|
||||
qinfo.db_len = strlen(db);
|
||||
|
||||
/* These DDL methods and logging protected with LOCK_mysql_create_db */
|
||||
/*
|
||||
These DDL methods and logging are protected with the exclusive
|
||||
metadata lock on the schema.
|
||||
*/
|
||||
if (mysql_bin_log.write(&qinfo))
|
||||
{
|
||||
error= -1;
|
||||
@ -1045,7 +890,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
|
||||
tbl_name_len= strlen(tbl->table_name) + 3;
|
||||
if (query_pos + tbl_name_len + 1 >= query_end)
|
||||
{
|
||||
/* These DDL methods and logging protected with LOCK_mysql_create_db */
|
||||
/*
|
||||
These DDL methods and logging are protected with the exclusive
|
||||
metadata lock on the schema.
|
||||
*/
|
||||
if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len))
|
||||
{
|
||||
error= -1;
|
||||
@ -1062,7 +910,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
|
||||
|
||||
if (query_pos != query_data_start)
|
||||
{
|
||||
/* These DDL methods and logging protected with LOCK_mysql_create_db */
|
||||
/*
|
||||
These DDL methods and logging are protected with the exclusive
|
||||
metadata lock on the schema.
|
||||
*/
|
||||
if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len))
|
||||
{
|
||||
error= -1;
|
||||
@ -1080,9 +931,6 @@ exit:
|
||||
*/
|
||||
if (thd->db && !strcmp(thd->db, db) && error == 0)
|
||||
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
|
||||
mysql_mutex_unlock(&LOCK_mysql_create_db);
|
||||
thd->global_read_lock.start_waiting_global_read_lock(thd);
|
||||
exit2:
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
@ -1099,12 +947,12 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
|
||||
long deleted=0;
|
||||
ulong found_other_files=0;
|
||||
char filePath[FN_REFLEN];
|
||||
TABLE_LIST *tot_list=0, **tot_list_next;
|
||||
TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global;
|
||||
List<String> raid_dirs;
|
||||
DBUG_ENTER("mysql_rm_known_files");
|
||||
DBUG_PRINT("enter",("path: %s", org_path));
|
||||
|
||||
tot_list_next= &tot_list;
|
||||
tot_list_next_local= tot_list_next_global= &tot_list;
|
||||
|
||||
for (uint idx=0 ;
|
||||
idx < (uint) dirp->number_off_files && !thd->killed ;
|
||||
@ -1192,23 +1040,28 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
|
||||
if (!table_list)
|
||||
goto err;
|
||||
table_list->db= (char*) (table_list+1);
|
||||
table_list->table_name= strmov(table_list->db, db) + 1;
|
||||
(void) filename_to_tablename(file->name, table_list->table_name,
|
||||
table_list->db_length= strmov(table_list->db, db) - table_list->db;
|
||||
table_list->table_name= table_list->db + table_list->db_length + 1;
|
||||
table_list->table_name_length= filename_to_tablename(file->name,
|
||||
table_list->table_name,
|
||||
MYSQL50_TABLE_NAME_PREFIX_LENGTH +
|
||||
strlen(file->name) + 1);
|
||||
table_list->open_type= OT_BASE_ONLY;
|
||||
|
||||
/* To be able to correctly look up the table in the table cache. */
|
||||
if (lower_case_table_names)
|
||||
my_casedn_str(files_charset_info, table_list->table_name);
|
||||
table_list->table_name_length= my_casedn_str(files_charset_info,
|
||||
table_list->table_name);
|
||||
|
||||
table_list->alias= table_list->table_name; // If lower_case_table_names=2
|
||||
table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix);
|
||||
table_list->mdl_request.init(MDL_key::TABLE, table_list->db,
|
||||
table_list->table_name, MDL_EXCLUSIVE);
|
||||
/* Link into list */
|
||||
(*tot_list_next)= table_list;
|
||||
tot_list_next= &table_list->next_local;
|
||||
(*tot_list_next_local)= table_list;
|
||||
(*tot_list_next_global)= table_list;
|
||||
tot_list_next_local= &table_list->next_local;
|
||||
tot_list_next_global= &table_list->next_global;
|
||||
deleted++;
|
||||
}
|
||||
else
|
||||
@ -1771,60 +1624,6 @@ bool mysql_opt_change_db(THD *thd,
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
lock_databases(THD *thd, const char *db1, uint length1,
|
||||
const char *db2, uint length2)
|
||||
{
|
||||
mysql_mutex_lock(&LOCK_lock_db);
|
||||
while (!thd->killed &&
|
||||
(my_hash_search(&lock_db_cache,(uchar*) db1, length1) ||
|
||||
my_hash_search(&lock_db_cache,(uchar*) db2, length2)))
|
||||
{
|
||||
wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
|
||||
mysql_mutex_lock(&LOCK_lock_db);
|
||||
}
|
||||
|
||||
if (thd->killed)
|
||||
{
|
||||
mysql_mutex_unlock(&LOCK_lock_db);
|
||||
return 1;
|
||||
}
|
||||
|
||||
lock_db_insert(db1, length1);
|
||||
lock_db_insert(db2, length2);
|
||||
creating_database++;
|
||||
|
||||
/*
|
||||
Wait if a concurent thread is creating a table at the same time.
|
||||
The assumption here is that it will not take too long until
|
||||
there is a point in time when a table is not created.
|
||||
*/
|
||||
|
||||
while (!thd->killed && creating_table)
|
||||
{
|
||||
wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
|
||||
mysql_mutex_lock(&LOCK_lock_db);
|
||||
}
|
||||
|
||||
if (thd->killed)
|
||||
{
|
||||
lock_db_delete(db1, length1);
|
||||
lock_db_delete(db2, length2);
|
||||
creating_database--;
|
||||
mysql_mutex_unlock(&LOCK_lock_db);
|
||||
mysql_cond_signal(&COND_refresh);
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
We can unlock now as the hash will protect against anyone creating a table
|
||||
in the databases we are using
|
||||
*/
|
||||
mysql_mutex_unlock(&LOCK_lock_db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Upgrade a 5.0 database.
|
||||
This function is invoked whenever an ALTER DATABASE UPGRADE query is executed:
|
||||
@ -1866,9 +1665,9 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
|
||||
new_db.str= old_db->str + MYSQL50_TABLE_NAME_PREFIX_LENGTH;
|
||||
new_db.length= old_db->length - MYSQL50_TABLE_NAME_PREFIX_LENGTH;
|
||||
|
||||
if (lock_databases(thd, old_db->str, old_db->length,
|
||||
new_db.str, new_db.length))
|
||||
DBUG_RETURN(1);
|
||||
/* Lock the old name, the new name will be locked by mysql_create_db().*/
|
||||
if (lock_schema_name(thd, old_db->str))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/*
|
||||
Let's remember if we should do "USE newdb" afterwards.
|
||||
@ -2035,15 +1834,6 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
|
||||
error|= mysql_change_db(thd, & new_db, FALSE);
|
||||
|
||||
exit:
|
||||
mysql_mutex_lock(&LOCK_lock_db);
|
||||
/* Remove the databases from db lock cache */
|
||||
lock_db_delete(old_db->str, old_db->length);
|
||||
lock_db_delete(new_db.str, new_db.length);
|
||||
creating_database--;
|
||||
/* Signal waiting CREATE TABLE's to continue */
|
||||
mysql_cond_signal(&COND_refresh);
|
||||
mysql_mutex_unlock(&LOCK_lock_db);
|
||||
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,8 @@ bool mysql_opt_change_db(THD *thd,
|
||||
LEX_STRING *saved_db_name,
|
||||
bool force_switch,
|
||||
bool *cur_db_changed);
|
||||
bool my_database_names_init(void);
|
||||
void my_database_names_free(void);
|
||||
bool my_dboptions_cache_init(void);
|
||||
void my_dboptions_cache_free(void);
|
||||
bool check_db_dir_existence(const char *db_name);
|
||||
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
|
||||
bool load_db_opt_by_name(THD *thd, const char *db_name,
|
||||
@ -45,9 +45,6 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name);
|
||||
bool my_dbopt_init(void);
|
||||
void my_dbopt_cleanup(void);
|
||||
|
||||
extern int creating_database; // How many database locks are made
|
||||
extern HASH lock_db_cache;
|
||||
|
||||
#define MY_DB_OPT_FILE "db.opt"
|
||||
|
||||
#endif /* SQL_DB_INCLUDED */
|
||||
|
@ -643,23 +643,24 @@ bool mysqld_help(THD *thd, const char *mask)
|
||||
MEM_ROOT *mem_root= thd->mem_root;
|
||||
DBUG_ENTER("mysqld_help");
|
||||
|
||||
bzero((uchar*)tables,sizeof(tables));
|
||||
tables[0].alias= tables[0].table_name= (char*) "help_topic";
|
||||
tables[0].lock_type= TL_READ;
|
||||
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("help_topic"),
|
||||
"help_topic", TL_READ);
|
||||
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("help_category"),
|
||||
"help_category", TL_READ);
|
||||
tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("help_relation"),
|
||||
"help_relation", TL_READ);
|
||||
tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
|
||||
C_STRING_WITH_LEN("help_keyword"),
|
||||
"help_keyword", TL_READ);
|
||||
tables[0].next_global= tables[0].next_local=
|
||||
tables[0].next_name_resolution_table= &tables[1];
|
||||
tables[1].alias= tables[1].table_name= (char*) "help_category";
|
||||
tables[1].lock_type= TL_READ;
|
||||
tables[1].next_global= tables[1].next_local=
|
||||
tables[1].next_name_resolution_table= &tables[2];
|
||||
tables[2].alias= tables[2].table_name= (char*) "help_relation";
|
||||
tables[2].lock_type= TL_READ;
|
||||
tables[2].next_global= tables[2].next_local=
|
||||
tables[2].next_name_resolution_table= &tables[3];
|
||||
tables[3].alias= tables[3].table_name= (char*) "help_keyword";
|
||||
tables[3].lock_type= TL_READ;
|
||||
tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql";
|
||||
init_mdl_requests(tables);
|
||||
|
||||
/*
|
||||
HELP must be available under LOCK TABLES.
|
||||
|
117
sql/sql_hset.h
Normal file
117
sql/sql_hset.h
Normal file
@ -0,0 +1,117 @@
|
||||
#ifndef SQL_HSET_INCLUDED
|
||||
#define SQL_HSET_INCLUDED
|
||||
/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "my_global.h"
|
||||
#include "hash.h"
|
||||
|
||||
|
||||
/**
|
||||
A type-safe wrapper around mysys HASH.
|
||||
*/
|
||||
|
||||
template <typename T, my_hash_get_key K>
|
||||
class Hash_set
|
||||
{
|
||||
public:
|
||||
typedef T Value_type;
|
||||
enum { START_SIZE= 8 };
|
||||
/**
|
||||
Constructs an empty hash. Does not allocate memory, it is done upon
|
||||
the first insert. Thus does not cause or return errors.
|
||||
*/
|
||||
Hash_set();
|
||||
/**
|
||||
Destroy the hash by freeing the buckets table. Does
|
||||
not call destructors for the elements.
|
||||
*/
|
||||
~Hash_set();
|
||||
/**
|
||||
Insert a single value into a hash. Does not tell whether
|
||||
the value was inserted -- if an identical value existed,
|
||||
it is not replaced.
|
||||
|
||||
@retval TRUE Out of memory.
|
||||
@retval FALSE OK. The value either was inserted or existed
|
||||
in the hash.
|
||||
*/
|
||||
bool insert(T *value);
|
||||
/** Is this hash set empty? */
|
||||
bool is_empty() const { return m_hash.records == 0; }
|
||||
/** Returns the number of unique elements. */
|
||||
size_t size() const { return static_cast<size_t>(m_hash.records); }
|
||||
/** An iterator over hash elements. Is not insert-stable. */
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
Iterator(Hash_set &hash_set);
|
||||
/**
|
||||
Return the current element and reposition the iterator to the next
|
||||
element.
|
||||
*/
|
||||
inline T *operator++(int);
|
||||
void rewind() { m_idx= 0; }
|
||||
private:
|
||||
HASH *m_hash;
|
||||
uint m_idx;
|
||||
};
|
||||
private:
|
||||
HASH m_hash;
|
||||
};
|
||||
|
||||
|
||||
template <typename T, my_hash_get_key K>
|
||||
Hash_set<T, K>::Hash_set()
|
||||
{
|
||||
my_hash_clear(&m_hash);
|
||||
}
|
||||
|
||||
|
||||
template <typename T, my_hash_get_key K>
|
||||
Hash_set<T, K>::~Hash_set()
|
||||
{
|
||||
my_hash_free(&m_hash);
|
||||
}
|
||||
|
||||
|
||||
template <typename T, my_hash_get_key K>
|
||||
bool Hash_set<T, K>::insert(T *value)
|
||||
{
|
||||
my_hash_init_opt(&m_hash, &my_charset_bin, START_SIZE, 0, 0, K, 0, MYF(0));
|
||||
size_t key_len;
|
||||
const uchar *key= K(reinterpret_cast<uchar*>(value), &key_len, FALSE);
|
||||
if (my_hash_search(&m_hash, key, key_len) == NULL)
|
||||
return my_hash_insert(&m_hash, reinterpret_cast<uchar *>(value));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, my_hash_get_key K>
|
||||
Hash_set<T, K>::Iterator::Iterator(Hash_set<T, K> &set_arg)
|
||||
:m_hash(&set_arg.m_hash),
|
||||
m_idx(0)
|
||||
{}
|
||||
|
||||
|
||||
template <typename T, my_hash_get_key K>
|
||||
inline T *Hash_set<T, K>::Iterator::operator++(int)
|
||||
{
|
||||
if (m_idx < m_hash->records)
|
||||
return reinterpret_cast<T*>(my_hash_element(m_hash, m_idx++));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // SQL_HSET_INCLUDED
|
@ -270,10 +270,10 @@ void init_update_queries(void)
|
||||
sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
|
||||
CF_PROTECT_AGAINST_GRL |
|
||||
CF_CAN_GENERATE_ROW_EVENTS;
|
||||
sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
|
||||
sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
|
||||
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
|
||||
sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
|
||||
sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
|
||||
sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
|
||||
@ -1803,7 +1803,8 @@ static bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
|
||||
current internal MDL asserts, fix after discussing with
|
||||
Dmitry.
|
||||
*/
|
||||
if (lock_table_names(thd, all_tables))
|
||||
if (lock_table_names(thd, all_tables, 0, thd->variables.lock_wait_timeout,
|
||||
MYSQL_OPEN_SKIP_TEMPORARY))
|
||||
goto error;
|
||||
|
||||
for (table_list= all_tables; table_list;
|
||||
@ -3644,12 +3645,6 @@ end_with_restore_list:
|
||||
#endif
|
||||
if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
|
||||
break;
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
||||
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
||||
goto error;
|
||||
}
|
||||
res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
|
||||
lex->name.str), &create_info, 0);
|
||||
break;
|
||||
@ -3679,12 +3674,6 @@ end_with_restore_list:
|
||||
#endif
|
||||
if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
|
||||
break;
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
||||
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
||||
goto error;
|
||||
}
|
||||
res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
|
||||
break;
|
||||
}
|
||||
@ -3713,14 +3702,6 @@ end_with_restore_list:
|
||||
res= 1;
|
||||
break;
|
||||
}
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
res= 1;
|
||||
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
||||
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
||||
goto error;
|
||||
}
|
||||
|
||||
res= mysql_upgrade_db(thd, db);
|
||||
if (!res)
|
||||
my_ok(thd);
|
||||
@ -3753,12 +3734,6 @@ end_with_restore_list:
|
||||
#endif
|
||||
if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0))
|
||||
break;
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
||||
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
||||
goto error;
|
||||
}
|
||||
res= mysql_alter_db(thd, db->str, &create_info);
|
||||
break;
|
||||
}
|
||||
|
@ -24,10 +24,9 @@
|
||||
#include "sql_table.h" // build_table_filename
|
||||
#include "sql_view.h" // mysql_frm_type, mysql_rename_view
|
||||
#include "sql_trigger.h"
|
||||
#include "lock.h" // wait_if_global_read_lock, lock_table_names,
|
||||
// unlock_table_names,
|
||||
#include "lock.h" // wait_if_global_read_lock
|
||||
// start_waiting_global_read_lock
|
||||
#include "sql_base.h" // tdc_remove_table
|
||||
#include "sql_base.h" // tdc_remove_table, lock_table_names,
|
||||
#include "sql_handler.h" // mysql_ha_rm_tables
|
||||
#include "datadict.h"
|
||||
|
||||
@ -144,7 +143,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
|
||||
}
|
||||
}
|
||||
|
||||
if (lock_table_names(thd, table_list))
|
||||
if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout,
|
||||
MYSQL_OPEN_SKIP_TEMPORARY))
|
||||
goto err;
|
||||
|
||||
mysql_mutex_lock(&LOCK_open);
|
||||
@ -197,7 +197,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
|
||||
if (!error)
|
||||
query_cache_invalidate3(thd, table_list, 0);
|
||||
|
||||
unlock_table_names(thd);
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
|
||||
err:
|
||||
thd->global_read_lock.start_waiting_global_read_lock(thd);
|
||||
|
@ -22,17 +22,17 @@
|
||||
#include "sql_rename.h" // do_rename
|
||||
#include "sql_parse.h" // test_if_data_home_dir
|
||||
#include "sql_cache.h" // query_cache_*
|
||||
#include "sql_base.h" // open_temporary_table
|
||||
#include "lock.h" // wait_if_global_read_lock, lock_table_names,
|
||||
#include "sql_base.h" // open_temporary_table, lock_table_names
|
||||
#include "lock.h" // wait_if_global_read_lock
|
||||
// start_waiting_global_read_lock,
|
||||
// unlock_table_names, mysql_unlock_tables
|
||||
// mysql_unlock_tables
|
||||
#include "strfunc.h" // find_type2, find_set
|
||||
#include "sql_view.h" // view_checksum
|
||||
#include "sql_truncate.h" // regenerate_locked_table
|
||||
#include "sql_partition.h" // mem_alloc_error,
|
||||
// generate_partition_syntax,
|
||||
// partition_info
|
||||
#include "sql_db.h" // load_db_opt_by_name, lock_db_cache, creating_database
|
||||
#include "sql_db.h" // load_db_opt_by_name
|
||||
#include "sql_time.h" // make_truncated_value_warning
|
||||
#include "records.h" // init_read_record, end_read_record
|
||||
#include "filesort.h" // filesort_free_buffers
|
||||
@ -58,8 +58,6 @@
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
int creating_table= 0; // How many mysql_create_table are running
|
||||
|
||||
const char *primary_key_name="PRIMARY";
|
||||
|
||||
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
|
||||
@ -1954,7 +1952,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
{
|
||||
if (!thd->locked_tables_mode)
|
||||
{
|
||||
if (lock_table_names(thd, tables))
|
||||
if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
|
||||
MYSQL_OPEN_SKIP_TEMPORARY))
|
||||
DBUG_RETURN(1);
|
||||
mysql_mutex_lock(&LOCK_open);
|
||||
for (table= tables; table; table= table->next_local)
|
||||
@ -2296,7 +2295,7 @@ err:
|
||||
leaving LOCK TABLES mode if we have dropped only temporary tables.
|
||||
*/
|
||||
if (! thd->locked_tables_mode)
|
||||
unlock_table_names(thd);
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
else
|
||||
{
|
||||
if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0)
|
||||
@ -4226,24 +4225,6 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
|
||||
bool result;
|
||||
DBUG_ENTER("mysql_create_table");
|
||||
|
||||
/* Wait for any database locks */
|
||||
mysql_mutex_lock(&LOCK_lock_db);
|
||||
while (!thd->killed &&
|
||||
my_hash_search(&lock_db_cache, (uchar*)create_table->db,
|
||||
create_table->db_length))
|
||||
{
|
||||
wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
|
||||
mysql_mutex_lock(&LOCK_lock_db);
|
||||
}
|
||||
|
||||
if (thd->killed)
|
||||
{
|
||||
mysql_mutex_unlock(&LOCK_lock_db);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
creating_table++;
|
||||
mysql_mutex_unlock(&LOCK_lock_db);
|
||||
|
||||
/*
|
||||
Open or obtain an exclusive metadata lock on table being created.
|
||||
*/
|
||||
@ -4282,10 +4263,6 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
|
||||
}
|
||||
|
||||
unlock:
|
||||
mysql_mutex_lock(&LOCK_lock_db);
|
||||
if (!--creating_table && creating_database)
|
||||
mysql_cond_signal(&COND_refresh);
|
||||
mysql_mutex_unlock(&LOCK_lock_db);
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
@ -4458,8 +4435,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
|
||||
{
|
||||
char key[MAX_DBKEY_LENGTH];
|
||||
uint key_length;
|
||||
MDL_request mdl_global_request;
|
||||
MDL_request_list mdl_requests;
|
||||
/*
|
||||
If the table didn't exist, we have a shared metadata lock
|
||||
on it that is left from mysql_admin_table()'s attempt to
|
||||
@ -4479,12 +4454,9 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
|
||||
table_list->db, table_list->table_name,
|
||||
MDL_EXCLUSIVE);
|
||||
|
||||
mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
|
||||
mdl_requests.push_front(&table_list->mdl_request);
|
||||
mdl_requests.push_front(&mdl_global_request);
|
||||
|
||||
if (thd->mdl_context.acquire_locks(&mdl_requests,
|
||||
thd->variables.lock_wait_timeout))
|
||||
if (lock_table_names(thd, table_list, table_list->next_global,
|
||||
thd->variables.lock_wait_timeout,
|
||||
MYSQL_OPEN_SKIP_TEMPORARY))
|
||||
DBUG_RETURN(0);
|
||||
has_mdl_lock= TRUE;
|
||||
|
||||
@ -6577,6 +6549,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
|
||||
Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info);
|
||||
|
||||
DEBUG_SYNC(thd, "alter_table_before_open_tables");
|
||||
error= open_and_lock_tables(thd, table_list, FALSE, 0,
|
||||
&alter_prelocking_strategy);
|
||||
|
||||
|
@ -210,7 +210,6 @@ uint explain_filename(THD* thd, const char *from, char *to, uint to_length,
|
||||
|
||||
|
||||
extern MYSQL_PLUGIN_IMPORT const char *primary_key_name;
|
||||
extern int creating_table; // How many mysql_create_table() are running
|
||||
extern mysql_mutex_t LOCK_gdl;
|
||||
|
||||
#endif /* SQL_TABLE_INCLUDED */
|
||||
|
@ -242,9 +242,10 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
MDL_ticket **ticket_downgrade)
|
||||
{
|
||||
TABLE *table= NULL;
|
||||
MDL_ticket *mdl_ticket= NULL;
|
||||
DBUG_ENTER("open_and_lock_table_for_truncate");
|
||||
|
||||
DBUG_ASSERT(table_ref->lock_type == TL_WRITE);
|
||||
DBUG_ASSERT(table_ref->mdl_request.type == MDL_SHARED_NO_READ_WRITE);
|
||||
/*
|
||||
Before doing anything else, acquire a metadata lock on the table,
|
||||
or ensure we have one. We don't use open_and_lock_tables()
|
||||
@ -266,6 +267,7 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
|
||||
*hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(),
|
||||
HTON_CAN_RECREATE);
|
||||
table_ref->mdl_request.ticket= table->mdl_ticket;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -273,21 +275,12 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
Even though we could use the previous execution branch here just as
|
||||
well, we must not try to open the table:
|
||||
*/
|
||||
MDL_request mdl_global_request, mdl_request;
|
||||
MDL_request_list mdl_requests;
|
||||
|
||||
mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
|
||||
mdl_request.init(MDL_key::TABLE, table_ref->db, table_ref->table_name,
|
||||
MDL_SHARED_NO_READ_WRITE);
|
||||
mdl_requests.push_front(&mdl_request);
|
||||
mdl_requests.push_front(&mdl_global_request);
|
||||
|
||||
if (thd->mdl_context.acquire_locks(&mdl_requests,
|
||||
thd->variables.lock_wait_timeout))
|
||||
DBUG_ASSERT(table_ref->next_global == NULL);
|
||||
if (lock_table_names(thd, table_ref, NULL,
|
||||
thd->variables.lock_wait_timeout,
|
||||
MYSQL_OPEN_SKIP_TEMPORARY))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
mdl_ticket= mdl_request.ticket;
|
||||
|
||||
if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name,
|
||||
HTON_CAN_RECREATE, hton_can_recreate))
|
||||
DBUG_RETURN(TRUE);
|
||||
@ -313,7 +306,9 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
else
|
||||
{
|
||||
ulong timeout= thd->variables.lock_wait_timeout;
|
||||
if (thd->mdl_context.upgrade_shared_lock_to_exclusive(mdl_ticket, timeout))
|
||||
if (thd->mdl_context.
|
||||
upgrade_shared_lock_to_exclusive(table_ref->mdl_request.ticket,
|
||||
timeout))
|
||||
DBUG_RETURN(TRUE);
|
||||
mysql_mutex_lock(&LOCK_open);
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db,
|
||||
@ -335,15 +330,14 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
table_ref->required_type= FRMTYPE_TABLE;
|
||||
/* We don't need to load triggers. */
|
||||
DBUG_ASSERT(table_ref->trg_event_map == 0);
|
||||
/* Work around partition parser rules using alter table's. */
|
||||
if (thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
|
||||
{
|
||||
table_ref->lock_type= TL_WRITE;
|
||||
table_ref->mdl_request.set_type(MDL_SHARED_WRITE);
|
||||
}
|
||||
/* Ensure proper lock types (e.g. from the parser). */
|
||||
DBUG_ASSERT(table_ref->lock_type == TL_WRITE);
|
||||
DBUG_ASSERT(table_ref->mdl_request.type == MDL_SHARED_WRITE);
|
||||
/*
|
||||
Even though we have an MDL lock on the table here, we don't
|
||||
pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables
|
||||
since to truncate a MERGE table, we must open and lock
|
||||
merge children, and on those we don't have an MDL lock.
|
||||
Thus clear the ticket to satisfy MDL asserts.
|
||||
*/
|
||||
table_ref->mdl_request.ticket= NULL;
|
||||
|
||||
/*
|
||||
Open the table as it will handle some required preparations.
|
||||
|
@ -19,10 +19,10 @@
|
||||
#include "sql_priv.h"
|
||||
#include "unireg.h"
|
||||
#include "sql_view.h"
|
||||
#include "sql_base.h" // find_table_in_global_list
|
||||
#include "sql_base.h" // find_table_in_global_list, lock_table_names
|
||||
#include "sql_parse.h" // sql_parse
|
||||
#include "sql_cache.h" // query_cache_*
|
||||
#include "lock.h" // wait_if_global_read_lock, lock_table_names
|
||||
#include "lock.h" // wait_if_global_read_lock
|
||||
#include "sql_show.h" // append_identifier
|
||||
#include "sql_table.h" // build_table_filename
|
||||
#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
|
||||
@ -1652,7 +1652,8 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
if (lock_table_names(thd, views))
|
||||
if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout,
|
||||
MYSQL_OPEN_SKIP_TEMPORARY))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
mysql_mutex_lock(&LOCK_open);
|
||||
|
@ -6442,6 +6442,8 @@ alter_commands:
|
||||
lex->sql_command= SQLCOM_TRUNCATE;
|
||||
lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
|
||||
lex->check_opt.init();
|
||||
lex->query_tables->mdl_request.set_type(MDL_SHARED_NO_READ_WRITE);
|
||||
lex->query_tables->lock_type= TL_WRITE;
|
||||
}
|
||||
| reorg_partition_rule
|
||||
;
|
||||
@ -10695,7 +10697,7 @@ truncate:
|
||||
lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
|
||||
lex->select_lex.init_order();
|
||||
YYPS->m_lock_type= TL_WRITE;
|
||||
YYPS->m_mdl_type= MDL_SHARED_WRITE;
|
||||
YYPS->m_mdl_type= MDL_SHARED_NO_READ_WRITE;
|
||||
}
|
||||
table_name
|
||||
{}
|
||||
|
Loading…
x
Reference in New Issue
Block a user