Skip to content

Commit

Permalink
optionally disable binlogging while executing triggers
Browse files Browse the repository at this point in the history
Summary:
Online schema change (OSC) creates triggers only on master side. Since RBR logs row changes made by triggers, sql_thread will hit errors due to missing tables on slaves. Add a session variable SQL_LOG_BIN_TRIGGERS to optionally disable binlogging for trigger statements so that RBR and OSC are fine with each other.

disable_sql_log_bin_triggers flag is added in the TABLE_LIST struct to track tables that are opened during trigger execution. This flag is used to skip writing Table_map_log_events for such tables.

With sql_log_bin_triggers enabled, the trigger changes which may be necessary for slave are not propagated. To avoid this, slave_run_triggers_for_rbr option must be enabled on slave.

Test Plan: mtr tests

Reviewers: jtolmer

Reviewed By: jtolmer
  • Loading branch information
santoshbanda authored and Herman Lee committed Jan 24, 2017
1 parent fe89886 commit cdeb012
Show file tree
Hide file tree
Showing 14 changed files with 270 additions and 3 deletions.
6 changes: 6 additions & 0 deletions mysql-test/r/mysqld--help-notwin-profiling.result
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,11 @@ The following options may be given as the first argument:
--sporadic-binlog-dump-fail
Option used by mysql-test for debugging and testing of
replication.
--sql-log-bin-triggers
The row changes generated by execution of triggers are
not logged inbinlog if this option is FALSE. Default is
TRUE.
(Defaults to on; use --skip-sql-log-bin-triggers to disable.)
--sql-mode=name Syntax: sql-mode=mode[,mode[,mode...]]. See the manual
for the complete list of valid sql modes
--stored-program-cache=#
Expand Down Expand Up @@ -1482,6 +1487,7 @@ slow-query-log FALSE
socket-umask 0
sort-buffer-size 262144
sporadic-binlog-dump-fail FALSE
sql-log-bin-triggers TRUE
sql-mode NO_ENGINE_SUBSTITUTION
stored-program-cache 256
super-read-only FALSE
Expand Down
6 changes: 6 additions & 0 deletions mysql-test/r/mysqld--help-notwin.result
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,11 @@ The following options may be given as the first argument:
--sporadic-binlog-dump-fail
Option used by mysql-test for debugging and testing of
replication.
--sql-log-bin-triggers
The row changes generated by execution of triggers are
not logged inbinlog if this option is FALSE. Default is
TRUE.
(Defaults to on; use --skip-sql-log-bin-triggers to disable.)
--sql-mode=name Syntax: sql-mode=mode[,mode[,mode...]]. See the manual
for the complete list of valid sql modes
--stored-program-cache=#
Expand Down Expand Up @@ -1478,6 +1483,7 @@ slow-query-log FALSE
socket-umask 0
sort-buffer-size 262144
sporadic-binlog-dump-fail FALSE
sql-log-bin-triggers TRUE
sql-mode NO_ENGINE_SUBSTITUTION
stored-program-cache 256
super-read-only FALSE
Expand Down
45 changes: 45 additions & 0 deletions mysql-test/suite/rpl/r/rpl_sql_log_bin_triggers.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
include/master-slave.inc
Warnings:
Note #### Sending passwords in plain text without SSL/TLS is extremely insecure.
Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
[connection master]
set @@session.sql_log_bin_triggers = OFF;
create table t1 (c1 char(1) primary key, c2 char(1));
set @@session.sql_log_bin=0;
create table t2 (id char(2) primary key, cnt int, o char(1), n char(1));
create trigger t1_cnt_b before update on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=new.C1 where id = 'u0';
create trigger t1_cnt_db before delete on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=' ' where id = 'd0';
create trigger t1_cnt_ib before insert on t1 for each row
update t2 set cnt=cnt+1, n=new.C1, o=' ' where id = 'i0';
create trigger t1_cnt_a after update on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=new.C1 where id = 'u1';
create trigger t1_cnt_da after delete on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=' ' where id = 'd1';
create trigger t1_cnt_ia after insert on t1 for each row
update t2 set cnt=cnt+1, n=new.C1, o=' ' where id = 'i1';
insert into t2 values
('u0', 0, ' ', ' '),('u1', 0, ' ', ' '),
('d0', 0, ' ', ' '),('d1', 0, ' ', ' '),
('i0', 0, ' ', ' '),('i1', 0, ' ', ' ');
set @@session.sql_log_bin=1;
# INSERT triggers test
insert into t1 values ('a','b');
select * from t1;
c1 c2
a b
# UPDATE triggers test
update t1 set C1= 'd';
select * from t1;
c1 c2
d b
# DELETE triggers test
delete from t1 where C1='d';
select * from t1;
c1 c2
set @@session.sql_log_bin=0;
drop table t2;
set @@session.sql_log_bin=1;
drop table t1;
include/rpl_end.inc
61 changes: 61 additions & 0 deletions mysql-test/suite/rpl/t/rpl_sql_log_bin_triggers.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# This test verifies the functionality of sql_log_bin_triggers configuration
# option using the following steps:
# 1. Create master side triggers and table, don't propogate them to slave by turning off sql_log_bin
# 2. Execute SQL statements on master which run the triggers
# 3. Sync slave with master and verify slave is not broken

