diff options
39 files changed, 1490 insertions, 993 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c index 0f30ebddacc..b77549df781 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -3726,11 +3726,12 @@ static int dump_all_tables_in_db(char *database) { DYNAMIC_STRING query; init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024); - for (numrows= 0 ; (table= getTableName(1)) ; numrows++) + for (numrows= 0 ; (table= getTableName(1)) ; ) { char *end= strmov(afterdot, table); if (include_table(hash_key,end - hash_key)) { + numrows++; dynstr_append_checked(&query, quote_name(table, table_buff, 1)); dynstr_append_checked(&query, " READ /*!32311 LOCAL */,"); } @@ -3804,6 +3805,11 @@ static my_bool dump_all_views_in_db(char *database) char *table; uint numrows; char table_buff[NAME_LEN*2+3]; + char hash_key[2*NAME_LEN+2]; /* "db.tablename" */ + char *afterdot; + + afterdot= strmov(hash_key, database); + *afterdot++= '.'; if (init_dumping(database, init_dumping_views)) return 1; @@ -3813,10 +3819,15 @@ static my_bool dump_all_views_in_db(char *database) { DYNAMIC_STRING query; init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024); - for (numrows= 0 ; (table= getTableName(1)); numrows++) + for (numrows= 0 ; (table= getTableName(1)); ) { - dynstr_append_checked(&query, quote_name(table, table_buff, 1)); - dynstr_append_checked(&query, " READ /*!32311 LOCAL */,"); + char *end= strmov(afterdot, table); + if (include_table(hash_key,end - hash_key)) + { + numrows++; + dynstr_append_checked(&query, quote_name(table, table_buff, 1)); + dynstr_append_checked(&query, " READ /*!32311 LOCAL */,"); + } } if (numrows && mysql_real_query(mysql, query.str, query.length-1)) DB_error(mysql, "when using LOCK TABLES"); @@ -3830,7 +3841,11 @@ static my_bool dump_all_views_in_db(char *database) /* We shall continue here, if --force was given */ } while ((table= getTableName(0))) - get_view_structure(table, database); + { + char *end= strmov(afterdot, table); + if (include_table(hash_key, end - hash_key)) + get_view_structure(table, database); + } if (opt_xml) { fputs("</database>\n", md_result_file); diff --git a/mysql-test/r/log_state.result b/mysql-test/r/log_state.result index 946797aeec7..0c6be16b9b7 100644 --- a/mysql-test/r/log_state.result +++ b/mysql-test/r/log_state.result @@ -131,7 +131,7 @@ set global general_log=ON; set global log_output=default; show variables like 'log_output'; Variable_name Value -log_output TABLE +log_output FILE set global general_log=OFF; set global log_output=FILE; truncate table mysql.general_log; @@ -153,3 +153,25 @@ select * from mysql.general_log; event_time user_host thread_id server_id command_type argument TIMESTAMP USER_HOST # 1 Query drop table t1 TIMESTAMP USER_HOST # 1 Query select * from mysql.general_log +SET @old_general_log_state = @@global.general_log; +SET @old_slow_log_state = @@global.slow_query_log; +SET GLOBAL general_log = ON; +SET GLOBAL slow_query_log = ON; +FLUSH TABLES WITH READ LOCK; +SET GLOBAL general_log = OFF; +SET GLOBAL slow_query_log = OFF; +UNLOCK TABLES; +FLUSH TABLES WITH READ LOCK; +SET GLOBAL general_log = ON; +SET GLOBAL slow_query_log = ON; +UNLOCK TABLES; +SET GLOBAL READ_ONLY = ON; +SET GLOBAL general_log = OFF; +SET GLOBAL slow_query_log = OFF; +SET GLOBAL READ_ONLY = OFF; +SET GLOBAL READ_ONLY = ON; +SET GLOBAL general_log = ON; +SET GLOBAL slow_query_log = ON; +SET GLOBAL READ_ONLY = OFF; +SET GLOBAL general_log = @old_general_log_state; +SET GLOBAL slow_query_log = @old_slow_log_state; diff --git a/mysql-test/r/log_tables.result b/mysql-test/r/log_tables.result index ce3dabe3a56..39349183276 100644 --- a/mysql-test/r/log_tables.result +++ b/mysql-test/r/log_tables.result @@ -29,30 +29,83 @@ on (mysql.general_log.command_type = join_test.command_type) drop table join_test; flush logs; lock tables mysql.general_log WRITE; -ERROR HY000: You can't write-lock a log table. Only read access is possible +ERROR HY000: You can't use locks with log tables. lock tables mysql.slow_log WRITE; -ERROR HY000: You can't write-lock a log table. Only read access is possible +ERROR HY000: You can't use locks with log tables. lock tables mysql.general_log READ; -ERROR HY000: You can't use usual read lock with log tables. Try READ LOCAL instead +ERROR HY000: You can't use locks with log tables. lock tables mysql.slow_log READ; -ERROR HY000: You can't use usual read lock with log tables. Try READ LOCAL instead +ERROR HY000: You can't use locks with log tables. lock tables mysql.slow_log READ LOCAL, mysql.general_log READ LOCAL; -unlock tables; -lock tables mysql.general_log READ LOCAL; +ERROR HY000: You can't use locks with log tables. +show create table mysql.general_log; +Table Create Table +general_log CREATE TABLE `general_log` ( + `event_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `user_host` mediumtext, + `thread_id` int(11) DEFAULT NULL, + `server_id` int(11) DEFAULT NULL, + `command_type` varchar(64) DEFAULT NULL, + `argument` mediumtext +) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='General log' +show fields from mysql.general_log; +Field Type Null Key Default Extra +event_time timestamp NO CURRENT_TIMESTAMP +user_host mediumtext YES NULL +thread_id int(11) YES NULL +server_id int(11) YES NULL +command_type varchar(64) YES NULL +argument mediumtext YES NULL +show create table mysql.slow_log; +Table Create Table +slow_log CREATE TABLE `slow_log` ( + `start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `user_host` mediumtext NOT NULL, + `query_time` time NOT NULL, + `lock_time` time NOT NULL, + `rows_sent` int(11) NOT NULL, + `rows_examined` int(11) NOT NULL, + `db` varchar(512) DEFAULT NULL, + `last_insert_id` int(11) DEFAULT NULL, + `insert_id` int(11) DEFAULT NULL, + `server_id` int(11) DEFAULT NULL, + `sql_text` mediumtext NOT NULL +) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' +show fields from mysql.slow_log; +Field Type Null Key Default Extra +start_time timestamp NO CURRENT_TIMESTAMP +user_host mediumtext NO +query_time time NO +lock_time time NO +rows_sent int(11) NO +rows_examined int(11) NO +db varchar(512) YES NULL +last_insert_id int(11) YES NULL +insert_id int(11) YES NULL +server_id int(11) YES NULL +sql_text mediumtext NO flush logs; -unlock tables; -select "Mark that we woke up from flush logs in the test" - as "test passed"; -test passed -Mark that we woke up from flush logs in the test -lock tables mysql.general_log READ LOCAL; -truncate mysql.general_log; -unlock tables; -select "Mark that we woke up from TRUNCATE in the test" - as "test passed"; -test passed -Mark that we woke up from TRUNCATE in the test -use test; +flush tables; +SET GLOBAL GENERAL_LOG=ON; +SET GLOBAL SLOW_QUERY_LOG=ON; +show open tables; +Database Table In_use Name_locked +mysql general_log 0 0 +flush logs; +show open tables; +Database Table In_use Name_locked +mysql general_log 0 0 +flush tables; +show open tables; +Database Table In_use Name_locked +mysql general_log 0 0 +SET GLOBAL GENERAL_LOG=OFF; +SET GLOBAL SLOW_QUERY_LOG=OFF; +flush tables; +show open tables; +Database Table In_use Name_locked +SET GLOBAL GENERAL_LOG=ON; +SET GLOBAL SLOW_QUERY_LOG=ON; truncate table mysql.general_log; set names utf8; create table bug16905 (s char(15) character set utf8 default 'пуÑто'); @@ -71,7 +124,7 @@ sleep(2) 0 select * from mysql.slow_log; start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text -TIMESTAMP USER_HOST QUERY_TIME 00:00:00 1 0 test 0 0 1 select sleep(2) +TIMESTAMP USER_HOST QUERY_TIME 00:00:00 1 0 mysql 0 0 1 select sleep(2) alter table mysql.general_log engine=myisam; ERROR HY000: You cannot 'ALTER' a log table if logging is enabled alter table mysql.slow_log engine=myisam; @@ -158,15 +211,13 @@ TIMESTAMP USER_HOST THREAD_ID 1 Query set global slow_query_log='ON' TIMESTAMP USER_HOST THREAD_ID 1 Query select * from mysql.general_log flush logs; lock tables mysql.general_log WRITE; -ERROR HY000: You can't write-lock a log table. Only read access is possible +ERROR HY000: You can't use locks with log tables. lock tables mysql.slow_log WRITE; -ERROR HY000: You can't write-lock a log table. Only read access is possible +ERROR HY000: You can't use locks with log tables. lock tables mysql.general_log READ; -ERROR HY000: You can't use usual read lock with log tables. Try READ LOCAL instead +ERROR HY000: You can't use locks with log tables. lock tables mysql.slow_log READ; -ERROR HY000: You can't use usual read lock with log tables. Try READ LOCAL instead -lock tables mysql.slow_log READ LOCAL, mysql.general_log READ LOCAL; -unlock tables; +ERROR HY000: You can't use locks with log tables. set global general_log='OFF'; set global slow_query_log='OFF'; set @save_storage_engine= @@session.storage_engine; @@ -217,6 +268,7 @@ flush tables with read lock; unlock tables; use mysql; lock tables general_log read local, help_category read local; +ERROR HY000: You can't use locks with log tables. unlock tables; use mysql; RENAME TABLE general_log TO renamed_general_log; @@ -258,9 +310,9 @@ RENAME TABLE general_log TO general_log2; set global slow_query_log='OFF'; RENAME TABLE slow_log TO slow_log2; set global general_log='ON'; -ERROR HY000: Cannot activate 'general' log +ERROR 42S02: Table 'mysql.general_log' doesn't exist set global slow_query_log='ON'; -ERROR HY000: Cannot activate 'slow query' log +ERROR 42S02: Table 'mysql.slow_log' doesn't exist RENAME TABLE general_log2 TO general_log; RENAME TABLE slow_log2 TO slow_log; set global general_log='ON'; @@ -355,3 +407,192 @@ SET SESSION long_query_time =@old_long_query_time; FLUSH LOGS; ALTER TABLE mysql.slow_log DROP COLUMN seq; ALTER TABLE mysql.slow_log ENGINE = CSV; +drop procedure if exists proc25422_truncate_slow; +drop procedure if exists proc25422_truncate_general; +drop procedure if exists proc25422_alter_slow; +drop procedure if exists proc25422_alter_general; +use test// +create procedure proc25422_truncate_slow (loops int) +begin +declare v1 int default 0; +while v1 < loops do +truncate mysql.slow_log; +set v1 = v1 + 1; +end while; +end// +create procedure proc25422_truncate_general (loops int) +begin +declare v1 int default 0; +while v1 < loops do +truncate mysql.general_log; +set v1 = v1 + 1; +end while; +end// +create procedure proc25422_alter_slow (loops int) +begin +declare v1 int default 0; +declare ER_BAD_LOG_STATEMENT condition for 1575; +declare continue handler for ER_BAD_LOG_STATEMENT begin end; +while v1 < loops do +set @old_log_state = @@global.slow_query_log; +set global slow_query_log = 'OFF'; +alter table mysql.slow_log engine = CSV; +set global slow_query_log = @old_log_state; +set v1 = v1 + 1; +end while; +end// +create procedure proc25422_alter_general (loops int) +begin +declare v1 int default 0; +declare ER_BAD_LOG_STATEMENT condition for 1575; +declare continue handler for ER_BAD_LOG_STATEMENT begin end; +while v1 < loops do +set @old_log_state = @@global.general_log; +set global general_log = 'OFF'; +alter table mysql.general_log engine = CSV; +set global general_log = @old_log_state; +set v1 = v1 + 1; +end while; +end// +"Serial test (proc25422_truncate_slow)" +call proc25422_truncate_slow(100); +"Serial test (proc25422_truncate_general)" +call proc25422_truncate_general(100); +"Serial test (proc25422_alter_slow)" +call proc25422_alter_slow(100); +"Serial test (proc25422_alter_general)" +call proc25422_alter_general(100); +"Parallel test" +call proc25422_truncate_slow(100); +call proc25422_truncate_slow(100); +call proc25422_truncate_general(100); +call proc25422_truncate_general(100); +call proc25422_alter_slow(100); +call proc25422_alter_slow(100); +call proc25422_alter_general(100); +call proc25422_alter_general(100); +drop procedure proc25422_truncate_slow; +drop procedure proc25422_truncate_general; +drop procedure proc25422_alter_slow; +drop procedure proc25422_alter_general; +FLUSH TABLE mysql.general_log; +show warnings; +Level Code Message +FLUSH TABLE mysql.slow_log; +show warnings; +Level Code Message +DROP TABLE IF EXISTS `db_17876.slow_log_data`; +DROP TABLE IF EXISTS `db_17876.general_log_data`; +DROP PROCEDURE IF EXISTS `db_17876.archiveSlowLog`; +DROP PROCEDURE IF EXISTS `db_17876.archiveGeneralLog`; +DROP DATABASE IF EXISTS `db_17876`; +CREATE DATABASE db_17876; +CREATE TABLE `db_17876.slow_log_data` ( +`start_time` timestamp default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, +`user_host` mediumtext , +`query_time` time , +`lock_time` time , +`rows_sent` int(11) , +`rows_examined` int(11) , +`db` varchar(512) default NULL, +`last_insert_id` int(11) default NULL, +`insert_id` int(11) default NULL, +`server_id` int(11) default NULL, +`sql_text` mediumtext +); +CREATE TABLE `db_17876.general_log_data` ( +`event_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, +`user_host` mediumtext, +`thread_id` int(11) DEFAULT NULL, +`server_id` int(11) DEFAULT NULL, +`command_type` varchar(64) DEFAULT NULL, +`argument` mediumtext +); +CREATE procedure `db_17876.archiveSlowLog`() +BEGIN +DECLARE start_time, query_time, lock_time CHAR(20); +DECLARE user_host MEDIUMTEXT; +DECLARE rows_set, rows_examined, last_insert_id, insert_id, server_id INT; +DECLARE dbname MEDIUMTEXT; +DECLARE sql_text BLOB; +DECLARE done INT DEFAULT 0; +DECLARE ER_SP_FETCH_NO_DATA CONDITION for 1329; +DECLARE cur1 CURSOR FOR SELECT * FROM mysql.slow_log; +OPEN cur1; +REPEAT +BEGIN +BEGIN +DECLARE CONTINUE HANDLER FOR ER_SP_FETCH_NO_DATA SET done = 1; +FETCH cur1 INTO +start_time, user_host, query_time, lock_time, +rows_set, rows_examined, dbname, last_insert_id, +insert_id, server_id, sql_text; +END; +IF NOT done THEN +BEGIN +INSERT INTO +`db_17876.slow_log_data` + VALUES(start_time, user_host, query_time, lock_time, rows_set, rows_examined, +dbname, last_insert_id, insert_id, server_id, sql_text); +END; +END IF; +END; +UNTIL done END REPEAT; +CLOSE cur1; +TRUNCATE mysql.slow_log; +END // +CREATE procedure `db_17876.archiveGeneralLog`() +BEGIN +DECLARE event_time CHAR(20); +DECLARE user_host, argument MEDIUMTEXT; +DECLARE thread_id, server_id INT; +DECLARE sql_text BLOB; +DECLARE done INT DEFAULT 0; +DECLARE command_type VARCHAR(64); +DECLARE ER_SP_FETCH_NO_DATA CONDITION for 1329; +DECLARE cur1 CURSOR FOR SELECT * FROM mysql.general_log; +OPEN cur1; +REPEAT +BEGIN +BEGIN +DECLARE CONTINUE HANDLER FOR ER_SP_FETCH_NO_DATA SET done = 1; +FETCH cur1 INTO +event_time, user_host, thread_id, server_id, +command_type, argument; +END; +IF NOT done THEN +BEGIN +INSERT INTO +`db_17876.general_log_data` + VALUES(event_time, user_host, thread_id, server_id, +command_type, argument); +END; +END IF; +END; +UNTIL done END REPEAT; +CLOSE cur1; +TRUNCATE mysql.general_log; +END // +SET @old_general_log_state = @@global.general_log; +SET @old_slow_log_state = @@global.slow_query_log; +SET GLOBAL general_log = ON; +SET GLOBAL slow_query_log = ON; +select "put something into general_log"; +put something into general_log +put something into general_log +select "... and something more ..."; +... and something more ... +... and something more ... +call `db_17876.archiveSlowLog`(); +call `db_17876.archiveGeneralLog`(); +SET GLOBAL general_log = OFF; +SET GLOBAL slow_query_log = OFF; +call `db_17876.archiveSlowLog`(); +call `db_17876.archiveGeneralLog`(); +DROP TABLE `db_17876.slow_log_data`; +DROP TABLE `db_17876.general_log_data`; +DROP PROCEDURE IF EXISTS `db_17876.archiveSlowLog`; +DROP PROCEDURE IF EXISTS `db_17876.archiveGeneralLog`; +DROP DATABASE IF EXISTS `db_17876`; +SET GLOBAL general_log = @old_general_log_state; +SET GLOBAL slow_query_log = @old_slow_log_state; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 9214d470b37..341a6b7cd43 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1850,61 +1850,57 @@ create procedure proc_1() flush tables; flush tables; show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 select Host, User from mysql.user limit 0; Host User select Host, Db from mysql.host limit 0; Host Db show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 +mysql slow_log 0 0 mysql host 0 0 mysql user 0 0 call proc_1(); show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 select Host, User from mysql.user limit 0; Host User select Host, Db from mysql.host limit 0; Host Db show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 +mysql slow_log 0 0 mysql host 0 0 mysql user 0 0 call proc_1(); show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 select Host, User from mysql.user limit 0; Host User select Host, Db from mysql.host limit 0; Host Db show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 +mysql slow_log 0 0 mysql host 0 0 mysql user 0 0 call proc_1(); show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 select Host, User from mysql.user limit 0; Host User select Host, Db from mysql.host limit 0; Host Db show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 +mysql slow_log 0 0 mysql host 0 0 mysql user 0 0 flush tables; @@ -1922,54 +1918,50 @@ select Host, Db from mysql.host limit 0; Host Db show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 -mysql host 0 0 mysql user 0 0 +mysql general_log 0 0 +mysql host 0 0 prepare abc from "flush tables"; execute abc; show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 select Host, User from mysql.user limit 0; Host User select Host, Db from mysql.host limit 0; Host Db show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 +mysql slow_log 0 0 mysql host 0 0 mysql user 0 0 execute abc; show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 select Host, User from mysql.user limit 0; Host User select Host, Db from mysql.host limit 0; Host Db show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 +mysql slow_log 0 0 mysql host 0 0 mysql user 0 0 execute abc; show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 select Host, User from mysql.user limit 0; Host User select Host, Db from mysql.host limit 0; Host Db show open tables from mysql; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 +mysql slow_log 0 0 mysql host 0 0 mysql user 0 0 flush tables; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 2bdd29602fb..9f79c34cd2c 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -251,14 +251,13 @@ drop table t1; flush tables; show open tables; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 create table t1(n int); insert into t1 values (1); show open tables; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 +mysql slow_log 0 0 test t1 0 0 drop table t1; create table t1 (a int not null, b VARCHAR(10), INDEX (b) ) AVG_ROW_LENGTH=10 CHECKSUM=1 COMMENT="test" ENGINE=MYISAM MIN_ROWS=10 MAX_ROWS=100 PACK_KEYS=1 DELAY_KEY_WRITE=1 ROW_FORMAT=fixed; @@ -672,24 +671,23 @@ SELECT 1 FROM mysql.db, mysql.proc, mysql.user, mysql.time_zone, mysql.time_zone 1 SHOW OPEN TABLES; Database Table In_use Name_locked -mysql proc 0 0 +mysql db 0 0 test urkunde 0 0 mysql time_zone 0 0 -mysql db 0 0 +mysql general_log 0 0 test txt1 0 0 -mysql slow_log 1 0 +mysql proc 0 0 test tyt2 0 0 -mysql general_log 1 0 mysql user 0 0 mysql time_zone_name 0 0 SHOW OPEN TABLES FROM mysql; Database Table In_use Name_locked -mysql proc 0 0 -mysql time_zone 0 0 mysql db 0 0 -mysql slow_log 1 0 -mysql general_log 1 0 +mysql time_zone 0 0 +mysql general_log 0 0 +mysql slow_log 0 0 mysql user 0 0 +mysql proc 0 0 mysql time_zone_name 0 0 SHOW OPEN TABLES FROM mysql LIKE 'u%'; Database Table In_use Name_locked @@ -702,16 +700,15 @@ test tyt2 0 0 mysql time_zone_name 0 0 SHOW OPEN TABLES LIKE '%o%'; Database Table In_use Name_locked -mysql proc 0 0 mysql time_zone 0 0 -mysql slow_log 1 0 -mysql general_log 1 0 +mysql general_log 0 0 +mysql slow_log 0 0 +mysql proc 0 0 mysql time_zone_name 0 0 FLUSH TABLES; SHOW OPEN TABLES; Database Table In_use Name_locked -mysql general_log 1 0 -mysql slow_log 1 0 +mysql general_log 0 0 DROP TABLE txt1; DROP TABLE tyt2; DROP TABLE urkunde; diff --git a/mysql-test/r/status.result b/mysql-test/r/status.result index b6dcbc251d7..8f10625744b 100644 --- a/mysql-test/r/status.result +++ b/mysql-test/r/status.result @@ -1,11 +1,11 @@ flush status; show status like 'Table_lock%'; Variable_name Value -Table_locks_immediate 0 +Table_locks_immediate 1 Table_locks_waited 0 select * from information_schema.session_status where variable_name like 'Table_lock%'; VARIABLE_NAME VARIABLE_VALUE -TABLE_LOCKS_IMMEDIATE 0 +TABLE_LOCKS_IMMEDIATE 2 TABLE_LOCKS_WAITED 0 SET SQL_LOG_BIN=0; drop table if exists t1; @@ -18,11 +18,11 @@ update t1 set n = 3; unlock tables; show status like 'Table_lock%'; Variable_name Value -Table_locks_immediate 3 +Table_locks_immediate 17 Table_locks_waited 1 select * from information_schema.session_status where variable_name like 'Table_lock%'; VARIABLE_NAME VARIABLE_VALUE -TABLE_LOCKS_IMMEDIATE 3 +TABLE_LOCKS_IMMEDIATE 18 TABLE_LOCKS_WAITED 1 drop table t1; select 1; @@ -97,7 +97,7 @@ Variable_name Value Com_show_status 3 show status like 'hand%write%'; Variable_name Value -Handler_write 0 +Handler_write 5 show status like '%tmp%'; Variable_name Value Created_tmp_disk_tables 0 @@ -105,7 +105,7 @@ Created_tmp_files 0 Created_tmp_tables 0 show status like 'hand%write%'; Variable_name Value -Handler_write 0 +Handler_write 7 show status like '%tmp%'; Variable_name Value Created_tmp_disk_tables 0 diff --git a/mysql-test/t/log_state.test b/mysql-test/t/log_state.test index 16f466e9b54..b0bb818b783 100644 --- a/mysql-test/t/log_state.test +++ b/mysql-test/t/log_state.test @@ -126,6 +126,59 @@ drop table t1; --replace_column 1 TIMESTAMP 2 USER_HOST 3 # select * from mysql.general_log; +# +# Bug#29129 (Resetting general_log while the GLOBAL READ LOCK is set causes +# a deadlock) + +# save state + +SET @old_general_log_state = @@global.general_log; +SET @old_slow_log_state = @@global.slow_query_log; + +# Test ON->OFF transition under a GLOBAL READ LOCK + +SET GLOBAL general_log = ON; +SET GLOBAL slow_query_log = ON; + +FLUSH TABLES WITH READ LOCK; + +SET GLOBAL general_log = OFF; +SET GLOBAL slow_query_log = OFF; + +UNLOCK TABLES; + +# Test OFF->ON transition under a GLOBAL READ LOCK + +FLUSH TABLES WITH READ LOCK; + +SET GLOBAL general_log = ON; +SET GLOBAL slow_query_log = ON; + +UNLOCK TABLES; + +# Test ON->OFF transition under a GLOBAL READ_ONLY + +SET GLOBAL READ_ONLY = ON; + +SET GLOBAL general_log = OFF; +SET GLOBAL slow_query_log = OFF; + +SET GLOBAL READ_ONLY = OFF; + +# Test OFF->ON transition under a GLOBAL READ_ONLY + +SET GLOBAL READ_ONLY = ON; + +SET GLOBAL general_log = ON; +SET GLOBAL slow_query_log = ON; + +SET GLOBAL READ_ONLY = OFF; + +# Restore state + +SET GLOBAL general_log = @old_general_log_state; +SET GLOBAL slow_query_log = @old_slow_log_state; + --enable_ps_protocol # diff --git a/mysql-test/t/log_tables.test b/mysql-test/t/log_tables.test index e8eff7cba0a..8baa4e5a373 100644 --- a/mysql-test/t/log_tables.test +++ b/mysql-test/t/log_tables.test @@ -64,10 +64,10 @@ flush logs; # check locking of the log tables # ---error ER_CANT_WRITE_LOCK_LOG_TABLE +--error ER_CANT_LOCK_LOG_TABLE lock tables mysql.general_log WRITE; ---error ER_CANT_WRITE_LOCK_LOG_TABLE +--error ER_CANT_LOCK_LOG_TABLE lock tables mysql.slow_log WRITE; # @@ -76,78 +76,59 @@ lock tables mysql.slow_log WRITE; # tables are always opened and locked by the logger. # ---error ER_CANT_READ_LOCK_LOG_TABLE +--error ER_CANT_LOCK_LOG_TABLE lock tables mysql.general_log READ; ---error ER_CANT_READ_LOCK_LOG_TABLE +--error ER_CANT_LOCK_LOG_TABLE lock tables mysql.slow_log READ; # -# This call should result in TL_READ lock on the log table. This is ok and -# should pass. +# This call should result in TL_READ lock on the log table. +# This is not ok and should fail. # +--error ER_CANT_LOCK_LOG_TABLE lock tables mysql.slow_log READ LOCAL, mysql.general_log READ LOCAL; -unlock tables; - -# -# check that FLUSH LOGS waits for all readers of the log table to vanish -# - -connect (con1,localhost,root,,); -connect (con2,localhost,root,,); - -connection con1; - -lock tables mysql.general_log READ LOCAL; - -connection con2; +# Misc locking tests -# this should wait for log tables to unlock -send flush logs; - -connection con1; - -unlock tables; - -# this connection should be alive by the time -connection con2; - -reap; +show create table mysql.general_log; +show fields from mysql.general_log; -select "Mark that we woke up from flush logs in the test" - as "test passed"; +show create table mysql.slow_log; +show fields from mysql.slow_log; # -# perform the same check for TRUNCATE: it should also wait for readers -# to disappear +# check that FLUSH LOGS does not flush the log tables # -connection con1; - -lock tables mysql.general_log READ LOCAL; - -connection con2; - -# this should wait for log tables to unlock -send truncate mysql.general_log; +flush logs; +flush tables; -connection con1; +SET GLOBAL GENERAL_LOG=ON; +SET GLOBAL SLOW_QUERY_LOG=ON; -unlock tables; +show open tables; +flush logs; +show open tables; -# this connection should be alive by the time -connection con2; +# +# check that FLUSH TABLES does flush the log tables +# -reap; +flush tables; +# Since the flush is logged, mysql.general_log will be in the cache +show open tables; -select "Mark that we woke up from TRUNCATE in the test" - as "test passed"; +SET GLOBAL GENERAL_LOG=OFF; +SET GLOBAL SLOW_QUERY_LOG=OFF; -connection con1; +flush tables; +# Here the table cache is empty +show open tables; -use test; +SET GLOBAL GENERAL_LOG=ON; +SET GLOBAL SLOW_QUERY_LOG=ON; # # Bug #16905 Log tables: unicode statements are logged incorrectly @@ -220,10 +201,10 @@ flush logs; # check locking of myisam-based log tables ---error ER_CANT_WRITE_LOCK_LOG_TABLE +--error ER_CANT_LOCK_LOG_TABLE lock tables mysql.general_log WRITE; ---error ER_CANT_WRITE_LOCK_LOG_TABLE +--error ER_CANT_LOCK_LOG_TABLE lock tables mysql.slow_log WRITE; # @@ -232,21 +213,12 @@ lock tables mysql.slow_log WRITE; # tables are always opened and locked by the logger. # ---error ER_CANT_READ_LOCK_LOG_TABLE +--error ER_CANT_LOCK_LOG_TABLE lock tables mysql.general_log READ; ---error ER_CANT_READ_LOCK_LOG_TABLE +--error ER_CANT_LOCK_LOG_TABLE lock tables mysql.slow_log READ; -# -# This call should result in TL_READ lock on the log table. This is ok and -# should pass. -# - -lock tables mysql.slow_log READ LOCAL, mysql.general_log READ LOCAL; - -unlock tables; - # check that we can drop them set global general_log='OFF'; set global slow_query_log='OFF'; @@ -314,6 +286,7 @@ use test; flush tables with read lock; unlock tables; use mysql; +--error ER_CANT_LOCK_LOG_TABLE lock tables general_log read local, help_category read local; unlock tables; @@ -368,9 +341,9 @@ set global slow_query_log='OFF'; RENAME TABLE slow_log TO slow_log2; # this should fail ---error ER_CANT_ACTIVATE_LOG +--error ER_NO_SUCH_TABLE set global general_log='ON'; ---error ER_CANT_ACTIVATE_LOG +--error ER_NO_SUCH_TABLE set global slow_query_log='ON'; RENAME TABLE general_log2 TO general_log; @@ -470,8 +443,305 @@ FLUSH LOGS; ALTER TABLE mysql.slow_log DROP COLUMN seq; ALTER TABLE mysql.slow_log ENGINE = CSV; -# kill all connections -disconnect con1; -disconnect con2; +# +# Bug#25422 (Hang with log tables) +# + +--disable_warnings +drop procedure if exists proc25422_truncate_slow; +drop procedure if exists proc25422_truncate_general; +drop procedure if exists proc25422_alter_slow; +drop procedure if exists proc25422_alter_general; +--enable_warnings + +delimiter //; + +use test// +create procedure proc25422_truncate_slow (loops int) +begin + declare v1 int default 0; + while v1 < loops do + truncate mysql.slow_log; + set v1 = v1 + 1; + end while; +end// + +create procedure proc25422_truncate_general (loops int) +begin + declare v1 int default 0; + while v1 < loops do + truncate mysql.general_log; + set v1 = v1 + 1; + end while; +end// + +create procedure proc25422_alter_slow (loops int) +begin + declare v1 int default 0; + declare ER_BAD_LOG_STATEMENT condition for 1575; + declare continue handler for ER_BAD_LOG_STATEMENT begin end; + + while v1 < loops do + set @old_log_state = @@global.slow_query_log; + set global slow_query_log = 'OFF'; + alter table mysql.slow_log engine = CSV; + set global slow_query_log = @old_log_state; + set v1 = v1 + 1; + end while; +end// + +create procedure proc25422_alter_general (loops int) +begin + declare v1 int default 0; + declare ER_BAD_LOG_STATEMENT condition for 1575; + declare continue handler for ER_BAD_LOG_STATEMENT begin end; + + while v1 < loops do + set @old_log_state = @@global.general_log; + set global general_log = 'OFF'; + alter table mysql.general_log engine = CSV; + set global general_log = @old_log_state; + set v1 = v1 + 1; + end while; +end// + +delimiter ;// + +--echo "Serial test (proc25422_truncate_slow)" +call proc25422_truncate_slow(100); +--echo "Serial test (proc25422_truncate_general)" +call proc25422_truncate_general(100); +--echo "Serial test (proc25422_alter_slow)" +call proc25422_alter_slow(100); +--echo "Serial test (proc25422_alter_general)" +call proc25422_alter_general(100); + +--echo "Parallel test" + +# ER_BAD_LOG_STATEMENT errors will occur, +# since concurrent threads do SET GLOBAL general_log= ... +# This is silenced by handlers and will not affect the test + +connect (addconroot1, localhost, root,,); +connect (addconroot2, localhost, root,,); +connect (addconroot3, localhost, root,,); +connect (addconroot4, localhost, root,,); +connect (addconroot5, localhost, root,,); +connect (addconroot6, localhost, root,,); +connect (addconroot7, localhost, root,,); +connect (addconroot8, localhost, root,,); + +connection addconroot1; +send call proc25422_truncate_slow(100); +connection addconroot2; +send call proc25422_truncate_slow(100); + +connection addconroot3; +send call proc25422_truncate_general(100); +connection addconroot4; +send call proc25422_truncate_general(100); + +connection addconroot5; +send call proc25422_alter_slow(100); +connection addconroot6; +send call proc25422_alter_slow(100); + +connection addconroot7; +send call proc25422_alter_general(100); +connection addconroot8; +send call proc25422_alter_general(100); + +connection addconroot1; +reap; +connection addconroot2; +reap; +connection addconroot3; +reap; +connection addconroot4; +reap; +connection addconroot5; +reap; +connection addconroot6; +reap; +connection addconroot7; +reap; +connection addconroot8; +reap; + +connection default; + +disconnect addconroot1; +disconnect addconroot2; +disconnect addconroot3; +disconnect addconroot4; +disconnect addconroot5; +disconnect addconroot6; +disconnect addconroot7; +disconnect addconroot8; + +drop procedure proc25422_truncate_slow; +drop procedure proc25422_truncate_general; +drop procedure proc25422_alter_slow; +drop procedure proc25422_alter_general; + --enable_ps_protocol + +# +# Bug#23044 (Warnings on flush of a log table) +# + +FLUSH TABLE mysql.general_log; +show warnings; + +FLUSH TABLE mysql.slow_log; +show warnings; + +# +# Bug#17876 (Truncating mysql.slow_log in a SP after using cursor locks the +# thread) +# + +--disable_warnings +DROP TABLE IF EXISTS `db_17876.slow_log_data`; +DROP TABLE IF EXISTS `db_17876.general_log_data`; +DROP PROCEDURE IF EXISTS `db_17876.archiveSlowLog`; +DROP PROCEDURE IF EXISTS `db_17876.archiveGeneralLog`; +DROP DATABASE IF EXISTS `db_17876`; +--enable_warnings + +CREATE DATABASE db_17876; + +CREATE TABLE `db_17876.slow_log_data` ( + `start_time` timestamp default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `user_host` mediumtext , + `query_time` time , + `lock_time` time , + `rows_sent` int(11) , + `rows_examined` int(11) , + `db` varchar(512) default NULL, + `last_insert_id` int(11) default NULL, + `insert_id` int(11) default NULL, + `server_id` int(11) default NULL, + `sql_text` mediumtext +); + +CREATE TABLE `db_17876.general_log_data` ( + `event_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `user_host` mediumtext, + `thread_id` int(11) DEFAULT NULL, + `server_id` int(11) DEFAULT NULL, + `command_type` varchar(64) DEFAULT NULL, + `argument` mediumtext +); + +DELIMITER //; + +CREATE procedure `db_17876.archiveSlowLog`() +BEGIN + DECLARE start_time, query_time, lock_time CHAR(20); + DECLARE user_host MEDIUMTEXT; + DECLARE rows_set, rows_examined, last_insert_id, insert_id, server_id INT; + DECLARE dbname MEDIUMTEXT; + DECLARE sql_text BLOB; + DECLARE done INT DEFAULT 0; + DECLARE ER_SP_FETCH_NO_DATA CONDITION for 1329; + + DECLARE cur1 CURSOR FOR SELECT * FROM mysql.slow_log; + + OPEN cur1; + + REPEAT + BEGIN + BEGIN + DECLARE CONTINUE HANDLER FOR ER_SP_FETCH_NO_DATA SET done = 1; + + FETCH cur1 INTO + start_time, user_host, query_time, lock_time, + rows_set, rows_examined, dbname, last_insert_id, + insert_id, server_id, sql_text; + END; + + IF NOT done THEN + BEGIN + INSERT INTO + `db_17876.slow_log_data` + VALUES(start_time, user_host, query_time, lock_time, rows_set, rows_examined, + dbname, last_insert_id, insert_id, server_id, sql_text); + END; + END IF; + END; + UNTIL done END REPEAT; + + CLOSE cur1; + TRUNCATE mysql.slow_log; +END // + +CREATE procedure `db_17876.archiveGeneralLog`() +BEGIN + DECLARE event_time CHAR(20); + DECLARE user_host, argument MEDIUMTEXT; + DECLARE thread_id, server_id INT; + DECLARE sql_text BLOB; + DECLARE done INT DEFAULT 0; + DECLARE command_type VARCHAR(64); + DECLARE ER_SP_FETCH_NO_DATA CONDITION for 1329; + + DECLARE cur1 CURSOR FOR SELECT * FROM mysql.general_log; + + OPEN cur1; + + REPEAT + BEGIN + BEGIN + DECLARE CONTINUE HANDLER FOR ER_SP_FETCH_NO_DATA SET done = 1; + + FETCH cur1 INTO + event_time, user_host, thread_id, server_id, + command_type, argument; + END; + + IF NOT done THEN + BEGIN + INSERT INTO + `db_17876.general_log_data` + VALUES(event_time, user_host, thread_id, server_id, + command_type, argument); + END; + END IF; + END; + UNTIL done END REPEAT; + + CLOSE cur1; + TRUNCATE mysql.general_log; +END // + +DELIMITER ;// + +SET @old_general_log_state = @@global.general_log; +SET @old_slow_log_state = @@global.slow_query_log; + +SET GLOBAL general_log = ON; +SET GLOBAL slow_query_log = ON; + +select "put something into general_log"; +select "... and something more ..."; + +call `db_17876.archiveSlowLog`(); +call `db_17876.archiveGeneralLog`(); + +SET GLOBAL general_log = OFF; +SET GLOBAL slow_query_log = OFF; + +call `db_17876.archiveSlowLog`(); +call `db_17876.archiveGeneralLog`(); + +DROP TABLE `db_17876.slow_log_data`; +DROP TABLE `db_17876.general_log_data`; +DROP PROCEDURE IF EXISTS `db_17876.archiveSlowLog`; +DROP PROCEDURE IF EXISTS `db_17876.archiveGeneralLog`; +DROP DATABASE IF EXISTS `db_17876`; + +SET GLOBAL general_log = @old_general_log_state; +SET GLOBAL slow_query_log = @old_slow_log_state; + diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 394f44bbc83..5a4d30fdd50 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -2018,10 +2018,17 @@ delimiter ;| select func_1(), func_1(), func_1() from dual; drop function func_1; drop procedure proc_1; + +# make the output deterministic: +# the order used in SHOW OPEN TABLES +# is too much implementation dependent +--disable_ps_protocol flush tables; select Host, User from mysql.user limit 0; select Host, Db from mysql.host limit 0; show open tables from mysql; +--enable_ps_protocol + prepare abc from "flush tables"; execute abc; show open tables from mysql; diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index 09c0b08a3cd..5d7d8d3130d 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -438,6 +438,11 @@ drop table if exists t1; CREATE TABLE txt1(a int); CREATE TABLE tyt2(a int); CREATE TABLE urkunde(a int); + +# make the output deterministic: +# the order used in SHOW OPEN TABLES +# is too much implementation dependent +--disable_ps_protocol FLUSH TABLES; SELECT 1 FROM mysql.db, mysql.proc, mysql.user, mysql.time_zone, mysql.time_zone_name, txt1, tyt2, urkunde LIMIT 0; SHOW OPEN TABLES; @@ -447,6 +452,8 @@ SHOW OPEN TABLES LIKE 't%'; SHOW OPEN TABLES LIKE '%o%'; FLUSH TABLES; SHOW OPEN TABLES; +--enable_ps_protocol + DROP TABLE txt1; DROP TABLE tyt2; DROP TABLE urkunde; diff --git a/sql/handler.cc b/sql/handler.cc index 2d836b30862..e0ec2962d17 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1516,34 +1516,6 @@ THD *handler::ha_thd(void) const } -bool handler::check_if_log_table_locking_is_allowed(uint sql_command, - ulong type, TABLE *table) -{ - /* - Deny locking of the log tables, which is incompatible with - concurrent insert. The routine is not called if the table is - being locked from a logger THD (general_log_thd or slow_log_thd) - or from a privileged thread (see log.cc for details) - */ - if (table->s->log_table && - sql_command != SQLCOM_TRUNCATE && - sql_command != SQLCOM_ALTER_TABLE && - !(sql_command == SQLCOM_FLUSH && - type & REFRESH_LOG) && - (table->reginfo.lock_type >= TL_READ_NO_INSERT)) - { - /* - The check >= TL_READ_NO_INSERT denies all write locks - plus the only read lock (TL_READ_NO_INSERT itself) - */ - table->reginfo.lock_type == TL_READ_NO_INSERT ? - my_error(ER_CANT_READ_LOCK_LOG_TABLE, MYF(0)) : - my_error(ER_CANT_WRITE_LOCK_LOG_TABLE, MYF(0)); - return FALSE; - } - return TRUE; -} - /** @brief Open database-handler. @@ -3681,6 +3653,18 @@ int handler::ha_write_row(uchar *buf) return 0; } + +/** + Write a record to the engine bypassing row-level binary logging. + This method is used internally by the server for writing to + performance schema tables, which are never replicated. +*/ +int handler::ha_write_row_no_binlog(uchar *buf) +{ + return write_row(buf); +} + + int handler::ha_update_row(const uchar *old_data, uchar *new_data) { int error; diff --git a/sql/handler.h b/sql/handler.h index f45b28c55f5..06f0a2cf035 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1035,44 +1035,6 @@ public: { cached_table_flags= table_flags(); } - /* - Check whether a handler allows to lock the table. - - SYNOPSIS - check_if_locking_is_allowed() - thd Handler of the thread, trying to lock the table - table Table handler to check - count Total number of tables to be locked - current Index of the current table in the list of the tables - to be locked. - system_count Pointer to the counter of system tables seen thus - far. - called_by_privileged_thread TRUE if called from a logger THD - (general_log_thd or slow_log_thd) - or by a privileged thread, which - has the right to lock log tables. - - DESCRIPTION - Check whether a handler allows to lock the table. For instance, - MyISAM does not allow to lock mysql.proc along with other tables. - This limitation stems from the fact that MyISAM does not support - row-level locking and we have to add this limitation to avoid - deadlocks. - - RETURN - TRUE Locking is allowed - FALSE Locking is not allowed. The error was thrown. - */ - virtual bool check_if_locking_is_allowed(uint sql_command, - ulong type, TABLE *table, - uint count, uint current, - uint *system_count, - bool called_by_privileged_thread) - { - return TRUE; - } - bool check_if_log_table_locking_is_allowed(uint sql_command, - ulong type, TABLE *table); int ha_open(TABLE *table, const char *name, int mode, int test_if_locked); void adjust_next_insert_id_after_explicit_value(ulonglong nr); int update_auto_increment(); @@ -1196,6 +1158,7 @@ public: */ int ha_external_lock(THD *thd, int lock_type); int ha_write_row(uchar * buf); + int ha_write_row_no_binlog(uchar * buf); int ha_update_row(const uchar * old_data, uchar * new_data); int ha_delete_row(const uchar * buf); diff --git a/sql/lock.cc b/sql/lock.cc index 4260a031def..34a2e202f8f 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -117,9 +117,64 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, MYSQL_LOCK *sql_lock; TABLE *write_lock_used; int rc; + uint i; + bool log_table_write_query; + uint system_count; + DBUG_ENTER("mysql_lock_tables"); *need_reopen= FALSE; + system_count= 0; + + log_table_write_query= (is_log_table_write_query(thd->lex->sql_command) + || ((flags & MYSQL_LOCK_PERF_SCHEMA) != 0)); + + for (i=0 ; i<count; i++) + { + TABLE *t= tables[i]; + + /* Protect against 'fake' partially initialized TABLE_SHARE */ + DBUG_ASSERT(t->s->table_category != TABLE_UNKNOWN_CATEGORY); + + /* + Table I/O to performance schema tables is performed + only internally by the server implementation. + When a user is requesting a lock, the following + constraints are enforced: + */ + if (t->s->require_write_privileges() && + ! log_table_write_query) + { + /* + A user should not be able to prevent writes, + or hold any type of lock in a session, + since this would be a DOS attack. + */ + if ((t->reginfo.lock_type >= TL_READ_NO_INSERT) + || (thd->lex->sql_command == SQLCOM_LOCK_TABLES)) + { + my_error(ER_CANT_LOCK_LOG_TABLE, MYF(0)); + DBUG_RETURN(0); + } + } + + if ((t->s->table_category == TABLE_CATEGORY_SYSTEM) && + (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE)) + { + system_count++; + } + } + + /* + Locking of system tables is restricted: + locking a mix of system and non-system tables in the same lock + is prohibited, to prevent contention. + */ + if ((system_count > 0) && (system_count < count)) + { + my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0)); + DBUG_RETURN(0); + } for (;;) { @@ -439,7 +494,8 @@ void mysql_lock_downgrade_write(THD *thd, TABLE *table, { MYSQL_LOCK *locked; TABLE *write_lock_used; - if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used))) + if ((locked = get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK, + &write_lock_used))) { for (uint i=0; i < locked->lock_count; i++) thr_downgrade_write_lock(locked->locks[i], new_lock_type); @@ -698,25 +754,19 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, TABLE **to, **table_buf; DBUG_ENTER("get_lock_data"); + DBUG_ASSERT((flags == GET_LOCK_UNLOCK) || (flags == GET_LOCK_STORE_LOCKS)); + DBUG_PRINT("info", ("count %d", count)); *write_lock_used=0; - uint system_count= 0; for (i=tables=lock_count=0 ; i < count ; i++) { - if (table_ptr[i]->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE) + TABLE *t= table_ptr[i]; + + if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE) { - tables+=table_ptr[i]->file->lock_count(); + tables+= t->file->lock_count(); lock_count++; } - /* - Check if we can lock the table. For some tables we cannot do that - beacause of handler-specific locking issues. - */ - if (!table_ptr[i]-> file-> - check_if_locking_is_allowed(thd->lex->sql_command, thd->lex->type, - table_ptr[i], count, i, &system_count, - logger.is_privileged_thread(thd))) - DBUG_RETURN(0); } /* diff --git a/sql/log.cc b/sql/log.cc index 0bf77d68410..af039c15ffc 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -57,6 +57,35 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all); static int binlog_rollback(handlerton *hton, THD *thd, bool all); static int binlog_prepare(handlerton *hton, THD *thd, bool all); +/** + Silence all errors and warnings reported when performing a write + to a log table. + Errors and warnings are not reported to the client or SQL exception + handlers, so that the presence of logging does not interfere and affect + the logic of an application. +*/ +class Silence_log_table_errors : public Internal_error_handler +{ +public: + Silence_log_table_errors() + {} + + virtual ~Silence_log_table_errors() {} + + virtual bool handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level, + THD *thd); +}; + +bool +Silence_log_table_errors::handle_error(uint /* sql_errno */, + MYSQL_ERROR::enum_warning_level /* level */, + THD * /* thd */) +{ + return TRUE; +} + + sql_print_message_func sql_print_message_handlers[3] = { sql_print_information, @@ -187,6 +216,19 @@ public: handlerton *binlog_hton; +bool LOGGER::is_log_table_enabled(uint log_table_type) +{ + switch (log_table_type) { + case QUERY_LOG_SLOW: + return (table_log_handler != NULL) && opt_slow_log; + case QUERY_LOG_GENERAL: + return (table_log_handler != NULL) && opt_log ; + default: + DBUG_ASSERT(0); + return FALSE; /* make compiler happy */ + } +} + /* Check if a given table is opened log table */ int check_if_log_table(uint db_len, const char *db, uint table_name_len, @@ -200,211 +242,38 @@ int check_if_log_table(uint db_len, const char *db, uint table_name_len, if (table_name_len == 11 && !(lower_case_table_names ? my_strcasecmp(system_charset_info, table_name, "general_log") : - strcmp(table_name, "general_log")) && - (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_GENERAL))) - return QUERY_LOG_GENERAL; - else - if (table_name_len == 8 && !(lower_case_table_names ? - my_strcasecmp(system_charset_info, table_name, "slow_log") : - strcmp(table_name, "slow_log")) && - (!check_if_opened ||logger.is_log_table_enabled(QUERY_LOG_SLOW))) + strcmp(table_name, "general_log"))) + { + if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_GENERAL)) + return QUERY_LOG_GENERAL; + return 0; + } + + if (table_name_len == 8 && !(lower_case_table_names ? + my_strcasecmp(system_charset_info, table_name, "slow_log") : + strcmp(table_name, "slow_log"))) + { + if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_SLOW)) return QUERY_LOG_SLOW; + return 0; + } } return 0; } -/* - Open log table of a given type (general or slow log) - - SYNOPSIS - open_log_table() - - log_table_type type of the log table to open: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW - - DESCRIPTION - - The function opens a log table and marks it as such. Log tables are open - during the whole time, while server is running. Except for the moments - when they have to be reopened: during FLUSH LOGS and TRUNCATE. This - function is invoked directly only once during startup. All subsequent - calls happen through reopen_log_table(), which performs additional check. - - RETURN - FALSE - OK - TRUE - error occured -*/ - -bool Log_to_csv_event_handler::open_log_table(uint log_table_type) -{ - THD *log_thd, *curr= current_thd; - TABLE_LIST *table; - bool error= FALSE; - DBUG_ENTER("open_log_table"); - - switch (log_table_type) { - case QUERY_LOG_GENERAL: - log_thd= general_log_thd; - table= &general_log; - /* clean up table before reuse/initial usage */ - bzero((char*) table, sizeof(TABLE_LIST)); - table->alias= table->table_name= (char*) "general_log"; - table->table_name_length= 11; - break; - case QUERY_LOG_SLOW: - log_thd= slow_log_thd; - table= &slow_log; - bzero((char*) table, sizeof(TABLE_LIST)); - table->alias= table->table_name= (char*) "slow_log"; - table->table_name_length= 8; - break; - default: - assert(0); // Impossible - } - - /* - This way we check that appropriate log thd was created ok during - initialization. We cannot check "is_log_tables_initialized" var, as - the very initialization is not finished until this function is - completed in the very first time. - */ - if (!log_thd) - { - DBUG_PRINT("error",("Cannot initialize log tables")); - DBUG_RETURN(TRUE); - } - - /* - Set THD's thread_stack. This is needed to perform stack overrun - check, which is done by some routines (e.g. open_table()). - In the case we are called by thread, which already has this parameter - set, we use this value. Otherwise we do a wild guess. This won't help - to correctly track the stack overrun in these exceptional cases (which - could probably happen only during startup and shutdown) but at least - lets us to pass asserts. - The problem stems from the fact that logger THDs are not real threads. - */ - if (curr) - log_thd->thread_stack= curr->thread_stack; - else - log_thd->thread_stack= (char*) &log_thd; - - log_thd->store_globals(); - - table->lock_type= TL_WRITE_CONCURRENT_INSERT; - table->db= log_thd->db; - table->db_length= log_thd->db_length; - - lex_start(log_thd); - log_thd->clear_error(); - if (simple_open_n_lock_tables(log_thd, table) || - table->table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) || - table->table->file->ha_rnd_init(0)) - error= TRUE; - else - { - table->table->use_all_columns(); - table->table->locked_by_logger= TRUE; - table->table->no_replicate= TRUE; - - /* Honor next number columns if present */ - table->table->next_number_field= table->table->found_next_number_field; - } - /* restore thread settings */ - if (curr) - curr->store_globals(); - else - { - my_pthread_setspecific_ptr(THR_THD, 0); - my_pthread_setspecific_ptr(THR_MALLOC, 0); - } - - /* - After a log table was opened, we should clear privileged thread - flag (which allows locking of a log table by a special thread, usually - the one who closed log tables temporarily). - */ - privileged_thread= 0; - DBUG_RETURN(error); -} - - Log_to_csv_event_handler::Log_to_csv_event_handler() { - /* init artificial THD's */ - general_log_thd= new THD; - /* logger thread always works with mysql database */ - general_log_thd->db= my_strdup("mysql", MYF(0)); - general_log_thd->db_length= 5; - general_log.table= 0; - - slow_log_thd= new THD; - /* logger thread always works with mysql database */ - slow_log_thd->db= my_strdup("mysql", MYF(0));; - slow_log_thd->db_length= 5; - slow_log.table= 0; - /* no privileged thread exists at the moment */ - privileged_thread= 0; } Log_to_csv_event_handler::~Log_to_csv_event_handler() { - /* now cleanup the tables */ - if (general_log_thd) - { - delete general_log_thd; - general_log_thd= NULL; - } - - if (slow_log_thd) - { - delete slow_log_thd; - slow_log_thd= NULL; - } -} - - -/* - Reopen log table of a given type - - SYNOPSIS - reopen_log_table() - - log_table_type type of the log table to open: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW - - DESCRIPTION - - The function is a wrapper around open_log_table(). It is used during - FLUSH LOGS and TRUNCATE of the log tables (i.e. when we need to close - and reopen them). The difference is in the check of the - logger.is_log_tables_initialized var, which can't be done in - open_log_table(), as it makes no sense during startup. - - NOTE: this code assumes that we have logger mutex locked - - RETURN - FALSE - ok - TRUE - open_log_table() returned an error -*/ - -bool Log_to_csv_event_handler::reopen_log_table(uint log_table_type) -{ - /* don't open the log table, if it wasn't enabled during startup */ - if (!logger.is_log_tables_initialized) - return FALSE; - return open_log_table(log_table_type); } void Log_to_csv_event_handler::cleanup() { - if (opt_log) - close_log_table(QUERY_LOG_GENERAL, FALSE); - if (opt_slow_log) - close_log_table(QUERY_LOG_SLOW, FALSE); logger.is_log_tables_initialized= FALSE; } @@ -436,49 +305,88 @@ void Log_to_csv_event_handler::cleanup() */ bool Log_to_csv_event_handler:: - log_general(time_t event_time, const char *user_host, + log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, CHARSET_INFO *client_cs) { - TABLE *table= general_log.table; + TABLE_LIST table_list; + TABLE *table; + bool result= TRUE; + bool need_close= FALSE; + bool need_pop= FALSE; + bool need_rnd_end= FALSE; uint field_index; + Silence_log_table_errors error_handler; + Open_tables_state open_tables_backup; + Field_timestamp *field0; + ulonglong save_thd_options; + bool save_query_start_used; + time_t save_start_time; + time_t save_time_after_lock; + time_t save_user_time; + bool save_time_zone_used; + + save_thd_options= thd->options; + thd->options&= ~OPTION_BIN_LOG; + + save_query_start_used= thd->query_start_used; + save_start_time= thd->start_time; + save_time_after_lock= thd->time_after_lock; + save_user_time= thd->user_time; + save_time_zone_used= thd->time_zone_used; + + bzero(& table_list, sizeof(TABLE_LIST)); + table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str; + table_list.table_name_length= GENERAL_LOG_NAME.length; + + table_list.lock_type= TL_WRITE_CONCURRENT_INSERT; + + table_list.db= MYSQL_SCHEMA_NAME.str; + table_list.db_length= MYSQL_SCHEMA_NAME.length; + + table= open_performance_schema_table(thd, & table_list, + & open_tables_backup); + need_close= TRUE; + + if (!table || + table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) || + table->file->ha_rnd_init(0)) + goto err; + + need_rnd_end= TRUE; + + /* Honor next number columns if present */ + table->next_number_field= table->found_next_number_field; /* "INSERT INTO general_log" can generate warning sometimes. - Let's reset warnings from previous queries, - otherwise warning list can grow too much, - so thd->query gets spoiled as some point in time, - and mysql_parse() receives a broken query. QQ: this problem needs to be studied in more details. - Probably it's better to suppress warnings in logging INSERTs at all. - Comment this line and run "cast.test" to see what's happening: + Comment this 2 lines and run "cast.test" to see what's happening: */ - mysql_reset_errors(table->in_use, 1); - - /* below should never happen */ - if (unlikely(!logger.is_log_tables_initialized)) - return FALSE; + thd->push_internal_handler(& error_handler); + need_pop= TRUE; /* NOTE: we do not call restore_record() here, as all fields are filled by the Logger (=> no need to load default ones). */ - /* Set current time. Required for CURRENT_TIMESTAMP to work */ - general_log_thd->start_time= event_time; - /* We do not set a value for table->field[0], as it will use default value (which is CURRENT_TIMESTAMP). */ /* check that all columns exist */ - if (!table->field[1] || !table->field[2] || !table->field[3] || - !table->field[4] || !table->field[5]) + if (table->s->fields < 6) goto err; + DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP); + + field0= (Field_timestamp*) (table->field[0]); + field0->set_time(); + /* do a write */ if (table->field[1]->store(user_host, user_host_len, client_cs) || table->field[2]->store((longlong) thread_id, TRUE) || @@ -500,16 +408,39 @@ bool Log_to_csv_event_handler:: table->field[field_index]->set_default(); } - /* log table entries are not replicated at the moment */ - tmp_disable_binlog(current_thd); + /* log table entries are not replicated */ + if (table->file->ha_write_row_no_binlog(table->record[0])) + { + struct tm start; + localtime_r(&event_time, &start); - table->file->ha_write_row(table->record[0]); + sql_print_error("%02d%02d%02d %2d:%02d:%02d - Failed to write to mysql.general_log", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); + } - reenable_binlog(current_thd); + result= FALSE; - return FALSE; err: - return TRUE; + if (need_rnd_end) + { + table->file->ha_rnd_end(); + table->file->ha_release_auto_increment(); + } + if (need_pop) + thd->pop_internal_handler(); + if (need_close) + close_performance_schema_table(thd, & open_tables_backup); + + thd->options= save_thd_options; + + thd->query_start_used= save_query_start_used; + thd->start_time= save_start_time; + thd->time_after_lock= save_time_after_lock; + thd->user_time= save_user_time; + thd->time_zone_used= save_time_zone_used; + return result; } @@ -548,34 +479,61 @@ bool Log_to_csv_event_handler:: longlong query_time, longlong lock_time, bool is_command, const char *sql_text, uint sql_text_len) { - /* table variables */ - TABLE *table= slow_log.table; + TABLE_LIST table_list; + TABLE *table; + bool result= TRUE; + bool need_close= FALSE; + bool need_rnd_end= FALSE; + Open_tables_state open_tables_backup; + bool save_query_start_used; + time_t save_start_time; + time_t save_time_after_lock; + time_t save_user_time; + bool save_time_zone_used; CHARSET_INFO *client_cs= thd->variables.character_set_client; - DBUG_ENTER("log_slow"); + DBUG_ENTER("Log_to_csv_event_handler::log_slow"); - /* below should never happen */ - if (unlikely(!logger.is_log_tables_initialized)) - return FALSE; + bzero(& table_list, sizeof(TABLE_LIST)); + table_list.alias= table_list.table_name= SLOW_LOG_NAME.str; + table_list.table_name_length= SLOW_LOG_NAME.length; + + table_list.lock_type= TL_WRITE_CONCURRENT_INSERT; + + table_list.db= MYSQL_SCHEMA_NAME.str; + table_list.db_length= MYSQL_SCHEMA_NAME.length; + + save_query_start_used= thd->query_start_used; + save_start_time= thd->start_time; + save_time_after_lock= thd->time_after_lock; + save_user_time= thd->user_time; + save_time_zone_used= thd->time_zone_used; + + table= open_performance_schema_table(thd, & table_list, + & open_tables_backup); + need_close= TRUE; + + if (!table || + table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) || + table->file->ha_rnd_init(0)) + goto err; + + need_rnd_end= TRUE; + + /* Honor next number columns if present */ + table->next_number_field= table->found_next_number_field; - /* - Set start time for CURRENT_TIMESTAMP to the start of the query. - This will be default value for the field[0] - */ - slow_log_thd->start_time= query_start_arg; restore_record(table, s->default_values); // Get empty record + /* check that all columns exist */ + if (table->s->fields < 11) + goto err; + /* We do not set a value for table->field[0], as it will use default value. */ - if (!table->field[1] || !table->field[2] || !table->field[3] || - !table->field[4] || !table->field[5] || !table->field[6] || - !table->field[7] || !table->field[8] || !table->field[9] || - !table->field[10]) - goto err; - /* store the value */ if (table->field[1]->store(user_host, user_host_len, client_cs)) goto err; @@ -612,7 +570,6 @@ bool Log_to_csv_event_handler:: table->field[4]->set_null(); table->field[5]->set_null(); } - /* fill database field */ if (thd->db) { @@ -654,17 +611,71 @@ bool Log_to_csv_event_handler:: if (table->field[10]->store(sql_text,sql_text_len, client_cs)) goto err; - /* log table entries are not replicated at the moment */ - tmp_disable_binlog(current_thd); + /* log table entries are not replicated */ + if (table->file->ha_write_row_no_binlog(table->record[0])) + { + struct tm start; + localtime_r(¤t_time, &start); - /* write the row */ - table->file->ha_write_row(table->record[0]); + sql_print_error("%02d%02d%02d %2d:%02d:%02d - Failed to write to mysql.slow_log", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); + } - reenable_binlog(current_thd); + result= FALSE; - DBUG_RETURN(0); err: - DBUG_RETURN(1); + if (need_rnd_end) + { + table->file->ha_rnd_end(); + table->file->ha_release_auto_increment(); + } + if (need_close) + close_performance_schema_table(thd, & open_tables_backup); + + thd->query_start_used= save_query_start_used; + thd->start_time= save_start_time; + thd->time_after_lock= save_time_after_lock; + thd->user_time= save_user_time; + thd->time_zone_used= save_time_zone_used; + DBUG_RETURN(result); +} + +int Log_to_csv_event_handler:: + activate_log(THD *thd, uint log_table_type) +{ + TABLE_LIST table_list; + TABLE *table; + int result; + Open_tables_state open_tables_backup; + + DBUG_ENTER("Log_to_csv_event_handler::activate_log"); + + bzero(& table_list, sizeof(TABLE_LIST)); + + if (log_table_type == QUERY_LOG_GENERAL) + { + table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str; + table_list.table_name_length= GENERAL_LOG_NAME.length; + } + else + { + DBUG_ASSERT(log_table_type == QUERY_LOG_SLOW); + table_list.alias= table_list.table_name= SLOW_LOG_NAME.str; + table_list.table_name_length= SLOW_LOG_NAME.length; + } + + table_list.lock_type= TL_WRITE_CONCURRENT_INSERT; + + table_list.db= MYSQL_SCHEMA_NAME.str; + table_list.db_length= MYSQL_SCHEMA_NAME.length; + + table= open_performance_schema_table(thd, & table_list, + & open_tables_backup); + result= (table ? 0 : 1); + close_performance_schema_table(thd, & open_tables_backup); + DBUG_RETURN(result); } bool Log_to_csv_event_handler:: @@ -697,10 +708,19 @@ bool Log_to_file_event_handler:: longlong query_time, longlong lock_time, bool is_command, const char *sql_text, uint sql_text_len) { - return mysql_slow_log.write(thd, current_time, query_start_arg, - user_host, user_host_len, - query_time, lock_time, is_command, - sql_text, sql_text_len); + bool res; + + (void) pthread_mutex_lock(mysql_slow_log.get_log_lock()); + + /* TODO: MYSQL_QUERY_LOG::write is not thread-safe */ + res= mysql_slow_log.write(thd, current_time, query_start_arg, + user_host, user_host_len, + query_time, lock_time, is_command, + sql_text, sql_text_len); + + (void) pthread_mutex_unlock(mysql_slow_log.get_log_lock()); + + return res; } @@ -710,15 +730,24 @@ bool Log_to_file_event_handler:: */ bool Log_to_file_event_handler:: - log_general(time_t event_time, const char *user_host, + log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, CHARSET_INFO *client_cs) { - return mysql_log.write(event_time, user_host, user_host_len, - thread_id, command_type, command_type_len, - sql_text, sql_text_len); + bool res; + + (void) pthread_mutex_lock (mysql_log.get_log_lock()); + + /* TODO: MYSQL_QUERY_LOG::write is not thread-safe */ + res= mysql_log.write(event_time, user_host, user_host_len, + thread_id, command_type, command_type_len, + sql_text, sql_text_len); + + (void) pthread_mutex_unlock (mysql_log.get_log_lock()); + + return res; } @@ -787,7 +816,7 @@ bool LOGGER::error_log_print(enum loglevel level, const char *format, void LOGGER::cleanup_base() { DBUG_ASSERT(inited == 1); - (void) pthread_mutex_destroy(&LOCK_logger); + rwlock_destroy(&LOCK_logger); if (table_log_handler) { table_log_handler->cleanup(); @@ -806,12 +835,6 @@ void LOGGER::cleanup_end() } -void LOGGER::close_log_table(uint log_table_type, bool lock_in_use) -{ - table_log_handler->close_log_table(log_table_type, lock_in_use); -} - - /* Perform basic log initialization: create file-based log handler and init error log. @@ -833,7 +856,7 @@ void LOGGER::init_base() init_error_log(LOG_FILE); file_log_handler->init_pthread_objects(); - (void) pthread_mutex_init(&LOCK_logger, MY_MUTEX_INIT_SLOW); + my_rwlock_init(&LOCK_logger, NULL); } @@ -848,29 +871,6 @@ void LOGGER::init_log_tables() } -bool LOGGER::reopen_log_table(uint log_table_type) -{ - return table_log_handler->reopen_log_table(log_table_type); -} - -bool LOGGER::reopen_log_tables() -{ - /* - we use | and not || here, to ensure that both reopen_log_table - are called, even if the first one fails - */ - if ((opt_slow_log && logger.reopen_log_table(QUERY_LOG_SLOW)) | - (opt_log && logger.reopen_log_table(QUERY_LOG_GENERAL))) - return TRUE; - return FALSE; -} - - -void LOGGER::tmp_close_log_tables(THD *thd) -{ - table_log_handler->tmp_close_log_tables(thd); -} - bool LOGGER::flush_logs(THD *thd) { int rc= 0; @@ -879,19 +879,11 @@ bool LOGGER::flush_logs(THD *thd) Now we lock logger, as nobody should be able to use logging routines while log tables are closed */ - logger.lock(); - if (logger.is_log_tables_initialized) - table_log_handler->tmp_close_log_tables(thd); // the locking happens here + logger.lock_exclusive(); /* reopen log files */ file_log_handler->flush(); - /* reopen tables in the case they were enabled */ - if (logger.is_log_tables_initialized) - { - if (reopen_log_tables()) - rc= TRUE; - } /* end of log flush */ logger.unlock(); return rc; @@ -939,7 +931,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, if (thd->slave_thread) return 0; - lock(); + lock_shared(); if (!opt_slow_log) { unlock(); @@ -1011,7 +1003,7 @@ bool LOGGER::general_log_print(THD *thd, enum enum_server_command command, else id=0; /* Log from connect handler */ - lock(); + lock_shared(); if (!opt_log) { unlock(); @@ -1035,7 +1027,7 @@ bool LOGGER::general_log_print(THD *thd, enum enum_server_command command, while (*current_handler) error+= (*current_handler++)-> - log_general(current_time, user_host_buff, + log_general(thd, current_time, user_host_buff, user_host_len, id, command_name[(uint) command].str, command_name[(uint) command].length, @@ -1122,35 +1114,42 @@ void LOGGER::init_general_log(uint general_log_printer) bool LOGGER::activate_log_handler(THD* thd, uint log_type) { - bool res= 0; - lock(); + bool res= FALSE; + lock_exclusive(); switch (log_type) { case QUERY_LOG_SLOW: if (!opt_slow_log) { - if ((res= reopen_log_table(log_type))) - goto err; file_log_handler->get_mysql_slow_log()-> open_slow_log(sys_var_slow_log_path.value); init_slow_log(log_output_options); - opt_slow_log= TRUE; + if (table_log_handler->activate_log(thd, QUERY_LOG_SLOW)) + { + /* Error printed by open table in activate_log() */ + res= TRUE; + } + else + opt_slow_log= TRUE; } break; case QUERY_LOG_GENERAL: if (!opt_log) { - if ((res= reopen_log_table(log_type))) - goto err; file_log_handler->get_mysql_log()-> open_query_log(sys_var_general_log_path.value); init_general_log(log_output_options); - opt_log= TRUE; + if (table_log_handler->activate_log(thd, QUERY_LOG_GENERAL)) + { + /* Error printed by open table in activate_log() */ + res= TRUE; + } + else + opt_log= TRUE; } break; default: DBUG_ASSERT(0); } -err: unlock(); return res; } @@ -1158,23 +1157,17 @@ err: void LOGGER::deactivate_log_handler(THD *thd, uint log_type) { - TABLE_LIST *table_list; my_bool *tmp_opt= 0; MYSQL_LOG *file_log; - THD *log_thd; switch (log_type) { case QUERY_LOG_SLOW: - table_list= &table_log_handler->slow_log; tmp_opt= &opt_slow_log; file_log= file_log_handler->get_mysql_slow_log(); - log_thd= table_log_handler->slow_log_thd; break; case QUERY_LOG_GENERAL: - table_list= &table_log_handler->general_log; tmp_opt= &opt_log; file_log= file_log_handler->get_mysql_log(); - log_thd= table_log_handler->general_log_thd; break; default: assert(0); // Impossible @@ -1183,81 +1176,16 @@ void LOGGER::deactivate_log_handler(THD *thd, uint log_type) if (!(*tmp_opt)) return; - if (is_log_tables_initialized) - lock_and_wait_for_table_name(log_thd, table_list); - lock(); - - if (is_log_tables_initialized) - { - VOID(pthread_mutex_lock(&LOCK_open)); - close_log_table(log_type, TRUE); - table_list->table= 0; - query_cache_invalidate3(log_thd, table_list, 0); - unlock_table_name(log_thd, table_list); - VOID(pthread_mutex_unlock(&LOCK_open)); - } + lock_exclusive(); file_log->close(0); *tmp_opt= FALSE; unlock(); } -/* - Close log tables temporarily. The thread which closed - them this way can lock them in any mode it needs. - NOTE: one should call logger.lock() before entering this - function. -*/ -void Log_to_csv_event_handler::tmp_close_log_tables(THD *thd) -{ - TABLE_LIST close_slow_log, close_general_log; - - /* fill lists, we will need to perform operations on tables */ - bzero((char*) &close_slow_log, sizeof(TABLE_LIST)); - close_slow_log.alias= close_slow_log.table_name=(char*) "slow_log"; - close_slow_log.table_name_length= 8; - close_slow_log.db= (char*) "mysql"; - close_slow_log.db_length= 5; - - bzero((char*) &close_general_log, sizeof(TABLE_LIST)); - close_general_log.alias= close_general_log.table_name=(char*) "general_log"; - close_general_log.table_name_length= 11; - close_general_log.db= (char*) "mysql"; - close_general_log.db_length= 5; - - privileged_thread= thd; - - VOID(pthread_mutex_lock(&LOCK_open)); - /* - NOTE: in fact, the first parameter used in query_cache_invalidate3() - could be any non-NULL THD, as the underlying code makes certain - assumptions about this. - Here we use one of the logger handler THD's. Simply because it - seems appropriate. - */ - if (opt_log) - { - close_log_table(QUERY_LOG_GENERAL, TRUE); - query_cache_invalidate3(general_log_thd, &close_general_log, 0); - } - if (opt_slow_log) - { - close_log_table(QUERY_LOG_SLOW, TRUE); - query_cache_invalidate3(general_log_thd, &close_slow_log, 0); - } - VOID(pthread_mutex_unlock(&LOCK_open)); -} - /* the parameters are unused for the log tables */ bool Log_to_csv_event_handler::init() { - /* - we use | and not || here, to ensure that both open_log_table - are called, even if the first one fails - */ - if ((opt_log && open_log_table(QUERY_LOG_GENERAL)) | - (opt_slow_log && open_log_table(QUERY_LOG_SLOW))) - return 1; return 0; } @@ -1268,7 +1196,7 @@ int LOGGER::set_handlers(uint error_log_printer, /* error log table is not supported yet */ DBUG_ASSERT(error_log_printer < LOG_TABLE); - lock(); + lock_exclusive(); if ((slow_log_printer & LOG_TABLE || general_log_printer & LOG_TABLE) && !is_log_tables_initialized) @@ -1290,72 +1218,6 @@ int LOGGER::set_handlers(uint error_log_printer, } -/* - Close log table of a given type (general or slow log) - - SYNOPSIS - close_log_table() - - log_table_type type of the log table to close: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW - lock_in_use Set to TRUE if the caller owns LOCK_open. FALSE otherwise. - - DESCRIPTION - - The function closes a log table. It is invoked (1) when we need to reopen - log tables (e.g. FLUSH LOGS or TRUNCATE on the log table is being - executed) or (2) during shutdown. -*/ - -void Log_to_csv_event_handler:: - close_log_table(uint log_table_type, bool lock_in_use) -{ - THD *log_thd, *curr= current_thd; - TABLE_LIST *table; - - if (!logger.is_log_table_enabled(log_table_type)) - return; /* do nothing */ - - switch (log_table_type) { - case QUERY_LOG_GENERAL: - log_thd= general_log_thd; - table= &general_log; - break; - case QUERY_LOG_SLOW: - log_thd= slow_log_thd; - table= &slow_log; - break; - default: - assert(0); // Impossible - } - - /* - Set thread stack start for the logger thread. See comment in - open_log_table() for details. - */ - if (curr) - log_thd->thread_stack= curr->thread_stack; - else - log_thd->thread_stack= (char*) &log_thd; - - /* close the table */ - log_thd->store_globals(); - table->table->file->ha_rnd_end(); - table->table->file->ha_release_auto_increment(); - /* discard logger mark before unlock*/ - table->table->locked_by_logger= FALSE; - close_thread_tables(log_thd, lock_in_use); - - if (curr) - curr->store_globals(); - else - { - my_pthread_setspecific_ptr(THR_THD, 0); - my_pthread_setspecific_ptr(THR_MALLOC, 0); - } -} - - /* Save position of binary log transaction cache. diff --git a/sql/log.h b/sql/log.h index d92e0117bcc..faf6ead450c 100644 --- a/sql/log.h +++ b/sql/log.h @@ -212,6 +212,10 @@ public: return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0, WRITE_CACHE); } + + /* TODO: fix MYSQL_LOG::write to be thread safe instead. */ + inline pthread_mutex_t* get_log_lock() { return &LOCK_log; } + private: time_t last_time; }; @@ -398,7 +402,7 @@ public: const char *sql_text, uint sql_text_len)= 0; virtual bool log_error(enum loglevel level, const char *format, va_list args)= 0; - virtual bool log_general(time_t event_time, const char *user_host, + virtual bool log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, @@ -412,27 +416,7 @@ int check_if_log_table(uint db_len, const char *db, uint table_name_len, class Log_to_csv_event_handler: public Log_event_handler { - /* - We create artificial THD for each of the logs. This is to avoid - locking issues: we don't want locks on the log tables reside in the - THD's of the query. The reason is the locking order and duration. - */ - THD *general_log_thd, *slow_log_thd; - /* - This is for the thread, which called tmp_close_log_tables. The thread - will be allowed to write-lock the log tables (as it explicitly disabled - logging). This is used for such operations as REPAIR, which require - exclusive lock on the log tables. - NOTE: there can be only one priviliged thread, as one should - lock logger with logger.lock() before calling tmp_close_log_tables(). - So no other thread could get privileged status at the same time. - */ - THD *privileged_thread; friend class LOGGER; - TABLE_LIST general_log, slow_log; - -private: - bool open_log_table(uint log_type); public: Log_to_csv_event_handler(); @@ -447,18 +431,13 @@ public: const char *sql_text, uint sql_text_len); virtual bool log_error(enum loglevel level, const char *format, va_list args); - virtual bool log_general(time_t event_time, const char *user_host, + virtual bool log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, - CHARSET_INFO *client_cs); - void tmp_close_log_tables(THD *thd); - void close_log_table(uint log_type, bool lock_in_use); - bool reopen_log_table(uint log_type); - THD* get_privileged_thread() - { - return privileged_thread; - } + CHARSET_INFO *client_cs); + + int activate_log(THD *thd, uint log_type); }; @@ -484,7 +463,7 @@ public: const char *sql_text, uint sql_text_len); virtual bool log_error(enum loglevel level, const char *format, va_list args); - virtual bool log_general(time_t event_time, const char *user_host, + virtual bool log_general(THD *thd, time_t event_time, const char *user_host, uint user_host_len, int thread_id, const char *command_type, uint command_type_len, const char *sql_text, uint sql_text_len, @@ -499,7 +478,7 @@ public: /* Class which manages slow, general and error log event handlers */ class LOGGER { - pthread_mutex_t LOCK_logger; + rw_lock_t LOCK_logger; /* flag to check whether logger mutex is initialized */ uint inited; @@ -519,21 +498,10 @@ public: LOGGER() : inited(0), table_log_handler(NULL), file_log_handler(NULL), is_log_tables_initialized(FALSE) {} - void lock() { (void) pthread_mutex_lock(&LOCK_logger); } - void unlock() { (void) pthread_mutex_unlock(&LOCK_logger); } - void tmp_close_log_tables(THD *thd); - bool is_log_table_enabled(uint log_table_type) - { - switch (log_table_type) { - case QUERY_LOG_SLOW: - return table_log_handler && table_log_handler->slow_log.table != 0; - case QUERY_LOG_GENERAL: - return table_log_handler && table_log_handler->general_log.table != 0; - default: - DBUG_ASSERT(0); - return FALSE; /* make compiler happy */ - } - } + void lock_shared() { rw_rdlock(&LOCK_logger); } + void lock_exclusive() { rw_wrlock(&LOCK_logger); } + void unlock() { rw_unlock(&LOCK_logger); } + bool is_log_table_enabled(uint log_table_type); /* We want to initialize all log mutexes as soon as possible, but we cannot do it in constructor, as safe_mutex relies on @@ -543,20 +511,6 @@ public: void init_base(); void init_log_tables(); bool flush_logs(THD *thd); - THD *get_general_log_thd() - { - if (table_log_handler) - return (THD *) table_log_handler->general_log_thd; - else - return NULL; - } - THD *get_slow_log_thd() - { - if (table_log_handler) - return (THD *) table_log_handler->slow_log_thd; - else - return NULL; - } /* Perform basic logger cleanup. this will leave e.g. error log open. */ void cleanup_base(); /* Free memory. Nothing could be logged after this function is called */ @@ -568,10 +522,6 @@ public: bool general_log_print(THD *thd,enum enum_server_command command, const char *format, va_list args); - void close_log_table(uint log_type, bool lock_in_use); - bool reopen_log_table(uint log_type); - bool reopen_log_tables(); - /* we use this function to setup all enabled log event handlers */ int set_handlers(uint error_log_printer, uint slow_log_printer, @@ -593,19 +543,6 @@ public: return file_log_handler->get_mysql_log(); return NULL; } - THD* get_privileged_thread() - { - if (table_log_handler) - return table_log_handler->get_privileged_thread(); - else - return NULL; - } - bool is_privileged_thread(THD *thd) - { - return thd == get_general_log_thd() || - thd == get_slow_log_thd() || - thd == get_privileged_thread(); - } }; enum enum_binlog_format { diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 7b7bc81957e..1b36d97ad3f 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -944,6 +944,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); +bool is_log_table_write_query(enum enum_sql_command command); bool alloc_query(THD *thd, const char *packet, uint packet_length); void mysql_init_select(LEX *lex); void mysql_reset_thd_for_next_command(THD *thd); @@ -1123,7 +1124,8 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, uint key_length, uint db_flags, int *error); void release_table_share(TABLE_SHARE *share, enum release_type type); TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name); -TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update); +TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, + uint lock_flags); TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, bool *refresh, uint flags); bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in); @@ -1231,6 +1233,11 @@ void reset_status_vars(); /* information schema */ extern LEX_STRING INFORMATION_SCHEMA_NAME; +/* log tables */ +extern LEX_STRING MYSQL_SCHEMA_NAME; +extern LEX_STRING GENERAL_LOG_NAME; +extern LEX_STRING SLOW_LOG_NAME; + extern const LEX_STRING partition_keywords[]; ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name); ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx); @@ -1557,6 +1564,10 @@ bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, void close_system_tables(THD *thd, Open_tables_state *backup); TABLE *open_system_table_for_update(THD *thd, TABLE_LIST *one_table); +TABLE *open_performance_schema_table(THD *thd, TABLE_LIST *one_table, + Open_tables_state *backup); +void close_performance_schema_table(THD *thd, Open_tables_state *backup); + bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables, bool have_lock = FALSE); bool close_cached_connection_tables(THD *thd, bool wait_for_refresh, LEX_STRING *connect_string, @@ -1891,6 +1902,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, #define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004 #define MYSQL_OPEN_TEMPORARY_ONLY 0x0008 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0010 +#define MYSQL_LOCK_PERF_SCHEMA 0x0020 void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); diff --git a/sql/set_var.cc b/sql/set_var.cc index bd5234b42be..d21a4c18716 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2053,21 +2053,15 @@ end: bool sys_var_log_state::update(THD *thd, set_var *var) { - bool res= 0; + bool res; pthread_mutex_lock(&LOCK_global_system_variables); if (!var->save_result.ulong_value) - logger.deactivate_log_handler(thd, log_type); - else { - if ((res= logger.activate_log_handler(thd, log_type))) - { - my_error(ER_CANT_ACTIVATE_LOG, MYF(0), - log_type == QUERY_LOG_GENERAL ? "general" : - "slow query"); - goto err; - } + logger.deactivate_log_handler(thd, log_type); + res= false; } -err: + else + res= logger.activate_log_handler(thd, log_type); pthread_mutex_unlock(&LOCK_global_system_variables); return res; } @@ -2143,7 +2137,7 @@ bool update_sys_var_str_path(THD *thd, sys_var_str *var_str, } pthread_mutex_lock(&LOCK_global_system_variables); - logger.lock(); + logger.lock_exclusive(); if (file_log && log_state) file_log->close(0); @@ -2206,7 +2200,7 @@ static void sys_default_slow_log_path(THD *thd, enum_var_type type) bool sys_var_log_output::update(THD *thd, set_var *var) { pthread_mutex_lock(&LOCK_global_system_variables); - logger.lock(); + logger.lock_exclusive(); logger.init_slow_log(var->save_result.ulong_value); logger.init_general_log(var->save_result.ulong_value); *value= var->save_result.ulong_value; @@ -2219,10 +2213,10 @@ bool sys_var_log_output::update(THD *thd, set_var *var) void sys_var_log_output::set_default(THD *thd, enum_var_type type) { pthread_mutex_lock(&LOCK_global_system_variables); - logger.lock(); - logger.init_slow_log(LOG_TABLE); - logger.init_general_log(LOG_TABLE); - *value= LOG_TABLE; + logger.lock_exclusive(); + logger.init_slow_log(LOG_FILE); + logger.init_general_log(LOG_FILE); + *value= LOG_FILE; logger.unlock(); pthread_mutex_unlock(&LOCK_global_system_variables); } diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 682eee06e02..966e841ccbf 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5936,8 +5936,8 @@ ER_WARN_DEPRECATED_SYNTAX_WITH_VER ER_CANT_WRITE_LOCK_LOG_TABLE eng "You can't write-lock a log table. Only read access is possible" ger "Eine Log-Tabelle kann nicht schreibgesperrt werden. Es ist ohnehin nur Lesezugriff möglich" -ER_CANT_READ_LOCK_LOG_TABLE - eng "You can't use usual read lock with log tables. Try READ LOCAL instead" +ER_CANT_LOCK_LOG_TABLE + eng "You can't use locks with log tables." ger "Log-Tabellen können nicht mit normalen Lesesperren gesperrt werden. Verwenden Sie statt dessen READ LOCAL" ER_FOREIGN_DUPLICATE_KEY 23000 S1009 eng "Upholding foreign key constraints for table '%.192s', entry '%-.192s', key %d would lead to a duplicate entry" diff --git a/sql/slave.cc b/sql/slave.cc index 2e8e3f582de..96a105b06e4 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -977,7 +977,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, thd->proc_info = "Opening master dump table"; tables.lock_type = TL_WRITE; - if (!open_ltable(thd, &tables, TL_WRITE)) + if (!open_ltable(thd, &tables, TL_WRITE, 0)) { sql_print_error("create_table_from_dump: could not open created table"); goto err; diff --git a/sql/sp.cc b/sql/sp.cc index aed4976f839..2111ae8b154 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1067,7 +1067,7 @@ sp_show_status_routine(THD *thd, int type, const char *name_pattern) tables.db= (char*)"mysql"; tables.table_name= tables.alias= (char*)"proc"; - if (! (table= open_ltable(thd, &tables, TL_READ))) + if (! (table= open_ltable(thd, &tables, TL_READ, 0))) { res= SP_OPEN_TABLE_FAILED; goto done; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 67fa380d313..91f1570f653 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1602,7 +1602,7 @@ bool change_password(THD *thd, const char *host, const char *user, } #endif - if (!(table= open_ltable(thd, &tables, TL_WRITE))) + if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) DBUG_RETURN(1); VOID(pthread_mutex_lock(&acl_cache->lock)); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0cd46a7c6fb..5b63eac35dd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -902,8 +902,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, bool found=0; for (TABLE_LIST *table= tables; table; table= table->next_local) { - if ((!table->table || !table->table->s->log_table) && - remove_table_from_cache(thd, table->db, table->table_name, + if (remove_table_from_cache(thd, table->db, table->table_name, RTFC_OWNED_BY_THD_FLAG)) found=1; } @@ -951,8 +950,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, are employed by CREATE TABLE as in this case table simply does not exist yet. */ - if (!table->s->log_table && - (table->needs_reopen_or_name_lock() && table->db_stat)) + if (table->needs_reopen_or_name_lock() && table->db_stat) { found=1; DBUG_PRINT("signal", ("Waiting for COND_refresh")); @@ -2468,8 +2466,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, &state)) { /* - Here we flush tables marked for flush. However we never flush log - tables here. They are flushed only on FLUSH LOGS. + Here we flush tables marked for flush. Normally, table->s->version contains the value of refresh_version from the moment when this table was (re-)opened and added to the cache. @@ -2486,7 +2483,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, c1: name lock t2; -- blocks c2: open t1; -- blocks */ - if (table->needs_reopen_or_name_lock() && !table->s->log_table) + if (table->needs_reopen_or_name_lock()) { DBUG_PRINT("note", ("Found table '%s.%s' with different refresh version", @@ -2962,10 +2959,9 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, for (; table ; table=table->next) { /* - Reopen marked for flush. But close log tables. They are flushed only - explicitly on FLUSH LOGS + Reopen marked for flush. */ - if (table->needs_reopen_or_name_lock() && !table->s->log_table) + if (table->needs_reopen_or_name_lock()) { found=1; if (table->db_stat) @@ -3012,10 +3008,6 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, Wait until all threads has closed the tables in the list We have also to wait if there is thread that has a lock on this table even if the table is closed - NOTE: log tables are handled differently by the logging routines. - E.g. general_log is always opened and locked by the logger - and the table handler used by the logger, will be skipped by - this check. */ bool table_is_used(TABLE *table, bool wait_for_name_lock) @@ -3034,10 +3026,10 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) search= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length, &state)) { - DBUG_PRINT("info", ("share: 0x%lx locked_by_logger: %d " + DBUG_PRINT("info", ("share: 0x%lx " "open_placeholder: %d locked_by_name: %d " "db_stat: %u version: %lu", - (ulong) search->s, search->locked_by_logger, + (ulong) search->s, search->open_placeholder, search->locked_by_name, search->db_stat, search->s->version)); @@ -3049,12 +3041,9 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) - If we are in flush table and we didn't execute the flush - If the table engine is open and it's an old version (We must wait until all engines are shut down to use the table) - However we fo not wait if we encountered a table, locked by the logger. - Log tables are managed separately by logging routines. */ - if (!search->locked_by_logger && - (search->locked_by_name && wait_for_name_lock || - (search->is_name_opened() && search->needs_reopen_or_name_lock()))) + if ( (search->locked_by_name && wait_for_name_lock) || + (search->is_name_opened() && search->needs_reopen_or_name_lock())) DBUG_RETURN(1); } } while ((table=table->next)); @@ -3766,6 +3755,7 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, thd Thread handler table_list Table to open is first table in this list lock_type Lock to use for open + lock_flags Flags passed to mysql_lock_table NOTE This function don't do anything like SP/SF/views/triggers analysis done @@ -3781,7 +3771,8 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, table_list->table table */ -TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) +TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, + uint lock_flags) { TABLE *table; bool refresh; @@ -3816,8 +3807,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) { DBUG_ASSERT(thd->lock == 0); // You must lock everything at once if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) - if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, 0, - &refresh))) + if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, + lock_flags, &refresh))) table= 0; } } @@ -4156,11 +4147,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) DBUG_ASSERT(thd->lock == 0); // You must lock everything at once TABLE **start,**ptr; uint lock_flag= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN; - - /* Ignore GLOBAL READ LOCK and GLOBAL READ_ONLY if called from a logger */ - if (logger.is_privileged_thread(thd)) - lock_flag|= (MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | - MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY); if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count))) DBUG_RETURN(-1); @@ -7189,7 +7175,6 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, else if (in_use != thd) { DBUG_PRINT("info", ("Table was in use by other thread")); - in_use->some_tables_deleted=1; if (table->is_name_opened()) { DBUG_PRINT("info", ("Found another active instance of the table")); @@ -7625,7 +7610,7 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, if (!table) goto error; - DBUG_ASSERT(table->s->system_table); + DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM); table->use_all_columns(); table->reginfo.lock_type= tables->lock_type; @@ -7692,12 +7677,91 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table) { DBUG_ENTER("open_system_table_for_update"); - TABLE *table= open_ltable(thd, one_table, one_table->lock_type); + TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0); if (table) { - DBUG_ASSERT(table->s->system_table); + DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM); table->use_all_columns(); } DBUG_RETURN(table); } + +/** + Open a performance schema table. + Opening such tables is performed internally in the server + implementation, and is a 'nested' open, since some tables + might be already opened by the current thread. + The thread context before this call is saved, and is restored + when calling close_performance_schema_table(). + @param thd The current thread + @param one_table Performance schema table to open + @param backup [out] Temporary storage used to save the thread context +*/ +TABLE * +open_performance_schema_table(THD *thd, TABLE_LIST *one_table, + Open_tables_state *backup) +{ + uint flags= ( MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK + | MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY + | MYSQL_LOCK_PERF_SCHEMA ); + + DBUG_ENTER("open_performance_schema_table"); + + thd->reset_n_backup_open_tables_state(backup); + + TABLE *table= open_ltable(thd, one_table, one_table->lock_type, flags); + if (table) + { + DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_PERFORMANCE); + /* Make sure all columns get assigned to a default value */ + table->use_all_columns(); + } + + DBUG_RETURN(table); +} + +/** + Close a performance schema table. + The last table opened by open_performance_schema_table() + is closed, then the thread context is restored. + @param thd The current thread + @param backup [in] the context to restore. +*/ +void close_performance_schema_table(THD *thd, Open_tables_state *backup) +{ + bool found_old_table; + + if (thd->lock) + { + /* + Note: + We do not create explicitly a separate transaction for the + performance table I/O, but borrow the current transaction. + lock + unlock will autocommit the change done in the + performance schema table: this is the expected result. + The current transaction should not be affected by this code. + TODO: Note that if a transactional engine is used for log tables, + this code will need to be revised, as a separate transaction + might be needed. + */ + mysql_unlock_tables(thd, thd->lock); + thd->lock= 0; + } + + safe_mutex_assert_not_owner(&LOCK_open); + + pthread_mutex_lock(&LOCK_open); + + found_old_table= false; + while (thd->open_tables) + found_old_table|= close_thread_table(thd, &thd->open_tables); + + if (found_old_table) + broadcast_refresh(); + + pthread_mutex_unlock(&LOCK_open); + + thd->restore_backup_open_tables_state(backup); +} + diff --git a/sql/sql_class.h b/sql/sql_class.h index 71c13e001ee..d8005c40522 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2438,6 +2438,7 @@ public: #define CF_HAS_ROW_COUNT 2 #define CF_STATUS_COMMAND 4 #define CF_SHOW_TABLE_COMMAND 8 +#define CF_WRITE_LOGS_COMMAND 16 /* Functions in sql_class.cc */ diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 7c868092921..7d8fc119cf9 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -906,9 +906,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) char path[FN_REFLEN]; TABLE *table; bool error; - uint closed_log_tables= 0, lock_logger= 0; uint path_length; - uint log_type; DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); @@ -960,18 +958,6 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) DBUG_RETURN(TRUE); } - log_type= check_if_log_table(table_list->db_length, table_list->db, - table_list->table_name_length, - table_list->table_name, 1); - /* close log tables in use */ - if (log_type) - { - lock_logger= 1; - logger.lock(); - logger.close_log_table(log_type, FALSE); - closed_log_tables= closed_log_tables | log_type; - } - // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this // crashes, replacement works. *(path + path_length - reg_ext_length)= // '\0'; @@ -997,14 +983,6 @@ end: VOID(pthread_mutex_lock(&LOCK_open)); unlock_table_name(thd, table_list); VOID(pthread_mutex_unlock(&LOCK_open)); - - if (opt_slow_log && (closed_log_tables & QUERY_LOG_SLOW)) - logger.reopen_log_table(QUERY_LOG_SLOW); - - if (opt_log && (closed_log_tables & QUERY_LOG_GENERAL)) - logger.reopen_log_table(QUERY_LOG_GENERAL); - if (lock_logger) - logger.unlock(); } else if (error) { diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 5ebeba6109a..8bdb2e59ed5 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -137,6 +137,9 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, level= MYSQL_ERROR::WARN_LEVEL_ERROR; } + if (thd->handle_error(code, level)) + DBUG_RETURN(NULL); + if (thd->spcont && thd->spcont->handle_error(code, level, thd)) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index b747c706f75..dc5db54e128 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2254,7 +2254,7 @@ pthread_handler_t handle_delayed_insert(void *arg) } /* open table */ - if (!(di->table=open_ltable(thd,&di->table_list,TL_WRITE_DELAYED))) + if (!(di->table=open_ltable(thd, &di->table_list, TL_WRITE_DELAYED, 0))) { thd->fatal_error(); // Abort waiting inserts goto err; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f2a61b7f7c5..c2820581d71 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -197,8 +197,8 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA; @@ -210,8 +210,8 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA; - sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; @@ -250,6 +250,14 @@ void init_update_queries(void) */ sql_command_flags[SQLCOM_CALL]= CF_HAS_ROW_COUNT; sql_command_flags[SQLCOM_EXECUTE]= CF_HAS_ROW_COUNT; + + /* + The following admin table operations are allowed + on log tables. + */ + sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND; } @@ -259,6 +267,17 @@ bool is_update_query(enum enum_sql_command command) return (sql_command_flags[command] & CF_CHANGES_DATA) != 0; } +/** + Check if a sql command is allowed to write to log tables. + @param command The SQL command + @return true if writing is allowed +*/ +bool is_log_table_write_query(enum enum_sql_command command) +{ + DBUG_ASSERT(command >= 0 && command <= SQLCOM_END); + return (sql_command_flags[command] & CF_WRITE_LOGS_COMMAND) != 0; +} + void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex) { @@ -493,7 +512,7 @@ int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name) if (lower_case_table_names) my_casedn_str(files_charset_info, tbl_name); - if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT))) + if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT, 0))) DBUG_RETURN(1); if (check_one_table_access(thd, SELECT_ACL, table_list)) diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index a44e20b8daf..0d088063462 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1617,7 +1617,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl DBUG_RETURN(TRUE); /* need to open before acquiring LOCK_plugin or it will deadlock */ - if (! (table = open_ltable(thd, &tables, TL_WRITE))) + if (! (table = open_ltable(thd, &tables, TL_WRITE, 0))) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_plugin); @@ -1674,7 +1674,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) tables.table_name= tables.alias= (char *)"plugin"; /* need to open before acquiring LOCK_plugin or it will deadlock */ - if (! (table= open_ltable(thd, &tables, TL_WRITE))) + if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) DBUG_RETURN(TRUE); pthread_mutex_lock(&LOCK_plugin); diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index f5e1b8988f3..750bcd50479 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -37,7 +37,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) TABLE_LIST *ren_table= 0; int to_table; char *rename_log_table[2]= {NULL, NULL}; - int disable_logs= 0; DBUG_ENTER("mysql_rename_tables"); /* @@ -80,12 +79,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) ren_table->table_name, 1))) { /* - Log table encoutered we will need to disable and lock logs - for duration of rename. - */ - disable_logs= TRUE; - - /* as we use log_table_rename as an array index, we need it to start with 0, while QUERY_LOG_SLOW == 1 and QUERY_LOG_GENERAL == 2. So, we shift the value to start with 0; @@ -136,12 +129,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) rename_log_table[1]); DBUG_RETURN(1); } - - if (disable_logs) - { - logger.lock(); - logger.tmp_close_log_tables(thd); - } } pthread_mutex_lock(&LOCK_open); @@ -200,13 +187,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) pthread_mutex_unlock(&LOCK_open); err: - /* enable logging back if needed */ - if (disable_logs) - { - if (logger.reopen_log_tables()) - error= TRUE; - logger.unlock(); - } start_waiting_global_read_lock(thd); DBUG_RETURN(error); } diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index ac5ea6f4ac4..911372d5f4e 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -366,7 +366,7 @@ insert_server(THD *thd, FOREIGN_SERVER *server) tables.alias= tables.table_name= (char*) "servers"; /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ - if (! (table= open_ltable(thd, &tables, TL_WRITE))) + if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) goto end; /* insert the server into the table */ @@ -588,7 +588,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options) if ((error= delete_server_record_in_cache(server_options))) goto end; - if (! (table= open_ltable(thd, &tables, TL_WRITE))) + if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) { error= my_errno; goto end; @@ -705,7 +705,7 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered) tables.db= (char*)"mysql"; tables.alias= tables.table_name= (char*)"servers"; - if (!(table= open_ltable(thd, &tables, TL_WRITE))) + if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) { error= my_errno; goto end; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 5b8cb93baab..fdd75fbe844 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2176,9 +2176,6 @@ void calc_sum_of_all_status(STATUS_VAR *to) } -/* INFORMATION_SCHEMA name */ -LEX_STRING INFORMATION_SCHEMA_NAME= { C_STRING_WITH_LEN("information_schema")}; - /* This is only used internally, but we need it here as a forward reference */ extern ST_SCHEMA_TABLE schema_tables[]; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index dc3e72554fa..502600a673f 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1513,7 +1513,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->db_type= share->db_type(); /* Disable drop of enabled log tables */ - if (share && share->log_table && + if (share && (share->table_category == TABLE_CATEGORY_PERFORMANCE) && check_if_log_table(table->db_length, table->db, table->table_name_length, table->table_name, 1)) { @@ -3966,7 +3966,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, Item *item; Protocol *protocol= thd->protocol; LEX *lex= thd->lex; - int result_code, disable_logs= 0; + int result_code; DBUG_ENTER("mysql_admin_table"); if (end_active_trans(thd)) @@ -4014,22 +4014,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (view_operator_func == NULL) table->required_type=FRMTYPE_TABLE; - /* - If we want to perform an admin operation on the log table - (E.g. rename) and lock_type >= TL_READ_NO_INSERT disable - log tables - */ - - if (check_if_log_table(table->db_length, table->db, - table->table_name_length, - table->table_name, 1) && - lock_type >= TL_READ_NO_INSERT) - { - disable_logs= 1; - logger.lock(); - logger.tmp_close_log_tables(thd); - } - open_and_lock_tables(thd, table); thd->no_warnings_for_error= 0; table->next_global= save_next_global; @@ -4099,8 +4083,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, } /* Close all instances of the table to allow repair to rename files */ - if (lock_type == TL_WRITE && table->table->s->version && - !table->table->s->log_table) + if (lock_type == TL_WRITE && table->table->s->version) { pthread_mutex_lock(&LOCK_open); const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open, @@ -4258,7 +4241,7 @@ send_result_message: close_thread_tables(thd); if (!result_code) // recreation went ok { - if ((table->table= open_ltable(thd, table, lock_type)) && + if ((table->table= open_ltable(thd, table, lock_type, 0)) && ((result_code= table->table->file->analyze(thd, check_opt)) > 0)) result_code= 0; // analyze went ok } @@ -4324,10 +4307,9 @@ send_result_message: } if (table->table) { - /* in the below check we do not refresh the log tables */ if (fatal_error) table->table->s->version=0; // Force close of table - else if (open_for_modify && !table->table->s->log_table) + else if (open_for_modify) { if (table->table->s->tmp_table) table->table->file->info(HA_STATUS_CONST); @@ -4350,24 +4332,11 @@ send_result_message: } send_eof(thd); - if (disable_logs) - { - if (logger.reopen_log_tables()) - my_error(ER_CANT_ACTIVATE_LOG, MYF(0)); - logger.unlock(); - } DBUG_RETURN(FALSE); err: ha_autocommit_or_rollback(thd, 1); close_thread_tables(thd); // Shouldn't be needed - /* enable logging back if needed */ - if (disable_logs) - { - if (logger.reopen_log_tables()) - my_error(ER_CANT_ACTIVATE_LOG, MYF(0)); - logger.unlock(); - } if (table) table->table=0; DBUG_RETURN(TRUE); @@ -4812,7 +4781,7 @@ mysql_discard_or_import_tablespace(THD *thd, not complain when we lock the table */ thd->tablespace_op= TRUE; - if (!(table=open_ltable(thd,table_list,TL_WRITE))) + if (!(table=open_ltable(thd, table_list, TL_WRITE, 0))) { thd->tablespace_op=FALSE; DBUG_RETURN(-1); @@ -5728,7 +5697,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, #ifdef WITH_PARTITION_STORAGE_ENGINE if (alter_info->flags & ALTER_PARTITION) { - my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table"); + my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table"); DBUG_RETURN(TRUE); } #endif @@ -5817,7 +5786,7 @@ view_err: start_waiting_global_read_lock(thd); DBUG_RETURN(error); } - if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ))) + if (!(table=open_ltable(thd, table_list, TL_WRITE_ALLOW_READ, 0))) DBUG_RETURN(TRUE); table->use_all_columns(); @@ -6986,7 +6955,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, strxmov(table_name, table->db ,".", table->table_name, NullS); - t= table->table= open_ltable(thd, table, TL_READ); + t= table->table= open_ltable(thd, table, TL_READ, 0); thd->clear_error(); // these errors shouldn't get client protocol->prepare_for_resend(); diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 8361fc64f33..10bb7844d88 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -472,7 +472,7 @@ int mysql_create_function(THD *thd,udf_func *udf) tables.db= (char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; /* Allow creation of functions even if we can't open func table */ - if (!(table = open_ltable(thd,&tables,TL_WRITE))) + if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; table->use_all_columns(); restore_record(table, s->default_values); // Default values for fields @@ -547,7 +547,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) bzero((char*) &tables,sizeof(tables)); tables.db=(char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; - if (!(table = open_ltable(thd,&tables,TL_WRITE))) + if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; table->use_all_columns(); table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); diff --git a/sql/table.cc b/sql/table.cc index 5ac43343934..fef63170ea6 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -21,6 +21,18 @@ #include <m_ctype.h> #include "md5.h" +/* INFORMATION_SCHEMA name */ +LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")}; + +/* MYSQL_SCHEMA name */ +LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")}; + +/* GENERAL_LOG name */ +LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")}; + +/* SLOW_LOG name */ +LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")}; + /* Functions defined in this file */ void open_table_error(TABLE_SHARE *share, int error, int db_errno, @@ -31,6 +43,7 @@ static void fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types, char **names); static uint find_field(Field **fields, uchar *record, uint start, uint length); +inline bool is_system_table_name(const char *name, uint length); /************************************************************************** Object_creation_ctx implementation. @@ -192,6 +205,49 @@ char *fn_rext(char *name) return name + strlen(name); } +TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name) +{ + DBUG_ASSERT(db != NULL); + DBUG_ASSERT(name != NULL); + + if ((db->length == INFORMATION_SCHEMA_NAME.length) && + (my_strcasecmp(system_charset_info, + INFORMATION_SCHEMA_NAME.str, + db->str) == 0)) + { + return TABLE_CATEGORY_INFORMATION; + } + + if ((db->length == MYSQL_SCHEMA_NAME.length) && + (my_strcasecmp(system_charset_info, + MYSQL_SCHEMA_NAME.str, + db->str) == 0)) + { + if (is_system_table_name(name->str, name->length)) + { + return TABLE_CATEGORY_SYSTEM; + } + + if ((name->length == GENERAL_LOG_NAME.length) && + (my_strcasecmp(system_charset_info, + GENERAL_LOG_NAME.str, + name->str) == 0)) + { + return TABLE_CATEGORY_PERFORMANCE; + } + + if ((name->length == SLOW_LOG_NAME.length) && + (my_strcasecmp(system_charset_info, + SLOW_LOG_NAME.str, + name->str) == 0)) + { + return TABLE_CATEGORY_PERFORMANCE; + } + } + + return TABLE_CATEGORY_USER; +} + /* Allocate a setup TABLE_SHARE structure @@ -297,7 +353,8 @@ void init_tmp_table_share(TABLE_SHARE *share, const char *key, bzero((char*) share, sizeof(*share)); init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); - share->tmp_table= INTERNAL_TMP_TABLE; + share->table_category= TABLE_CATEGORY_TEMPORARY; + share->tmp_table= INTERNAL_TMP_TABLE; share->db.str= (char*) key; share->db.length= strlen(key); share->table_cache_key.str= (char*) key; @@ -544,28 +601,11 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) *root_ptr= &share->mem_root; error= open_binary_frm(thd, share, head, file); *root_ptr= old_root; - - if (share->db.length == 5 && !(lower_case_table_names ? - my_strcasecmp(system_charset_info, share->db.str, "mysql") : - strcmp(share->db.str, "mysql"))) - { - /* - We can't mark all tables in 'mysql' database as system since we don't - allow to lock such tables for writing with any other tables (even with - other system tables) and some privilege tables need this. - */ - share->system_table= is_system_table_name(share->table_name.str, - share->table_name.length); - if (!share->system_table) - { - share->log_table= check_if_log_table(share->db.length, share->db.str, - share->table_name.length, - share->table_name.str, 0); - } - } error_given= 1; } + share->table_category= get_table_category(& share->db, & share->table_name); + if (!error) thd->status_var.opened_shares++; diff --git a/sql/table.h b/sql/table.h index 494b74d564c..77d8e819d3e 100644 --- a/sql/table.h +++ b/sql/table.h @@ -140,6 +140,100 @@ class Field_timestamp; class Field_blob; class Table_triggers_list; +/** + Category of table found in the table share. +*/ +enum enum_table_category +{ + /** + Unknown value. + */ + TABLE_UNKNOWN_CATEGORY=0, + + /** + Temporary table. + The table is visible only in the session. + Therefore, + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + do not apply to this table. + Note that LOCK TABLE <t> FOR READ/WRITE + can be used on temporary tables. + Temporary tables are not part of the table cache. + */ + TABLE_CATEGORY_TEMPORARY=1, + + /** + User table. + These tables do honor: + - LOCK TABLE <t> FOR READ/WRITE + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + User tables are cached in the table cache. + */ + TABLE_CATEGORY_USER=2, + + /** + System table, maintained by the server. + These tables do honor: + - LOCK TABLE <t> FOR READ/WRITE + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + Typically, writes to system tables are performed by + the server implementation, not explicitly be a user. + System tables are cached in the table cache. + */ + TABLE_CATEGORY_SYSTEM=3, + + /** + Information schema tables. + These tables are an interface provided by the system + to inspect the system metadata. + These tables do *not* honor: + - LOCK TABLE <t> FOR READ/WRITE + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + as there is no point in locking explicitely + an INFORMATION_SCHEMA table. + Nothing is directly written to information schema tables. + Note that this value is not used currently, + since information schema tables are not shared, + but implemented as session specific temporary tables. + */ + /* + TODO: Fixing the performance issues of I_S will lead + to I_S tables in the table cache, which should use + this table type. + */ + TABLE_CATEGORY_INFORMATION=4, + + /** + Performance schema tables. + These tables are an interface provided by the system + to inspect the system performance data. + These tables do *not* honor: + - LOCK TABLE <t> FOR READ/WRITE + - FLUSH TABLES WITH READ LOCK + - SET GLOBAL READ_ONLY = ON + as there is no point in locking explicitely + a PERFORMANCE_SCHEMA table. + An example of PERFORMANCE_SCHEMA tables are: + - mysql.slow_log + - mysql.general_log, + which *are* updated even when there is either + a GLOBAL READ LOCK or a GLOBAL READ_ONLY in effect. + User queries do not write directly to these tables + (there are exceptions for log tables). + The server implementation perform writes. + Performance tables are cached in the table cache. + */ + TABLE_CATEGORY_PERFORMANCE=5 +}; +typedef enum enum_table_category TABLE_CATEGORY; + +TABLE_CATEGORY get_table_category(const LEX_STRING *db, + const LEX_STRING *name); + /* This structure is shared between different table objects. There is one instance of table share per one table in the database. @@ -148,6 +242,10 @@ class Table_triggers_list; typedef struct st_table_share { st_table_share() {} /* Remove gcc warning */ + + /** Category of this table. */ + TABLE_CATEGORY table_category; + /* hash of field names (contains pointers to elements of field array) */ HASH name_hash; /* hash of field names */ MEM_ROOT mem_root; @@ -259,18 +357,6 @@ typedef struct st_table_share */ int cached_row_logging_check; - /* - TRUE if this is a system table like 'mysql.proc', which we want to be - able to open and lock even when we already have some tables open and - locked. To avoid deadlocks we have to put certain restrictions on - locking of this table for writing. FALSE - otherwise. - */ - bool system_table; - /* - This flag is set for the log tables. Used during FLUSH instances to skip - log tables, while closing tables (since logs must be always available) - */ - bool log_table; #ifdef WITH_PARTITION_STORAGE_ENGINE bool auto_partitioned; const char *partition_info; @@ -334,6 +420,16 @@ typedef struct st_table_share set_table_cache_key(key_buff, key_length); } + inline bool honor_global_locks() + { + return ((table_category == TABLE_CATEGORY_USER) + || (table_category == TABLE_CATEGORY_SYSTEM)); + } + + inline bool require_write_privileges() + { + return (table_category == TABLE_CATEGORY_PERFORMANCE); + } } TABLE_SHARE; diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index 34c1fcde58d..00ca2a6c07f 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -786,18 +786,6 @@ void ha_tina::update_status() } -bool ha_tina::check_if_locking_is_allowed(uint sql_command, - ulong type, TABLE *table, - uint count, uint current, - uint *system_count, - bool called_by_privileged_thread) -{ - if (!called_by_privileged_thread) - return check_if_log_table_locking_is_allowed(sql_command, type, table); - - return TRUE; -} - /* Open a database file. Keep in mind that tables are caches, so this will not be called for every request. Any sort of positions diff --git a/storage/csv/ha_tina.h b/storage/csv/ha_tina.h index 5bb3e9a79a0..5ce09783b9b 100644 --- a/storage/csv/ha_tina.h +++ b/storage/csv/ha_tina.h @@ -131,11 +131,6 @@ public: */ ha_rows estimate_rows_upper_bound() { return HA_POS_ERROR; } - virtual bool check_if_locking_is_allowed(uint sql_command, - ulong type, TABLE *table, - uint count, uint current, - uint *system_count, - bool called_by_logger_thread); int open(const char *name, int mode, uint open_options); int close(void); int write_row(uchar * buf); diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index c86459ae0a7..77334b2cfba 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -607,41 +607,7 @@ err: #endif /* HAVE_REPLICATION */ -bool ha_myisam::check_if_locking_is_allowed(uint sql_command, - ulong type, TABLE *table, - uint count, uint current, - uint *system_count, - bool called_by_privileged_thread) -{ - /* - To be able to open and lock for reading system tables like 'mysql.proc', - when we already have some tables opened and locked, and avoid deadlocks - we have to disallow write-locking of these tables with any other tables. - */ - if (table->s->system_table && - table->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE) - (*system_count)++; - - /* 'current' is an index, that's why '<=' below. */ - if (*system_count > 0 && *system_count <= current) - { - my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0)); - return FALSE; - } - - /* - Deny locking of the log tables, which is incompatible with - concurrent insert. Unless called from a logger THD (general_log_thd - or slow_log_thd) or by a privileged thread. - */ - if (!called_by_privileged_thread) - return check_if_log_table_locking_is_allowed(sql_command, type, table); - - return TRUE; -} - - /* Name is here without an extension */ - +/* Name is here without an extension */ int ha_myisam::open(const char *name, int mode, uint test_if_locked) { MI_KEYDEF *keyinfo; diff --git a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h index 024675075c2..635f314b3da 100644 --- a/storage/myisam/ha_myisam.h +++ b/storage/myisam/ha_myisam.h @@ -60,11 +60,6 @@ class ha_myisam: public handler uint max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; } uint checksum() const; - virtual bool check_if_locking_is_allowed(uint sql_command, - ulong type, TABLE *table, - uint count, uint current, - uint *system_count, - bool called_by_logger_thread); int open(const char *name, int mode, uint test_if_locked); int close(void); int write_row(uchar * buf); |