Commit 9438b985 authored by unknown's avatar unknown
Browse files

Fix for Bug #17544 "Cannot do atomic log rotate",

Bug #21785 "Server crashes after rename of the log table" and
Bug #21966 "Strange warnings on create like/repair of the log
            tables"

According to the patch, from now on, one should use RENAME to
perform a log table rotation (this should also be reflected in
the manual).

Here is a sample:

use mysql;
CREATE TABLE IF NOT EXISTS general_log2 LIKE general_log;
RENAME TABLE general_log TO general_log_backup, general_log2 TO general_log;

The rules for Rename of the log tables are following:
      IF   1. Log tables are enabled
      AND  2. Rename operates on the log table and nothing is being
              renamed to the log table.
      DO   3. Throw an error message.
      ELSE 4. Perform rename.
 
The very RENAME query will go the the old (backup) table. This is
consistent with the behavoiur we have with binlog ROTATE LOGS
statement.

Other problems, which are solved by the patch are:

1) Now REPAIR of the log table is exclusive operation (as it should be), this
   also eliminates lock-related warnings. and
2) CREATE LIKE TABLE now usese usual read lock on the source table rather
   then name lock, which is too restrictive. This way we get rid of another
   log table-related warning, which occured because of the above fact
   (as a side-effect, name lock resulted in a warning).


mysql-test/r/log_tables.result:
  update result file
mysql-test/t/log_tables.test:
  Add tests for the bugs
sql/handler.cc:
  update comment
sql/handler.h:
  update function to reflect changes in log tables
  locking logic.
sql/lock.cc:
  Now we allow locking of the log tables for "privileged" threads
  Privileged thread must explicitly close and lock log tables. This
  is required for admin operations such as REPAIR.
sql/log.cc:
  Changes to the file:
  1) Add checks for table schema. It's more important now,
     as we allow rename of the log tables. Since we should
     check for schema when writing to a log table.
     E.g. if one created a table with one-only comlumn and
     renamed it to general_log, the server should cope with
     it.
  2) refactor LOGGER::flush(), so that we can now use the same
     machinery as we use in FLUSH LOGS in other statements:
     whenever we have to perform  a serious operation on the log
     tables, we have to
     (a) lock logger, which blocks other concurrent statements (such 
     as selects) (b) close logs. Then perform an
     exclusive operation, c) reenable logs and d) unlock logger.
  3) Add a function to check if a given table is a log table.
  4) Add support for "privileged" thread
  5) merge is_[general/slow]_log_table_enabled() into one function.
  6) Add new function: reopen _log_tables, which reopens the tables,
     which were enabled (after temporary close, required for admin
     operation)
sql/log.h:
  1) add a new call close_n_lock_tables(). Now we use it instead of
     LOGGER::flush() in FLUSH LOGS implementation.
  2) add a prototype for the function to check if a given
     table is a log table;
  3) add privileged table flag to table logger
  4) merge is_[general/slow]_log_table_enabled()
     into one function.
sql/mysql_priv.h:
  move log table defines to log.h
sql/sql_delete.cc:
  use new function check_if_log_table() instead of direct strcmp
sql/sql_rename.cc:
  Traverse the list of tables in mysql_rename_tables
  to make sure that log tables are processed correctly
  (that is, according to the rules specified in the
  main CS comment)
sql/sql_table.cc:
  1) mysql_admin_table() should disable logs if it performs
     exclusive admin operation on a log table. This way we
     also eliminate warning on REPAIR of the log table.
  2) mysql_create_like_table should read-lock the source table
     instead getting name lock on it. Name lock is too restrictive
     in this case.
sql/share/errmsg.txt:
  Add a new error message for rename of the log tables
sql/table.cc:
  use new function instead of direct strcmp.
  change my_strcasecmp() -> strcmp(), when
  comparing system db and table names
storage/csv/ha_tina.cc:
  update function to reflect changes in log tables
  locking logic.
storage/myisam/ha_myisam.cc:
  update function to reflect changes in log tables
  locking logic.