source include/master-slave.inc;
source include/have_binlog_format_row.inc;

connection master;
set @@session.sql_log_bin_triggers = OFF;

create table t1 (c1 char(1) primary key, c2 char(1));
set @@session.sql_log_bin=0;
create table t2 (id char(2) primary key, cnt int, o char(1), n char(1));
create trigger t1_cnt_b before update on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=new.C1 where id = 'u0';
create trigger t1_cnt_db before delete on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=' ' where id = 'd0';
create trigger t1_cnt_ib before insert on t1 for each row
update t2 set cnt=cnt+1, n=new.C1, o=' ' where id = 'i0';
create trigger t1_cnt_a after update on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=new.C1 where id = 'u1';
create trigger t1_cnt_da after delete on t1 for each row
update t2 set cnt=cnt+1, o=old.C1, n=' ' where id = 'd1';
create trigger t1_cnt_ia after insert on t1 for each row
update t2 set cnt=cnt+1, n=new.C1, o=' ' where id = 'i1';
insert into t2 values
('u0', 0, ' ', ' '),('u1', 0, ' ', ' '),
('d0', 0, ' ', ' '),('d1', 0, ' ', ' '),
('i0', 0, ' ', ' '),('i1', 0, ' ', ' ');

set @@session.sql_log_bin=1;
sync_slave_with_master;

connection master;
--echo # INSERT triggers test
insert into t1 values ('a','b');
sync_slave_with_master;
select * from t1;

connection master;
--echo # UPDATE triggers test
update t1 set C1= 'd';
sync_slave_with_master;
select * from t1;

connection master;
--echo # DELETE triggers test
delete from t1 where C1='d';
sync_slave_with_master;
select * from t1;

connection master;
set @@session.sql_log_bin=0;
drop table t2;
set @@session.sql_log_bin=1;

drop table t1;

