From cdfc516d46353477ac3ec79aa3474a4520e66e72 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Feb 2007 10:19:08 +0100 Subject: [PATCH] BUG#25091 (A DELETE statement to mysql database is not logged in ROW format): With this patch, statements that change metadata (in the mysql database) is logged as statements, while normal changes (e.g., using INSERT, DELETE, and/or UPDATE) is logged according to the format in effect. The log tables (i.e., general_log and slow_log) are not replicated at all. With this patch, the following statements are replicated as statements: GRANT, REVOKE (ALL), CREATE USER, DROP USER, and RENAME USER. mysql-test/extra/binlog_tests/binlog.test: Added test to check that normal INSERT, DELETE, and UPDATE to a table in the mysql database is replicated both under row-based and statement-based replication. mysql-test/r/binlog_row_binlog.result: Result change. mysql-test/r/binlog_stm_binlog.result: Result change. sql/handler.cc: Removed hardcoded check for mysql database. Added table-specific flag for non-replication (used by log tables). sql/log.cc: Adding flag that a table shall not be replicated and set it for log tables. sql/sp.cc: Turning row-based replication off for statements that change metadata. sql/sql_acl.cc: Turning row-based replication off for statements that change metadata. sql/table.h: Adding flag that a table shall not be replicated. --- mysql-test/extra/binlog_tests/binlog.test | 13 ++++++ mysql-test/r/binlog_row_binlog.result | 17 +++++++ mysql-test/r/binlog_stm_binlog.result | 11 +++++ sql/handler.cc | 2 +- sql/log.cc | 1 + sql/sp.cc | 21 +++++++++ sql/sql_acl.cc | 56 +++++++++++++++++++++++ sql/table.h | 5 ++ 8 files changed, 125 insertions(+), 1 deletion(-) diff --git a/mysql-test/extra/binlog_tests/binlog.test b/mysql-test/extra/binlog_tests/binlog.test index 48d9b859c26..834edcff474 100644 --- a/mysql-test/extra/binlog_tests/binlog.test +++ b/mysql-test/extra/binlog_tests/binlog.test @@ -67,6 +67,19 @@ create table if not exists t2 select * from t1; create temporary table tt1 (a int); create table if not exists t3 like tt1; +# BUG#25091 (A DELETE statement to mysql database is not logged with +# ROW mode format): Checking that some basic operations on tables in +# the mysql database is replicated even when the current database is +# 'mysql'. + +--disable_warnings +USE mysql; +INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test'); +UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@'; +DELETE FROM user WHERE host='localhost' AND user='@#@'; +--enable_warnings + +use test; --replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; diff --git a/mysql-test/r/binlog_row_binlog.result b/mysql-test/r/binlog_row_binlog.result index aee270bd7f6..769f23ea86c 100644 --- a/mysql-test/r/binlog_row_binlog.result +++ b/mysql-test/r/binlog_row_binlog.result @@ -249,6 +249,11 @@ create table t1 (a int); create table if not exists t2 select * from t1; create temporary table tt1 (a int); create table if not exists t3 like tt1; +USE mysql; +INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test'); +UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@'; +DELETE FROM user WHERE host='localhost' AND user='@#@'; +use test; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_increment primary key) @@ -262,6 +267,12 @@ master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t2` ( master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t3` ( `a` int(11) DEFAULT NULL ) +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Update_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Delete_rows 1 # table_id: # flags: STMT_END_F drop table t1,t2,t3,tt1; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam; set @@session.auto_increment_increment=1, @@session.auto_increment_offset=1; @@ -281,6 +292,12 @@ master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t2` ( master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t3` ( `a` int(11) DEFAULT NULL ) +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Update_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Delete_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `test`; DROP TABLE `t1`,`t2`,`t3` /* generated by server */ master-bin.000001 # Query 1 # use `test`; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam master-bin.000001 # Table_map 1 # table_id: # (test.t1) diff --git a/mysql-test/r/binlog_stm_binlog.result b/mysql-test/r/binlog_stm_binlog.result index a9b3f69ecee..fb0453b5b68 100644 --- a/mysql-test/r/binlog_stm_binlog.result +++ b/mysql-test/r/binlog_stm_binlog.result @@ -159,6 +159,11 @@ create table t1 (a int); create table if not exists t2 select * from t1; create temporary table tt1 (a int); create table if not exists t3 like tt1; +USE mysql; +INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test'); +UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@'; +DELETE FROM user WHERE host='localhost' AND user='@#@'; +use test; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_increment primary key) @@ -169,6 +174,9 @@ master-bin.000001 # Query 1 # use `test`; create table t1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t2 select * from t1 master-bin.000001 # Query 1 # use `test`; create temporary table tt1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t3 like tt1 +master-bin.000001 # Query 1 # use `mysql`; INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test') +master-bin.000001 # Query 1 # use `mysql`; UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@' +master-bin.000001 # Query 1 # use `mysql`; DELETE FROM user WHERE host='localhost' AND user='@#@' drop table t1,t2,t3,tt1; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam; set @@session.auto_increment_increment=1, @@session.auto_increment_offset=1; @@ -185,6 +193,9 @@ master-bin.000001 # Query 1 # use `test`; create table t1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t2 select * from t1 master-bin.000001 # Query 1 # use `test`; create temporary table tt1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t3 like tt1 +master-bin.000001 # Query 1 # use `mysql`; INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test') +master-bin.000001 # Query 1 # use `mysql`; UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@' +master-bin.000001 # Query 1 # use `mysql`; DELETE FROM user WHERE host='localhost' AND user='@#@' master-bin.000001 # Query 1 # use `test`; drop table t1,t2,t3,tt1 master-bin.000001 # Query 1 # use `test`; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam master-bin.000001 # Table_map 1 # table_id: # (test.t1) diff --git a/sql/handler.cc b/sql/handler.cc index 360d528f0ad..24cf5afb5c7 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3481,7 +3481,7 @@ namespace { { int const check(table->s->tmp_table == NO_TMP_TABLE && binlog_filter->db_ok(table->s->db.str) && - strcmp("mysql", table->s->db.str) != 0); + !table->no_replicate); table->s->cached_row_logging_check= check; } diff --git a/sql/log.cc b/sql/log.cc index 1b432ca15c0..99431304b96 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -312,6 +312,7 @@ bool Log_to_csv_event_handler::open_log_table(uint log_table_type) { table->table->use_all_columns(); table->table->locked_by_logger= TRUE; + table->table->no_replicate= TRUE; } /* restore thread settings */ if (curr) diff --git a/sql/sp.cc b/sql/sp.cc index 14703e3aa42..b2bb1654176 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -500,6 +500,13 @@ db_create_routine(THD *thd, int type, sp_head *sp) DBUG_PRINT("enter", ("type: %d name: %.*s",type,sp->m_name.length, sp->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) ret= SP_OPEN_TABLE_FAILED; else @@ -636,6 +643,13 @@ db_drop_routine(THD *thd, int type, sp_name *name) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) @@ -668,6 +682,13 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 0d9653172e0..bec632889c1 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3001,6 +3001,13 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -3218,6 +3225,13 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -3357,6 +3371,13 @@ bool mysql_grant(THD *thd, const char *db, List &list, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -5399,6 +5420,13 @@ bool mysql_create_user(THD *thd, List &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_create_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* CREATE USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5471,6 +5499,13 @@ bool mysql_drop_user(THD *thd, List &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_drop_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* DROP USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5535,6 +5570,13 @@ bool mysql_rename_user(THD *thd, List &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_rename_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* RENAME USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5610,6 +5652,13 @@ bool mysql_revoke_all(THD *thd, List &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_revoke_all"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5800,6 +5849,13 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, rw_wrlock(&LOCK_grant); VOID(pthread_mutex_lock(&acl_cache->lock)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* Remove procedure access */ do { diff --git a/sql/table.h b/sql/table.h index 80add0e0b91..7fd2b405073 100644 --- a/sql/table.h +++ b/sql/table.h @@ -389,6 +389,10 @@ struct st_table { /* If true, the current table row is considered to have all columns set to NULL, including columns declared as "not null" (see maybe_null). + + TODO: Each of these flags take up 8 bits. They can just as easily + be put into one single unsigned long and instead of taking up 18 + bytes, it would take up 4. */ my_bool null_row; my_bool force_index; @@ -396,6 +400,7 @@ struct st_table { my_bool key_read, no_keyread; my_bool locked_by_flush; my_bool locked_by_logger; + my_bool no_replicate; my_bool locked_by_name; my_bool fulltext_searched; my_bool no_cache;