parent d57163fc
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
@@ -218,3 +218,71 @@ unlock tables;
use mysql;
lock tables general_log read local, help_category read local;
unlock tables;
use mysql;
RENAME TABLE general_log TO renamed_general_log;
ERROR HY000: Cannot rename 'general_log'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to 'general_log'
RENAME TABLE slow_log TO renamed_slow_log;
ERROR HY000: Cannot rename 'slow_log'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to 'slow_log'
truncate table general_log;
select * from general_log;
event_time	user_host	thread_id	server_id	command_type	argument
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	select * from general_log
truncate table slow_log;
select * from slow_log;
start_time	user_host	query_time	lock_time	rows_sent	rows_examined	db	last_insert_id	insert_id	server_id	sql_text
create table general_log_new like general_log;
rename table general_log TO renamed_general_log, general_log_new TO general_log;
create table slow_log_new like slow_log;
rename table slow_log TO renamed_slow_log, slow_log_new TO slow_log;
rename table general_log TO general_log_new, renamed_general_log TO general_log, slow_log to renamed_slow_log;
ERROR HY000: Cannot rename 'slow_log'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to 'slow_log'
select * from general_log;
event_time	user_host	thread_id	server_id	command_type	argument
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	create table slow_log_new like slow_log
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	rename table slow_log TO renamed_slow_log, slow_log_new TO slow_log
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	rename table general_log TO general_log_new, renamed_general_log TO general_log, slow_log to renamed_slow_log
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	select * from general_log
select * from renamed_general_log;
event_time	user_host	thread_id	server_id	command_type	argument
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	select * from general_log
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	truncate table slow_log
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	select * from slow_log
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	create table general_log_new like general_log
TIMESTAMP	USER_HOST	THREAD_ID	1	Query	rename table general_log TO renamed_general_log, general_log_new TO general_log
select * from slow_log;
start_time	user_host	query_time	lock_time	rows_sent	rows_examined	db	last_insert_id	insert_id	server_id	sql_text
select * from renamed_slow_log;
start_time	user_host	query_time	lock_time	rows_sent	rows_examined	db	last_insert_id	insert_id	server_id	sql_text
set global general_log='OFF';
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
set global slow_query_log='ON';
ERROR HY000: Cannot activate 'slow query' log
RENAME TABLE general_log2 TO general_log;
RENAME TABLE slow_log2 TO slow_log;
set global general_log='ON';
set global slow_query_log='ON';
flush logs;
flush logs;
drop table renamed_general_log, renamed_slow_log;
use test;
use mysql;
repair table general_log;
Table	Op	Msg_type	Msg_text
mysql.general_log	repair	status	OK
repair table slow_log;
Table	Op	Msg_type	Msg_text
mysql.slow_log	repair	status	OK
create table general_log_new like general_log;
create table slow_log_new like slow_log;
show tables like "%log%";
Tables_in_mysql (%log%)
general_log
general_log_new
slow_log
slow_log_new
drop table slow_log_new, general_log_new;
use test;
+83 −0
Original line number Diff line number Diff line
@@ -314,6 +314,89 @@ use mysql;
lock tables general_log read local, help_category read local;
unlock tables;

#
# Bug #17544 Cannot do atomic log rotate and
# Bug #21785 Server crashes after rename of the log table
#

use mysql;
# Should result in error
--error ER_CANT_RENAME_LOG_TABLE
RENAME TABLE general_log TO renamed_general_log;
--error ER_CANT_RENAME_LOG_TABLE
RENAME TABLE slow_log TO renamed_slow_log;

#check rotate logs
truncate table general_log;
--replace_column 1 TIMESTAMP 2 USER_HOST 3 THREAD_ID
select * from general_log;

truncate table slow_log;
--replace_column 1 TIMESTAMP 2 USER_HOST
select * from slow_log;

create table general_log_new like general_log;
rename table general_log TO renamed_general_log, general_log_new TO general_log;

create table slow_log_new like slow_log;
rename table slow_log TO renamed_slow_log, slow_log_new TO slow_log;

# check that rename checks more then first table in the list
--error ER_CANT_RENAME_LOG_TABLE
rename table general_log TO general_log_new, renamed_general_log TO general_log, slow_log to renamed_slow_log;

# now check the content of tables
--replace_column 1 TIMESTAMP 2 USER_HOST 3 THREAD_ID
select * from general_log;
--replace_column 1 TIMESTAMP 2 USER_HOST 3 THREAD_ID
select * from renamed_general_log;

# the content of the slow log is empty, but we will try a select anyway
--replace_column 1 TIMESTAMP 2 USER_HOST
select * from slow_log;
--replace_column 1 TIMESTAMP 2 USER_HOST
select * from renamed_slow_log;

# check that we can do whatever we want with disabled log
set global general_log='OFF';
RENAME TABLE general_log TO general_log2;

set global slow_query_log='OFF';
RENAME TABLE slow_log TO slow_log2;

# this should fail
--error ER_CANT_ACTIVATE_LOG
set global general_log='ON';
--error ER_CANT_ACTIVATE_LOG
set global slow_query_log='ON';

RENAME TABLE general_log2 TO general_log;
RENAME TABLE slow_log2 TO slow_log;

# this should work
set global general_log='ON';
set global slow_query_log='ON';
# now check flush logs
flush logs;
flush logs;
drop table renamed_general_log, renamed_slow_log;
use test;

#
# Bug #21966 Strange warnings on repair of the log tables
#

use mysql;
# check that no warning occurs on repair of the log tables
repair table general_log;
repair table slow_log;
# check that no warning occurs on "create like" for the log tables
create table general_log_new like general_log;
create table slow_log_new like slow_log;
show tables like "%log%";
drop table slow_log_new, general_log_new;
use test;

# kill all connections
disconnect con1;
disconnect con2;
+3 −2
Original line number Diff line number Diff line
@@ -1427,8 +1427,9 @@ bool handler::check_if_log_table_locking_is_allowed(uint sql_command,
{
  /*
    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.
    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 &&
+5 −1
Original line number Diff line number Diff line
@@ -957,6 +957,10 @@ class handler :public Sql_alloc
        thd     Handler of the thread, trying to lock the table
        table   Table handler to check
        count   Number of locks already granted to the table
        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,
@@ -972,7 +976,7 @@ class handler :public Sql_alloc
  virtual bool check_if_locking_is_allowed(uint sql_command,
                                           ulong type, TABLE *table,
                                           uint count,
                                           bool called_by_logger_thread)
                                           bool called_by_privileged_thread)
  {
    return TRUE;
  }
+2 −1
Original line number Diff line number Diff line
@@ -691,7 +691,8 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
          check_if_locking_is_allowed(thd->lex->sql_command, thd->lex->type,
                                      table_ptr[i], count,
                                      (thd == logger.get_general_log_thd()) ||
                                           (thd == logger.get_slow_log_thd())))
                                      (thd == logger.get_slow_log_thd()) ||
                                      (thd == logger.get_privileged_thread())))
      DBUG_RETURN(0);
  }

Loading