source include/rpl_end.inc;
66 changes: 66 additions & 0 deletions mysql-test/suite/sys_vars/r/sql_log_bin_triggers_basic.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
SET @start_sql_log_bin_triggers = @@global.sql_log_bin_triggers;
SELECT @start_sql_log_bin_triggers;
@start_sql_log_bin_triggers
1
SET @@global.sql_log_bin_triggers = false;
SET @@global.sql_log_bin_triggers = DEFAULT;
SELECT @@global.sql_log_bin_triggers;
@@global.sql_log_bin_triggers
1
SET @@global.sql_log_bin_triggers = @start_sql_log_bin_triggers;
SELECT @@global.sql_log_bin_triggers = true;
@@global.sql_log_bin_triggers = true
1
SET @@global.sql_log_bin_triggers = false;
SELECT @@global.sql_log_bin_triggers;
@@global.sql_log_bin_triggers
0
SET @@global.sql_log_bin_triggers = true;
SELECT @@global.sql_log_bin_triggers;
@@global.sql_log_bin_triggers
1
SET @@global.sql_log_bin_triggers = 1;
SELECT @@global.sql_log_bin_triggers;
@@global.sql_log_bin_triggers
1
SET @@global.sql_log_bin_triggers = 0;
SELECT @@global.sql_log_bin_triggers;
@@global.sql_log_bin_triggers
0
SET @@global.sql_log_bin_triggers = -1;
ERROR 42000: Variable 'sql_log_bin_triggers' can't be set to the value of '-1'
SELECT @@global.sql_log_bin_triggers;
@@global.sql_log_bin_triggers
0
SET @@global.sql_log_bin_triggers = 100;
ERROR 42000: Variable 'sql_log_bin_triggers' can't be set to the value of '100'
SELECT @@global.sql_log_bin_triggers;
@@global.sql_log_bin_triggers
0
SET @@global.sql_log_bin_triggers = 1000.01;
ERROR 42000: Incorrect argument type to variable 'sql_log_bin_triggers'
SELECT @@global.sql_log_bin_triggers;
@@global.sql_log_bin_triggers
0
SET @@session.sql_log_bin_triggers = FALSE;
SELECT @@session.sql_log_bin_triggers;
@@session.sql_log_bin_triggers
0
SELECT @@global.sql_log_bin_triggers = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
WHERE VARIABLE_NAME='sql_log_bin_triggers';
@@global.sql_log_bin_triggers = VARIABLE_VALUE
1
Warnings:
Warning 1292 Truncated incorrect DOUBLE value: 'OFF'
SELECT @@sql_log_bin_triggers = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='sql_log_bin_triggers';
@@sql_log_bin_triggers = VARIABLE_VALUE
1
Warnings:
Warning 1292 Truncated incorrect DOUBLE value: 'OFF'
SET @@global.sql_log_bin_triggers = @start_sql_log_bin_triggers;
SELECT @@global.sql_log_bin_triggers;
@@global.sql_log_bin_triggers
1
46 changes: 46 additions & 0 deletions mysql-test/suite/sys_vars/t/sql_log_bin_triggers_basic.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
source include/load_sysvars.inc;

SET @start_sql_log_bin_triggers = @@global.sql_log_bin_triggers;
SELECT @start_sql_log_bin_triggers;

SET @@global.sql_log_bin_triggers = false;
SET @@global.sql_log_bin_triggers = DEFAULT;
SELECT @@global.sql_log_bin_triggers;

SET @@global.sql_log_bin_triggers = @start_sql_log_bin_triggers;
SELECT @@global.sql_log_bin_triggers = true;

SET @@global.sql_log_bin_triggers = false;
SELECT @@global.sql_log_bin_triggers;
SET @@global.sql_log_bin_triggers = true;
SELECT @@global.sql_log_bin_triggers;

SET @@global.sql_log_bin_triggers = 1;
SELECT @@global.sql_log_bin_triggers;
SET @@global.sql_log_bin_triggers = 0;
SELECT @@global.sql_log_bin_triggers;

--Error ER_WRONG_VALUE_FOR_VAR
SET @@global.sql_log_bin_triggers = -1;
SELECT @@global.sql_log_bin_triggers;
--Error ER_WRONG_VALUE_FOR_VAR
SET @@global.sql_log_bin_triggers = 100;
SELECT @@global.sql_log_bin_triggers;
--Error ER_WRONG_TYPE_FOR_VAR
SET @@global.sql_log_bin_triggers = 1000.01;
SELECT @@global.sql_log_bin_triggers;

SET @@session.sql_log_bin_triggers = FALSE;
SELECT @@session.sql_log_bin_triggers;

SELECT @@global.sql_log_bin_triggers = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
WHERE VARIABLE_NAME='sql_log_bin_triggers';

SELECT @@sql_log_bin_triggers = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='sql_log_bin_triggers';


SET @@global.sql_log_bin_triggers = @start_sql_log_bin_triggers;
SELECT @@global.sql_log_bin_triggers;
5 changes: 4 additions & 1 deletion sql/handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7310,6 +7310,8 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
- The binary log is open
- The database the table resides in shall be binlogged (binlog_*_db rules)
- table is not mysql.event
- This is not a table opened when executing triggers or sql_log_bin_triggers
is TRUE.
*/

