WL#3984 (Revise locking of mysql.general_log and mysql.slow_log)

Bug#25422 (Hang with log tables)
Bug 17876 (Truncating mysql.slow_log in a SP after using cursor locks the
          thread)
Bug 23044 (Warnings on flush of a log table)
Bug 29129 (Resetting general_log while the GLOBAL READ LOCK is set causes
           a deadlock)

Prior to this fix, the server would hang when performing concurrent
ALTER TABLE or TRUNCATE TABLE statements against the LOG TABLES,
which are mysql.general_log and mysql.slow_log.

The root cause traces to the following code:
in sql_base.cc, open_table()
  if (table->in_use != thd)
  {
    /* wait_for_condition will unlock LOCK_open for us */
    wait_for_condition(thd, &LOCK_open, &COND_refresh);
  }
The problem with this code is that the current implementation of the
LOGGER creates 'fake' THD objects, like
- Log_to_csv_event_handler::general_log_thd
- Log_to_csv_event_handler::slow_log_thd
which are not associated to a real thread running in the server,
so that waiting for these non-existing threads to release table locks
cause the dead lock.

In general, the design of Log_to_csv_event_handler does not fit into the
general architecture of the server, so that the concept of general_log_thd
and slow_log_thd has to be abandoned:
- this implementation does not work with table locking
- it will not work with commands like SHOW PROCESSLIST
- having the log tables always opened does not integrate well with DDL
operations / FLUSH TABLES / SET GLOBAL READ_ONLY

With this patch, the fundamental design of the LOGGER has been changed to:
- always open and close a log table when writing a log
- remove totally the usage of fake THD objects
- clarify how locking of log tables is implemented in general.

See WL#3984 for details related to the new locking design.

Additional changes (misc bugs exposed and fixed):

1)

mysqldump which would ignore some tables in dump_all_tables_in_db(),
 but forget to ignore the same in dump_all_views_in_db().

2)

mysqldump would also issue an empty "LOCK TABLE" command when all the tables
to lock are to be ignored (numrows == 0), instead of not issuing the query.

3)

Internal errors handlers could intercept errors but not warnings
(see sql_error.cc).

4)

Implementing a nested call to open tables, for the performance schema tables,
exposed an existing bug in remove_table_from_cache(), which would perform:
  in_use->some_tables_deleted=1;
against another thread, without any consideration about thread locking.
This call inside remove_table_from_cache() was not required anyway,
since calling mysql_lock_abort() takes care of aborting -- cleanly -- threads
that might hold a lock on a table.
This line (in_use->some_tables_deleted=1) has been removed.
This commit is contained in:
malff/marcsql@weblab.(none) 2007-07-27 00:31:06 -06:00
parent cbd6e56ffa
commit c7bbd8917c
39 changed files with 1490 additions and 993 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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
#

View File

@ -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;
# Misc locking tests
show create table mysql.general_log;
show fields from mysql.general_log;
show create table mysql.slow_log;
show fields from mysql.slow_log;
#
# check that FLUSH LOGS waits for all readers of the log table to vanish
# check that FLUSH LOGS does not flush the log tables
#
connect (con1,localhost,root,,);
connect (con2,localhost,root,,);
flush logs;
flush tables;
connection con1;
SET GLOBAL GENERAL_LOG=ON;
SET GLOBAL SLOW_QUERY_LOG=ON;
lock tables mysql.general_log READ LOCAL;
connection con2;
# 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;
select "Mark that we woke up from flush logs in the test"
as "test passed";
show open tables;
flush logs;
show open tables;
#
# perform the same check for TRUNCATE: it should also wait for readers
# to disappear
# check that FLUSH TABLES does flush the log tables
#
connection con1;
flush tables;
# Since the flush is logged, mysql.general_log will be in the cache
show open tables;
lock tables mysql.general_log READ LOCAL;
SET GLOBAL GENERAL_LOG=OFF;
SET GLOBAL SLOW_QUERY_LOG=OFF;
connection con2;
flush tables;
# Here the table cache is empty
show open tables;
# this should wait for log tables to unlock
send truncate mysql.general_log;
connection con1;
unlock tables;
# this connection should be alive by the time
connection con2;
reap;
select "Mark that we woke up from TRUNCATE in the test"
as "test passed";
connection con1;
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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
/*

View File

@ -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(&current_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.

View File

@ -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 {

View File

@ -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);

View File

@ -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);
}

View File

@ -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"

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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);
}

View File

@ -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 */

View File

@ -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)
{

View File

@ -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))
{

View File

@ -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;

View File

@ -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))

View File

@ -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);

View File

@ -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");
/*
@ -79,12 +78,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
ren_table->table_name_length,
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.
@ -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);
}

View File

@ -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;

View File

@ -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[];

View File

@ -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();

View File

@ -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);

View File

@ -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++;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);