From: Luis Soares Date: July 18 2009 4:52pm Subject: bzr commit into mysql-5.1-bugteam branch (luis.soares:3024) Bug#42851 List-Archive: http://lists.mysql.com/commits/78967 X-Bug: 42851 Message-Id: <0KMZ00GATLK2VK00@fe-emea-09.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="Boundary_(ID_HNAAgZYomMk/NjRP4UpfvQ)" --Boundary_(ID_HNAAgZYomMk/NjRP4UpfvQ) MIME-version: 1.0 Content-type: text/plain; CHARSET=US-ASCII Content-transfer-encoding: 7BIT Content-disposition: inline #At file:///home/lsoares/Workspace/mysql-server/bugfix/b42851/mysql-5.1-bugteam/ based on revid:joro@stripped 3024 Luis Soares 2009-07-18 BUG#42851: Spurious "Statement is not safe to log in statement format." warnings Several warnings are printed when using statement based logging and unsafe operations are logged to the binlog. For example, this is the case for statements using LIMIT + ORDER BY PK. As a consequence, this would rapidly increase mysqld error log size, in some cases to several gigabytes, causing a maintenance nightmare. This patch proposes a mechanism to selectively and voluntarily suppress warnings from mysqld error log. It adds a dynamic server variable "suppress_log_warnings", which can be used to set/reset warning filtering from mysqld error log. Its details are the following: - Default: empty => no suppressions from error log. - Accepted values: list of tuples separated by commas, eg: :,: where LEVEL is one of: 'warning' or 'note, and code is the numerical representation of the warning code to suppress. In the case of unsafe statement warnings the code is 1592. As such, to suppress such warnings one could use: --suppress_log_warnings=warning:1592 or mysql>SET @@global.suppress_log_warnings=warning:1592 - Multiple suppressions can be done by adding other tuples : and separating each with a comma. Every time the suppress_log_warnings variable is set, the internal structure (instance of class Suppress_Log_Warnings) is updated and new rules are computed based on the suppress_log_warnings variable content. Finally, in this patch we only address the suppressing of unsafe warnings from error log, however, for this mechanism to work with other warnings, one needs to refactor part of the source code so that calls to sql_print_warnings get replaced with sql_print_or_suppress_warnings referencing the warning code whenever possible. @ mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result Result file update. Notice the extra warning for unsafe statements that are not suppressed. These are intended and guarded by DBUG_EXECUTE_IF. @ mysql-test/suite/binlog/t/binlog_stm_unsafe_warning-master.opt Added option to suppress unsafe warning. @ mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test Added test case for explicit suppressions through new server variable: suppress_log_warning. @ sql/log.cc Added function that conditionally prints to the error log, based on the rules established by the contents of suppress_log_warnings filtering rules. @ sql/mysql_priv.h Added references to sql_print_or_suppress_warning and opt_suppress_log_warnings. @ sql/mysqld.cc Added handling of command line arguments for option: suppress_log_warnings. If options are given, then it initializes the Suppress_Log_Warnings instance with the given argument. @ sql/set_var.cc Added new servr system variable suppress_log_warnings. Everytime, the variable is reset, the suppress_log_warnings filter gets updated. @ sql/set_var.h Exported system variable suppress_log_warnings. @ sql/sql_class.cc Replaced the sql_print_warning, for unsafe statements, with sql_print_or_suppress_warning. @ sql/sql_error.cc Created a class named Suppress_Log_Warnings. It holds warning filters for mysqld's error log. At its core is a Hash that maps error code into information on which levels the error code is to be suppressed. The class interface provides methods to set list of suppressions and check if a code is to be suppressed for a specific level. Apart from these, it also contains private utility methods. @ sql/sql_error.h Added interface for class Suppress_Log_Warnings. modified: mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result mysql-test/suite/binlog/t/binlog_stm_unsafe_warning-master.opt mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test sql/log.cc sql/mysql_priv.h sql/mysqld.cc sql/set_var.cc sql/set_var.h sql/sql_class.cc sql/sql_error.cc sql/sql_error.h === modified file 'mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result' --- a/mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result 2009-06-27 13:18:47 +0000 +++ b/mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result 2009-07-18 16:52:43 +0000 @@ -1,3 +1,90 @@ +SET @old_debug= @@global.debug; +SET @old_suppress_log_warnings= @@global.suppress_log_warnings; +echo ###### PART I: basic variable testing +### assertion: show that the variable exists and has CLI value +SHOW VARIABLES LIKE 'suppress_log_warnings'; +Variable_name Value +suppress_log_warnings warning:1592,note:1592 +### assertion: show that variable is dynamic: set to different value +SET @@global.suppress_log_warnings= 'warning:1953'; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +Variable_name Value +suppress_log_warnings warning:1953 +### assertion: show that variable is dynamic: set to default value +SET @@global.suppress_log_warnings= DEFAULT; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +Variable_name Value +suppress_log_warnings +### assertion: this variable should be able to cope with long values set on session +SET @@global.suppress_log_warnings='warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312'; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +Variable_name Value +suppress_log_warnings warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312 +### assertion: show that variable is dynamic: set to original value +SET @@global.suppress_log_warnings= @old_suppress_log_warnings; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +Variable_name Value +suppress_log_warnings warning:1592,note:1592 +###### PART II: check that warnings are/aren't printed when option to suppress is used +SET @@global.debug= "+d,b42851_extra_warning_when_not_supressing"; +### assertion: no warnings because CLI option to suppress warnings was set +CREATE TABLE t1 (a int, b int, pattern varchar(256), primary key (a)); +INSERT INTO t1 VALUES (1,2,null), (2,3,null); +UPDATE t1 SET b='4', pattern='b42851-suppressed_log_warning-1592' WHERE a=1 LIMIT 1; +Warnings: +Note 1592 Statement may not be safe to log in statement format. +UPDATE t1 SET b='5', pattern='b42851-suppressed_log_warning-1592' WHERE a=2 ORDER BY a LIMIT 1; +Warnings: +Note 1592 Statement may not be safe to log in statement format. +DROP TABLE t1; +### assertion: we get warnings again by reverting CLI option dynamically +SET @@global.suppress_log_warnings= ""; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +Variable_name Value +suppress_log_warnings +CREATE TABLE t1 (a int, b int, pattern varchar(256), primary key (a)); +INSERT INTO t1 VALUES (1,2,null), (2,3,null); +UPDATE t1 SET b='4', pattern='b42851-not-suppressed_log_warning-1592' WHERE a=1 LIMIT 1; +Warnings: +Note 1592 Statement may not be safe to log in statement format. +Note 1592 BUG#42851: FOR TESTING PURPOSES ONLY THIS WARNING IS PUSHED WHEN NOT SUPPRESSING THE WARNING FROM ERROR LOG! +UPDATE t1 SET b='5', pattern='b42851-not-suppressed_log_warning-1592' WHERE a=2 ORDER BY a LIMIT 1; +Warnings: +Note 1592 Statement may not be safe to log in statement format. +Note 1592 BUG#42851: FOR TESTING PURPOSES ONLY THIS WARNING IS PUSHED WHEN NOT SUPPRESSING THE WARNING FROM ERROR LOG! +DROP TABLE t1; +### assertion: warnings are turned off again by setting option dynamically +SET @@global.suppress_log_warnings= 'warning:1592'; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +Variable_name Value +suppress_log_warnings warning:1592 +CREATE TABLE t1 (a int, b int, pattern varchar(256), primary key (a)); +INSERT INTO t1 VALUES (1,2,null), (2,3,null); +UPDATE t1 SET b='4', pattern='b42851-suppressed_log_warning-1592' WHERE a=1 LIMIT 1; +Warnings: +Note 1592 Statement may not be safe to log in statement format. +UPDATE t1 SET b='5', pattern='b42851-suppressed_log_warning-1592' WHERE a=2 ORDER BY a LIMIT 1; +Warnings: +Note 1592 Statement may not be safe to log in statement format. +DROP TABLE t1; +### assertion: we get warnings again by setting the variable to default (no suppressions) +SET @@global.suppress_log_warnings= DEFAULT; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +Variable_name Value +suppress_log_warnings +CREATE TABLE t1 (a int, b int, pattern varchar(256), primary key (a)); +INSERT INTO t1 VALUES (1,2,null), (2,3,null); +UPDATE t1 SET b='4', pattern='b42851-not-suppressed_log_warning-1592' WHERE a=1 LIMIT 1; +Warnings: +Note 1592 Statement may not be safe to log in statement format. +Note 1592 BUG#42851: FOR TESTING PURPOSES ONLY THIS WARNING IS PUSHED WHEN NOT SUPPRESSING THE WARNING FROM ERROR LOG! +UPDATE t1 SET b='5', pattern='b42851-not-suppressed_log_warning-1592' WHERE a=2 ORDER BY a LIMIT 1; +Warnings: +Note 1592 Statement may not be safe to log in statement format. +Note 1592 BUG#42851: FOR TESTING PURPOSES ONLY THIS WARNING IS PUSHED WHEN NOT SUPPRESSING THE WARNING FROM ERROR LOG! +DROP TABLE t1; +SET @@global.suppress_log_warnings= @old_suppress_log_warnings; +SET @@global.debug= @old_debug; ### NOT filtered database => assertion: warnings ARE shown DROP TABLE IF EXISTS t1; CREATE TABLE t1 (a int, b int, primary key (a)); === modified file 'mysql-test/suite/binlog/t/binlog_stm_unsafe_warning-master.opt' --- a/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning-master.opt 2009-06-27 13:18:47 +0000 +++ b/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning-master.opt 2009-07-18 16:52:43 +0000 @@ -1 +1 @@ ---binlog-ignore-db=b42851 +--binlog-ignore-db=b42851 --suppress-log-warnings=warning:1592,note:1592 === modified file 'mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test' --- a/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test 2009-06-27 13:18:47 +0000 +++ b/mysql-test/suite/binlog/t/binlog_stm_unsafe_warning.test 2009-07-18 16:52:43 +0000 @@ -3,14 +3,45 @@ # # WHY # === -# +# # This test aims at checking that the fix that removes spurious -# entries in the error log when the statement is filtered out from -# binlog, is working. +# entries in the error log when: (i) option to suppresss warnings +# is used; or (ii) the statement is filtered out from binlog; is +# indeed working. +# # # HOW # === # +# - PART I: basic variable testing +# +# i) Check that dynamic variable is working properly +# +# +# - PART II: is split into four assertions. These rely on conditional +# code in the server, which will push an extra warning to +# the warning pool, when the warning is not suppressed +# from mysqld error log. This way MTR can find out what is +# and what is not suppressed from error log (check result +# file for details on the extra warning). Details on each +# sub-test are provided below: +# +# i) assert that warnings are suppressed when CLI option was used +# +# ii) assert that warnings are *not* suppressed when variable is +# changed dynamically to not suppress warnings (setting it to +# ""). +# +# iii) assert that warnings are suppressed when variable is changed +# dynamically to suppress warnings (set to 'warning:1592') +# +# iv) assert that warnings are *not* suppressed when variable is +# changed dynamically to the default value (set to +# 'warning:1592') +# +# - Part III: of the test is split into three assertions when issuing +# statements containing LIMIT and ORDER BY: +# # The test case is split into three assertions when issuing statements # containing LIMIT and ORDER BY: # @@ -25,6 +56,73 @@ -- source include/have_log_bin.inc -- source include/have_binlog_format_statement.inc +-- source include/have_debug.inc + +SET @old_debug= @@global.debug; +SET @old_suppress_log_warnings= @@global.suppress_log_warnings; + +-- echo echo ###### PART I: basic variable testing + +-- echo ### assertion: show that the variable exists and has CLI value +SHOW VARIABLES LIKE 'suppress_log_warnings'; + +-- echo ### assertion: show that variable is dynamic: set to different value +SET @@global.suppress_log_warnings= 'warning:1953'; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +-- echo ### assertion: show that variable is dynamic: set to default value +SET @@global.suppress_log_warnings= DEFAULT; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +-- echo ### assertion: this variable should be able to cope with long values set on session +SET @@global.suppress_log_warnings='warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312,warning:1592,warning:23423,warning:1235,note:20312'; +SHOW VARIABLES LIKE 'suppress_log_warnings'; +-- echo ### assertion: show that variable is dynamic: set to original value +SET @@global.suppress_log_warnings= @old_suppress_log_warnings; +SHOW VARIABLES LIKE 'suppress_log_warnings'; + +-- echo ###### PART II: check that warnings are/aren't printed when option to suppress is used + +SET @@global.debug= "+d,b42851_extra_warning_when_not_supressing"; + +-- echo ### assertion: no warnings because CLI option to suppress warnings was set +CREATE TABLE t1 (a int, b int, pattern varchar(256), primary key (a)); +INSERT INTO t1 VALUES (1,2,null), (2,3,null); +UPDATE t1 SET b='4', pattern='b42851-suppressed_log_warning-1592' WHERE a=1 LIMIT 1; +UPDATE t1 SET b='5', pattern='b42851-suppressed_log_warning-1592' WHERE a=2 ORDER BY a LIMIT 1; +DROP TABLE t1; + +-- echo ### assertion: we get warnings again by reverting CLI option dynamically +SET @@global.suppress_log_warnings= ""; +SHOW VARIABLES LIKE 'suppress_log_warnings'; + +CREATE TABLE t1 (a int, b int, pattern varchar(256), primary key (a)); +INSERT INTO t1 VALUES (1,2,null), (2,3,null); +UPDATE t1 SET b='4', pattern='b42851-not-suppressed_log_warning-1592' WHERE a=1 LIMIT 1; +UPDATE t1 SET b='5', pattern='b42851-not-suppressed_log_warning-1592' WHERE a=2 ORDER BY a LIMIT 1; +DROP TABLE t1; + +-- echo ### assertion: warnings are turned off again by setting option dynamically +SET @@global.suppress_log_warnings= 'warning:1592'; +SHOW VARIABLES LIKE 'suppress_log_warnings'; + +CREATE TABLE t1 (a int, b int, pattern varchar(256), primary key (a)); +INSERT INTO t1 VALUES (1,2,null), (2,3,null); +UPDATE t1 SET b='4', pattern='b42851-suppressed_log_warning-1592' WHERE a=1 LIMIT 1; +UPDATE t1 SET b='5', pattern='b42851-suppressed_log_warning-1592' WHERE a=2 ORDER BY a LIMIT 1; +DROP TABLE t1; + +-- echo ### assertion: we get warnings again by setting the variable to default (no suppressions) +SET @@global.suppress_log_warnings= DEFAULT; +SHOW VARIABLES LIKE 'suppress_log_warnings'; + +CREATE TABLE t1 (a int, b int, pattern varchar(256), primary key (a)); +INSERT INTO t1 VALUES (1,2,null), (2,3,null); +UPDATE t1 SET b='4', pattern='b42851-not-suppressed_log_warning-1592' WHERE a=1 LIMIT 1; +UPDATE t1 SET b='5', pattern='b42851-not-suppressed_log_warning-1592' WHERE a=2 ORDER BY a LIMIT 1; +DROP TABLE t1; + +# clean up +SET @@global.suppress_log_warnings= @old_suppress_log_warnings; +SET @@global.debug= @old_debug; -- echo ### NOT filtered database => assertion: warnings ARE shown @@ -71,3 +169,4 @@ DROP TABLE t1; # clean up DROP DATABASE b42851; + === modified file 'sql/log.cc' --- a/sql/log.cc 2009-07-10 23:12:13 +0000 +++ b/sql/log.cc 2009-07-18 16:52:43 +0000 @@ -4930,6 +4930,26 @@ void sql_print_error(const char *format, } +void sql_print_or_suppress_warning(uint code, const char *format, ...) +{ + if (!suppress_log_warnings->is_suppress_warning(code)) + { + DBUG_EXECUTE_IF("b42851_extra_warning_when_not_supressing", + push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_BINLOG_UNSAFE_STATEMENT, "BUG#42851: FOR TESTING" + " PURPOSES ONLY THIS WARNING IS PUSHED WHEN NOT" + " SUPPRESSING THE WARNING FROM ERROR LOG!");); + + va_list args; + DBUG_ENTER("sql_print_or_suppress_warning"); + + va_start(args, format); + error_log_print(WARNING_LEVEL, format, args); + va_end(args); + + DBUG_VOID_RETURN; + } +} void sql_print_warning(const char *format, ...) { va_list args; === modified file 'sql/mysql_priv.h' --- a/sql/mysql_priv.h 2009-06-05 11:23:58 +0000 +++ b/sql/mysql_priv.h 2009-07-18 16:52:43 +0000 @@ -845,6 +845,8 @@ Item *negate_expression(THD *thd, Item * int vprint_msg_to_log(enum loglevel level, const char *format, va_list args); void sql_print_error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +void sql_print_or_suppress_warning(uint code, const char *format, ...) + ATTRIBUTE_FORMAT(printf, 2, 3); void sql_print_information(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); typedef void (*sql_print_message_func)(const char *format, ...) @@ -2001,6 +2003,7 @@ extern uint opt_large_page_size; #ifdef MYSQL_SERVER extern char *opt_logname, *opt_slow_logname; extern const char *log_output_str; +extern char *opt_suppress_log_warnings; extern MYSQL_BIN_LOG mysql_bin_log; extern LOGGER logger; === modified file 'sql/mysqld.cc' --- a/sql/mysqld.cc 2009-06-23 09:10:04 +0000 +++ b/sql/mysqld.cc 2009-07-18 16:52:43 +0000 @@ -403,6 +403,7 @@ static pthread_cond_t COND_thread_cache, /* Global variables */ +char *opt_suppress_log_warnings; bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0; my_bool opt_log, opt_slow_log; ulong log_output_options; @@ -590,6 +591,7 @@ I_List threads; I_List key_caches; Rpl_filter* rpl_filter; Rpl_filter* binlog_filter; +Suppress_Log_Warnings *suppress_log_warnings; struct system_variables global_system_variables; struct system_variables max_system_variables; @@ -655,7 +657,6 @@ my_bool master_ssl; char *master_ssl_key, *master_ssl_cert; char *master_ssl_ca, *master_ssl_capath, *master_ssl_cipher; char *opt_logname, *opt_slow_logname; - /* Static variables */ static bool kill_in_progress, segfaulted; @@ -1301,6 +1302,7 @@ void clean_up(bool print_message) #endif delete binlog_filter; delete rpl_filter; + delete suppress_log_warnings; #ifndef EMBEDDED_LIBRARY end_ssl(); #endif @@ -3170,6 +3172,12 @@ static int init_common_variables(const c sql_perror("Could not allocate replication and binlog filters"); return 1; } + suppress_log_warnings= new Suppress_Log_Warnings; + if (!suppress_log_warnings) + { + sql_perror("Could not allocate suppress log warnings filter"); + return 1; + } if (init_thread_environment() || mysql_init_variables()) @@ -3495,6 +3503,11 @@ You should consider changing lower_case_ files_charset_info : &my_charset_bin); + if (opt_suppress_log_warnings) + { + sys_var_suppress_log_warnings.value= my_strdup(opt_suppress_log_warnings, MYF(0)); + sys_var_slow_log_path.value_length= strlen(opt_suppress_log_warnings); + } return 0; } @@ -5629,7 +5642,8 @@ enum options_mysqld OPT_SLAVE_EXEC_MODE, OPT_GENERAL_LOG_FILE, OPT_SLOW_QUERY_LOG_FILE, - OPT_IGNORE_BUILTIN_INNODB + OPT_IGNORE_BUILTIN_INNODB, + OPT_SUPPRESS_LOG_WARNINGS }; @@ -5699,6 +5713,16 @@ struct my_option my_long_options[] = {"binlog-ignore-db", OPT_BINLOG_IGNORE_DB, "Tells the master that updates to the given database should not be logged tothe binary log.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"suppress_log_warnings", OPT_SUPPRESS_LOG_WARNINGS, + "List containing warnings to be suppressed from error log. The list must" + " be structured as follows: :,: ." + " LEVEL is one of: 'warning' or 'note'. CODE must be the numerical" + " representation of the warning that is to be suppressed. Multiple" + " suppressions can be specified, in which case, a comma becomes the." + " separator. Example: --suppress-log-warnings=warning:1592,note:1593", + (uchar **) &opt_suppress_log_warnings, + (uchar **) &opt_suppress_log_warnings, 0, GET_STR_ALLOC, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"binlog-row-event-max-size", OPT_BINLOG_ROWS_EVENT_MAX_SIZE, "The maximum size of a row-based binary log event in bytes. Rows will be " "grouped into events smaller than this size if possible. " @@ -7922,6 +7946,25 @@ mysqld_get_one_option(int optid, binlog_filter->add_ignore_db(argument); break; } + case (int)OPT_SUPPRESS_LOG_WARNINGS: + { + if (argument != NULL) + { + if (suppress_log_warnings->set_suppressions(opt_suppress_log_warnings)) + { + sql_print_error("Could not add log warning suppression filter '%s'!\n", argument); + return 1; + } + } + else + { + sql_print_error("Could not add log warning suppression. Option was used\ + but no argument was set!\n"); + return 1; + } + + break; + } case OPT_BINLOG_FORMAT: { int id; === modified file 'sql/set_var.cc' --- a/sql/set_var.cc 2009-06-19 09:29:21 +0000 +++ b/sql/set_var.cc 2009-07-18 16:52:43 +0000 @@ -148,6 +148,10 @@ static void sys_default_general_log_path static bool sys_update_slow_log_path(THD *thd, set_var * var); static void sys_default_slow_log_path(THD *thd, enum_var_type type); +static void sys_default_var_suppress_log_warnings(THD *thd, enum_var_type type); +static int sys_check_var_suppress_log_warnings(THD *thd, set_var *var); +static bool sys_update_var_suppress_log_warnings(THD *thd, set_var *var); + /* Variable definition list @@ -892,7 +896,11 @@ sys_var_str sys_var_slow_log_path(&vars, opt_slow_logname); static sys_var_log_output sys_var_log_output_state(&vars, "log_output", &log_output_options, &log_output_typelib, 0); - +sys_var_str sys_var_suppress_log_warnings(&vars, "suppress_log_warnings", + sys_check_var_suppress_log_warnings, + sys_update_var_suppress_log_warnings, + sys_default_var_suppress_log_warnings, + (char*)opt_suppress_log_warnings); bool sys_var::check(THD *thd, set_var *var) { @@ -916,6 +924,82 @@ bool sys_var_str::check(THD *thd, set_va Functions to check and update variables */ +static bool update_sys_var_suppress_log_warnings(sys_var_str *var_str, set_var *var) +{ + char *old_val= var_str->value; + char *new_val= NULL; + uint new_len= 0; + bool res= FALSE; + char buff[256]; + String str(buff, sizeof(buff), system_charset_info), *newval; + if (var) + { + /** + Per BUG#38124, one needs to fetch the new value through a call to + val_str, instead of accessing directly str_value.ptr(). This way, + we can assign other values than constant strings (for example, + we can use user vars as the assigned value). + */ + newval= var->value->val_str(&str); + new_val= newval->c_ptr(); + new_len= strlen(new_val); + } + + if (!(res= suppress_log_warnings->set_suppressions(new_val))) + { + var_str->value= my_strndup(new_val, new_len, MYF(0)); + var_str->value_length= new_len; + + if (old_val) + my_free(old_val, MYF(MY_ALLOW_ZERO_PTR)); + } + else // reset to previous rules + suppress_log_warnings->set_suppressions(old_val); + + return res; +} + +static void sys_default_var_suppress_log_warnings(THD *thd, enum_var_type type) +{ + /** + Set default which is no suppressions. + */ + (void) update_sys_var_suppress_log_warnings(&sys_var_suppress_log_warnings, + 0); +} + +static int sys_check_var_suppress_log_warnings(THD *thd, set_var *var) +{ + char *str_val= (char*) ""; + char buff[256]; + String str(buff, sizeof(buff), system_charset_info), *newval; + if (var) + { + /** + Per BUG#38124, one needs to fetch the new value through a call to + val_str, instead of accessing directly str_value.ptr(). This way, + we can assign other values than constant strings (for example, + we can use user vars as the assigned value). + */ + newval= var->value->val_str(&str); + str_val= newval->c_ptr(); + } + + /** + Verify whether the variable contents is a valid list. + */ + return suppress_log_warnings->check_list((char*)str_val); +} + +static bool sys_update_var_suppress_log_warnings(THD *thd, set_var *var) +{ + /** + Sets the variable and updates the suppression filters. + */ + return update_sys_var_suppress_log_warnings(&sys_var_suppress_log_warnings, + var); +} + /* Update variables 'init_connect, init_slave'. @@ -1295,7 +1379,6 @@ void fix_binlog_format_after_update(THD thd->reset_current_stmt_binlog_row_based(); } - static void fix_max_binlog_size(THD *thd, enum_var_type type) { DBUG_ENTER("fix_max_binlog_size"); === modified file 'sql/set_var.h' --- a/sql/set_var.h 2009-06-19 09:29:21 +0000 +++ b/sql/set_var.h 2009-07-18 16:52:43 +0000 @@ -1440,7 +1440,8 @@ CHARSET_INFO *get_old_charset_by_name(co uchar* find_named(I_List *list, const char *name, uint length, NAMED_LIST **found); -extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path; +extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path, + sys_var_suppress_log_warnings; /* key_cache functions */ KEY_CACHE *get_key_cache(LEX_STRING *cache_name); === modified file 'sql/sql_class.cc' --- a/sql/sql_class.cc 2009-07-10 23:12:13 +0000 +++ b/sql/sql_class.cc 2009-07-18 16:52:43 +0000 @@ -3697,9 +3697,9 @@ int THD::binlog_query(THD::enum_binlog_q ER(ER_BINLOG_UNSAFE_STATEMENT)); if (!(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED)) { - sql_print_warning("%s Statement: %.*s", - ER(ER_BINLOG_UNSAFE_STATEMENT), - MYSQL_ERRMSG_SIZE, query_arg); + sql_print_or_suppress_warning(ER_BINLOG_UNSAFE_STATEMENT, "%s Statement: %.*s", + ER(ER_BINLOG_UNSAFE_STATEMENT), + MYSQL_ERRMSG_SIZE, query_arg); binlog_flags|= BINLOG_FLAG_UNSAFE_STMT_PRINTED; } } === modified file 'sql/sql_error.cc' --- a/sql/sql_error.cc 2009-02-13 16:41:47 +0000 +++ b/sql/sql_error.cc 2009-07-18 16:52:43 +0000 @@ -258,3 +258,231 @@ bool mysqld_show_warnings(THD *thd, ulon my_eof(thd); DBUG_RETURN(FALSE); } + +/** ******** **/ + +extern "C" uchar *get_key(const uchar *, size_t *, my_bool); +extern "C" void free_ent(void* a); + +Suppress_Log_Warnings::Suppress_Log_Warnings() +{ + hash_inited= FALSE; + pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST); +} + +Suppress_Log_Warnings::~Suppress_Log_Warnings() +{ + if (hash_inited) + hash_free(&h); + + pthread_mutex_destroy(&lock); +} + +bool Suppress_Log_Warnings::check_list(const char *list) +{ + + /** allow to use set_suppressions as a hash resetter */ + if (!list || strlen(list) == 0) + return FALSE; + + char *buf = (char*)my_malloc(strlen(list) + 1, MYF(0)); + bool res= FALSE; + + const char *next= list; + while (next) + { + char *code=NULL, *col=NULL; + uint key_len= 0; + char *comma= strchr(next, ','); + int len= (comma ? comma-next : strlen(next)) + 1; + int level_len= 0; + + strnmov(buf, next, len); + buf[len-1]= '\0'; + + col= strchr(buf, ':'); + if (!col) + { + res= TRUE; + goto end; + } + + code= col+1; + key_len= strlen(code); + level_len= col - buf; + if (key_len == 0 || // empty key! + (level_len != 4 && level_len != 7)) // neither 'note' nor 'warning' + { + res= TRUE; + goto end; + } + + if (strncasecmp(buf, "note", 4) && + strncasecmp(buf, "warning", 7)) + { + res= TRUE; + goto end; + } + + /* @todo: check whether code is a number or not */ + + next = (comma ? comma+1 : NULL); + } + +end: + my_free(buf, MYF(0)); + return res; +} + +bool Suppress_Log_Warnings::set_suppressions(const char *list) +{ + bool res= FALSE; + if (check_list(list)) + res= TRUE; + else + { + pthread_mutex_lock(&lock); + + if (hash_inited) + my_hash_reset(&h); + + if (list && strlen(list) > 0) + { + char *buf= (char*)my_malloc(strlen(list) + 1, MYF(0)); + const char *next= list; + while (next && !res) + { + char *comma= strchr(next, ','); + int len= (comma ? comma-next : strlen(next)) + 1; + strnmov(buf, next, len); + buf[len-1]= '\0'; + res= res || add_suppression(buf); + next = (comma ? comma+1 : NULL); + } + + my_free(buf, MYF(0)); + } + + pthread_mutex_unlock(&lock); + } + + return res; +} + +bool Suppress_Log_Warnings::add_suppression(const char *ent) +{ + bool res= FALSE; + WARNING_ENT *e= NULL; + uint key_len= 0, mask= 0, ent_size= 0; + char *col= NULL, *code= NULL; + + /** + not performing checks as list and entries have already been + checked in the the set_suppressions through check_list. + */ + col= strchr(ent, ':'); + code= col+1; + key_len= strlen(code); + + /* decide which mask to use */ + if (!strncasecmp(ent, "note", (col - ent))) + mask= NOTE_SUPPRESS_MASK; + else if (!strncasecmp(ent, "warning", (col - ent))) + mask= WARN_SUPPRESS_MASK; + else + { + DBUG_ASSERT(0); + } + + if (!hash_inited) + init_hash(); + else /* check if there is already an entry for the given code */ + e= (WARNING_ENT*) hash_search(&h, (const uchar *)code, key_len); + + if (!e) + { + /* If there was no entry, then allocate memory, copy code string + as key and insert in hash. + */ + ent_size= sizeof(WARNING_ENT) + key_len; // struct + key + + e= (WARNING_ENT*) my_malloc(ent_size, MYF(MY_WME)); + if (!e) + { + res= TRUE; + goto end; + } + memset(e, 0, ent_size); + + /* save key properties */ + e->code= (uchar *)e + (sizeof(WARNING_ENT)); + strnmov((char*)e->code, code, key_len); + e->key_len= key_len; + + /* finally, insert WARNING_ENT into hash */ + if (my_hash_insert(&h, (uchar *)e)) + { + res= TRUE; + goto end; + } + } + + /* set the active flag */ + e->supp_flags= (e->supp_flags | mask); + +end: + return res; +} + +void +Suppress_Log_Warnings::init_hash() +{ + hash_init(&h, system_charset_info,WARNING_ENT_HASH_SIZE,0,0, + get_key, free_ent, 0); + hash_inited = 1; +} + +bool +Suppress_Log_Warnings::is_suppression(uint code, uchar type_mask) +{ + bool res= FALSE; + if (hash_inited) + { + char s_code[NAME_LEN]; + int2str(code, s_code, 10, 0); + + pthread_mutex_lock(&lock); + + WARNING_ENT* e= (WARNING_ENT*) + hash_search(&h, (const uchar *)s_code, strlen(s_code)); + if (e) + res= (e->supp_flags & type_mask); + + pthread_mutex_unlock(&lock); + } + return res; +} + +bool Suppress_Log_Warnings::is_suppress_note(uint code) +{ + return is_suppression(code, NOTE_SUPPRESS_MASK); +} + +bool Suppress_Log_Warnings::is_suppress_warning(uint code) +{ + return is_suppression(code, WARN_SUPPRESS_MASK); +} + +uchar *get_key(const uchar* a, size_t *len, + my_bool __attribute__((unused))) +{ + WARNING_ENT *e= (WARNING_ENT *) a; + *len= e->key_len; + return (uchar*)e->code; +} + +void free_ent(void* a) +{ + WARNING_ENT *e= (WARNING_ENT *) a; + my_free((uchar*) e, MYF(0)); +} === modified file 'sql/sql_error.h' --- a/sql/sql_error.h 2007-06-07 08:53:23 +0000 +++ b/sql/sql_error.h 2009-07-18 16:52:43 +0000 @@ -41,3 +41,159 @@ void mysql_reset_errors(THD *thd, bool f bool mysqld_show_warnings(THD *thd, ulong levels_to_show); extern const LEX_STRING warning_level_names[]; + +/** + Suppress Warnings data structures + ================================= +*/ + +#define NOTE_SUPPRESS_MASK 0x1 +#define WARN_SUPPRESS_MASK 0x2 + +/** + initial size of internal array used for hash. +*/ +#define WARNING_ENT_HASH_SIZE 16 + +typedef struct st_warning_ent +{ + /** + This is the error code for which the suppress information is + stored. The code is used as a hash key when searching for + information on whether the warning should be suppressed or not. + */ + uchar *code; + + /** + These flags hold information on which LEVEL the output of warning + into log should be ignored. + + supp_flags | NOTE_SUPPRESS_MASK => notes for this code should be + ignored + + supp_flags | WARN_SUPPRESS_MASK => warnings for this code should + be ignored + + They are independent, so one can set both for the same code, + meaning that both notes and warnings should be + suppressed. Example: + + supp_flags = (supp_flags | NOTE_SUPPRESS_MASK | + WARN_SUPPRESS_MASK); + */ + uint supp_flags; + + /** + The length of the code string (which is used as a key). + */ + uint key_len; +} WARNING_ENT; + + +class Suppress_Log_Warnings +{ +private: + + /** + This hash holds data regarding warning codes and their suppress + information. + + The warning code is used as the key for storing and retrieving + data. + */ + HASH h; + + /** + This flag indicates whether the hash has been initialized or not. + */ + bool hash_inited; + + /** + Mutex that synchronizes accesses to hash contents. + */ + pthread_mutex_t lock; + + /** + This is a private method and is used to search and evaluate + suppress information for the given warning code. It is called + from the public is_suppression methods. + + */ + bool is_suppression(uint code, uchar type_mask); + + /** + Adds a single suppression. It must be represented as: + + : , for instance: warning:1592 + + @return true if adding the suppression was not + successfully completed, false otherwise. + */ + bool add_suppression(const char *ent); + + /** + This method initializes the hash. It should be called just once. + */ + void init_hash(); + +public: + /** + Sets multiple suppressions based on one list. The list must have + the following pattern: + + :,: + + for instance: + + warning:1592,note:1592,warning:1593,note:1594 + + @param[in] list A list containing pairs of levels and codes to + suppress. Elements in the list are separated by + commas. Should this parameter be NULL or empty + string ("") then the internal state is reset to + void, meaning that existent suppressing entries + are discarded. + + @return true if adding the suppression was not successful, + false otherwise. + */ + bool set_suppressions(const char *list); + + /** + Checks whether the given note code is to be suppressed + from the error log or not. + + @param[in] code The code number to check if its corresponding note + is to be suppressed or not. + + @return true if it should be suppressed, false otherwise. + */ + bool is_suppress_note(uint code); + + /** + Checks whether the given warning code is to be suppressed + from the error log or not. + + @param[in] code The code number to check if its corresponding + warning is to be suppressed or not. + + @return true if it should be suppressed, false otherwise. + */ + bool is_suppress_warning(uint code); + + /** + Performs some basic checks on the list so that it finds whether + the list is according to the format expected or not. + + @return true if the list does not conform to specification, + false otherwise. + */ + bool check_list(const char *list); + + Suppress_Log_Warnings(); + ~Suppress_Log_Warnings(); + Suppress_Log_Warnings(Suppress_Log_Warnings const&); + Suppress_Log_Warnings& operator=(Suppress_Log_Warnings const&); +}; + +extern Suppress_Log_Warnings *suppress_log_warnings; --Boundary_(ID_HNAAgZYomMk/NjRP4UpfvQ) MIME-version: 1.0 Content-type: text/bzr-bundle; CHARSET=US-ASCII; name="bzr/luis.soares@stripped" Content-transfer-encoding: 7BIT Content-disposition: inline; filename="bzr/luis.soares@stripped" # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: luis.soares@stripped # target_branch: file:///home/lsoares/Workspace/mysql-\ # server/bugfix/b42851/mysql-5.1-bugteam/ # testament_sha1: 1f43c1fc8ab0d27dd901ff82718f5c70b5aec2f7 # timestamp: 2009-07-18 17:52:48 +0100 # base_revision_id: joro@stripped # # Begin bundle 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 4u5IpwoSFfN0poA= --Boundary_(ID_HNAAgZYomMk/NjRP4UpfvQ)--