static bool check_table_binlog_row_based(THD *thd, TABLE *table)
Expand All @@ -7328,7 +7330,8 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
return (thd->is_current_stmt_binlog_format_row() &&
table->s->cached_row_logging_check &&
(thd->variables.option_bits & OPTION_BIN_LOG) &&
mysql_bin_log.is_open());
mysql_bin_log.is_open() &&
!table->pos_in_table_list->disable_sql_log_bin_triggers);
}


Expand Down
4 changes: 3 additions & 1 deletion sql/log_event.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12386,7 +12386,9 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl,
(tbl->s->db.str[tbl->s->db.length] == 0));
DBUG_ASSERT(tbl->s->table_name.str[tbl->s->table_name.length] == 0);

if (tbl->triggers)
// Trigger changes are not logged if sql_log_bin_triggers is FALSE.
// In this case, slaves should execute triggers while applying row events.
if (tbl->triggers && thd->variables.sql_log_bin_triggers)
m_flags |= TM_BIT_HAS_TRIGGERS_F;

m_data_size= TABLE_MAP_HEADER_LEN;
Expand Down
3 changes: 2 additions & 1 deletion sql/sp_head.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1939,7 +1939,8 @@ bool sp_head::add_used_tables_to_table_list(THD *thd,
table->lock_type >= TL_WRITE_ALLOW_WRITE ?
MDL_SHARED_WRITE : MDL_SHARED_READ,
MDL_TRANSACTION);

table->disable_sql_log_bin_triggers =
!thd->variables.sql_log_bin_triggers;
/* Everyting else should be zeroed */

**query_tables_last_ptr= table;
Expand Down
14 changes: 14 additions & 0 deletions sql/sp_instr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,20 @@ bool sp_lex_instr::reset_lex_and_exec_core(THD *thd,

reinit_stmt_before_use(thd, m_lex);

/*
Set disable_sql_log_bin_triggers flag for query_tables if binlog is
truned OFF for trigger statements. This is necessary to avoid writing down
Table_map_log_events.
*/
if (!thd->variables.sql_log_bin_triggers)
{
for (TABLE_LIST *tables = m_lex->query_tables; tables;
tables=tables->next_global)
{
tables->disable_sql_log_bin_triggers = TRUE;
}
}

/* Open tables if needed. */

if (open_tables)
Expand Down
1 change: 1 addition & 0 deletions sql/sql_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ typedef struct system_variables
my_bool expand_fast_index_creation;

my_bool use_fbson_output_format;
my_bool sql_log_bin_triggers;
} SV;


Expand Down
8 changes: 8 additions & 0 deletions sql/sql_trigger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2173,6 +2173,14 @@ bool Table_triggers_list::process_triggers(THD *thd,

thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);

my_bool disable_binlog = !thd->variables.sql_log_bin_triggers;
if (disable_binlog)
{
// option_bits restored back using statement_state in
// restore_sub_statement_state().
thd->variables.option_bits &= ~OPTION_BIN_LOG;
}

/*
Reset current_select before call execute_trigger() and
restore it after return from one. This way error is set
Expand Down
6 changes: 6 additions & 0 deletions sql/sys_vars.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2967,6 +2967,12 @@ static Sys_var_enum Slave_run_triggers_for_rbr(
slave_run_triggers_for_rbr_names,
DEFAULT(SLAVE_RUN_TRIGGERS_FOR_RBR_NO));

static Sys_var_mybool sql_log_bin_triggers(
"sql_log_bin_triggers",
"The row changes generated by execution of triggers are not logged in"
"binlog if this option is FALSE. Default is TRUE.",
SESSION_VAR(sql_log_bin_triggers), CMD_LINE(OPT_ARG), DEFAULT(TRUE));

const char *slave_type_conversions_name[]=
{"ALL_LOSSY", "ALL_NON_LOSSY", "ALL_UNSIGNED", "ALL_SIGNED", 0};
static Sys_var_set Slave_type_conversions(
Expand Down
2 changes: 2 additions & 0 deletions sql/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -2085,6 +2085,8 @@ struct TABLE_LIST
enum enum_table_ref_type m_table_ref_type;
/** See comments for TABLE_SHARE::get_table_ref_version() */
ulonglong m_table_ref_version;
public:
my_bool disable_sql_log_bin_triggers;
};


Expand Down

0 comments on commit cdeb012

Please sign in to comment.