From 01be66ad54c3d01a83161a17c90ca59f8fc50629 Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Wed, 27 Dec 2006 18:14:28 +0400 Subject: [PATCH 01/15] Bug#24747 XPath error with the node name "Text" --- mysql-test/r/xml.result | 75 +++++++++++++++++++++++++++++++++ mysql-test/t/xml.test | 38 +++++++++++++++++ sql/item_xmlfunc.cc | 92 ++++++++++++++++++++++++++++++++++------- 3 files changed, 189 insertions(+), 16 deletions(-) diff --git a/mysql-test/r/xml.result b/mysql-test/r/xml.result index bb7f84d0287..3991c97626a 100644 --- a/mysql-test/r/xml.result +++ b/mysql-test/r/xml.result @@ -809,3 +809,78 @@ ExtractValue(@xml, "/entry[(50test','/a/b/Text'); +ExtractValue('test','/a/b/Text') +test +select ExtractValue('test','/a/b/comment'); +ExtractValue('test','/a/b/comment') +test +select ExtractValue('test','/a/b/node'); +ExtractValue('test','/a/b/node') +test +select ExtractValue('test','/a/b/processing-instruction'); +ExtractValue('test','/a/b/processing-instruction') +test +select ExtractValue('test', '/a/and'); +ExtractValue('test', '/a/and') +test +select ExtractValue('test', '/a/or'); +ExtractValue('test', '/a/or') +test +select ExtractValue('test', '/a/mod'); +ExtractValue('test', '/a/mod') +test +select ExtractValue('
test
', '/a/div'); +ExtractValue('
test
', '/a/div') +test +select ExtractValue('test', '/a/and:and'); +ExtractValue('test', '/a/and:and') +test +select ExtractValue('test', '/a/or:or'); +ExtractValue('test', '/a/or:or') +test +select ExtractValue('test', '/a/mod:mod'); +ExtractValue('test', '/a/mod:mod') +test +select ExtractValue('test', '/a/div:div'); +ExtractValue('test', '/a/div:div') +test +select ExtractValue('test', '/a/ancestor'); +ExtractValue('test', '/a/ancestor') +test +select ExtractValue('test', '/a/ancestor-or-self'); +ExtractValue('test', '/a/ancestor-or-self') +test +select ExtractValue('test', '/a/attribute'); +ExtractValue('test', '/a/attribute') +test +select ExtractValue('test', '/a/child'); +ExtractValue('test', '/a/child') +test +select ExtractValue('test', '/a/descendant'); +ExtractValue('test', '/a/descendant') +test +select ExtractValue('test', '/a/descendant-or-self'); +ExtractValue('test', '/a/descendant-or-self') +test +select ExtractValue('test', '/a/following'); +ExtractValue('test', '/a/following') +test +select ExtractValue('test', '/a/following-sibling'); +ExtractValue('test', '/a/following-sibling') +test +select ExtractValue('test', '/a/namespace'); +ExtractValue('test', '/a/namespace') +test +select ExtractValue('test', '/a/parent'); +ExtractValue('test', '/a/parent') +test +select ExtractValue('test', '/a/preceding'); +ExtractValue('test', '/a/preceding') +test +select ExtractValue('test', '/a/preceding-sibling'); +ExtractValue('test', '/a/preceding-sibling') +test +select ExtractValue('test', '/a/self'); +ExtractValue('test', '/a/self') +test diff --git a/mysql-test/t/xml.test b/mysql-test/t/xml.test index ef94c7508c4..8517dce111f 100644 --- a/mysql-test/t/xml.test +++ b/mysql-test/t/xml.test @@ -406,3 +406,41 @@ select ExtractValue(@xml, "/entry[(50>pt)]/id"); select ExtractValue(@xml, "/entry[(50>=pt)]/id"); select ExtractValue(@xml, "/entry[(50test','/a/b/Text'); +select ExtractValue('test','/a/b/comment'); +select ExtractValue('test','/a/b/node'); +select ExtractValue('test','/a/b/processing-instruction'); +# +# Test keywords in node name contexts +# +select ExtractValue('test', '/a/and'); +select ExtractValue('test', '/a/or'); +select ExtractValue('test', '/a/mod'); +select ExtractValue('
test
', '/a/div'); +select ExtractValue('test', '/a/and:and'); +select ExtractValue('test', '/a/or:or'); +select ExtractValue('test', '/a/mod:mod'); +select ExtractValue('test', '/a/div:div'); +# +# Test axis names in node name context +# +select ExtractValue('test', '/a/ancestor'); +select ExtractValue('test', '/a/ancestor-or-self'); +select ExtractValue('test', '/a/attribute'); +select ExtractValue('test', '/a/child'); +select ExtractValue('test', '/a/descendant'); +select ExtractValue('test', '/a/descendant-or-self'); +select ExtractValue('test', '/a/following'); +select ExtractValue('test', '/a/following-sibling'); +select ExtractValue('test', '/a/namespace'); +select ExtractValue('test', '/a/parent'); +select ExtractValue('test', '/a/preceding'); +select ExtractValue('test', '/a/preceding-sibling'); +select ExtractValue('test', '/a/self'); diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 966bae43984..d0b80ba49c0 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -1047,12 +1047,12 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = {MY_XPATH_LEX_OR , "or" , 2, 0 }, {MY_XPATH_LEX_DIV , "div" , 3, 0 }, {MY_XPATH_LEX_MOD , "mod" , 3, 0 }, - - {MY_XPATH_LEX_NODETYPE, "comment" , 7, 0 }, - {MY_XPATH_LEX_NODETYPE, "text" , 4, 0 }, - {MY_XPATH_LEX_NODETYPE, "processing-instruction" , 22,0 }, - {MY_XPATH_LEX_NODETYPE, "node" , 4, 0 }, - + {0,NULL,0,0} +}; + + +static struct my_xpath_keyword_names_st my_axis_names[]= +{ {MY_XPATH_LEX_AXIS,"ancestor" , 8,MY_XPATH_AXIS_ANCESTOR }, {MY_XPATH_LEX_AXIS,"ancestor-or-self" ,16,MY_XPATH_AXIS_ANCESTOR_OR_SELF }, {MY_XPATH_LEX_AXIS,"attribute" , 9,MY_XPATH_AXIS_ATTRIBUTE }, @@ -1066,7 +1066,16 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = {MY_XPATH_LEX_AXIS,"preceding" , 9,MY_XPATH_AXIS_PRECEDING }, {MY_XPATH_LEX_AXIS,"preceding-sibling" ,17,MY_XPATH_AXIS_PRECEDING_SIBLING }, {MY_XPATH_LEX_AXIS,"self" , 4,MY_XPATH_AXIS_SELF }, + {0,NULL,0,0} +}; + +static struct my_xpath_keyword_names_st my_nodetype_names[]= +{ + {MY_XPATH_LEX_NODETYPE, "comment" , 7, 0 }, + {MY_XPATH_LEX_NODETYPE, "text" , 4, 0 }, + {MY_XPATH_LEX_NODETYPE, "processing-instruction" , 22,0 }, + {MY_XPATH_LEX_NODETYPE, "node" , 4, 0 }, {0,NULL,0,0} }; @@ -1081,11 +1090,14 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = - Token type, on lookup success. - MY_XPATH_LEX_IDENT, on lookup failure. */ -static int my_xpath_keyword(MY_XPATH *x, const char *beg, const char *end) +static int +my_xpath_keyword(MY_XPATH *x, + struct my_xpath_keyword_names_st *keyword_names, + const char *beg, const char *end) { struct my_xpath_keyword_names_st *k; size_t length= end-beg; - for (k= my_keyword_names; k->name; k++) + for (k= keyword_names; k->name; k++) { if (length == k->length && !strncasecmp(beg, k->name, length)) { @@ -1371,15 +1383,32 @@ my_xpath_lex_scan(MY_XPATH *xpath, beg+= length) /* no op */; lex->end= beg; - // check if a function call - if (*beg == '(' && (xpath->func= my_xpath_function(lex->beg, beg))) + if (beg < end) { - lex->term= MY_XPATH_LEX_FUNC; - return; + if (*beg == '(') + { + /* + check if a function call, e.g.: count(/a/b) + or a nodetype test, e.g.: /a/b/text() + */ + if ((xpath->func= my_xpath_function(lex->beg, beg))) + lex->term= MY_XPATH_LEX_FUNC; + else + lex->term= my_xpath_keyword(xpath, my_nodetype_names, + lex->beg, beg); + return; + } + // check if an axis specifier, e.g.: /a/b/child::* + else if (*beg == ':' && beg + 1 < end && beg[1] == ':') + { + lex->term= my_xpath_keyword(xpath, my_axis_names, + lex->beg, beg); + return; + } } - // check if a keyword - lex->term= my_xpath_keyword(xpath, lex->beg, beg); + lex->term= my_xpath_keyword(xpath, my_keyword_names, + lex->beg, beg); return; } @@ -2333,6 +2362,36 @@ static int my_xpath_parse_Number(MY_XPATH *xpath) } +/* + Scan NCName. + + SYNOPSYS + + The keywords AND, OR, MOD, DIV are valid identitiers + when they are in identifier context: + + SELECT + ExtractValue('
VALUE
', + '/and/or/mod/div') + -> VALUE + + RETURN + 1 - success + 0 - failure +*/ + +static int +my_xpath_parse_NCName(MY_XPATH *xpath) +{ + return + my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_AND) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_OR) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_MOD) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_DIV) ? 1 : 0; +} + + /* QName grammar can be found in a separate document http://www.w3.org/TR/REC-xml-names/#NT-QName @@ -2341,16 +2400,17 @@ static int my_xpath_parse_Number(MY_XPATH *xpath) [7] Prefix ::= NCName [8] LocalPart ::= NCName */ + static int my_xpath_parse_QName(MY_XPATH *xpath) { const char *beg; - if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)) + if (!my_xpath_parse_NCName(xpath)) return 0; beg= xpath->prevtok.beg; if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON)) return 1; /* Non qualified name */ - if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)) + if (!my_xpath_parse_NCName(xpath)) return 0; xpath->prevtok.beg= beg; return 1; From 4d815a489368e3df4d46749288bf95a86cc9d2e5 Mon Sep 17 00:00:00 2001 From: "aelkin/elkin@dsl-hkibras-fe36f900-97.dhcp.inet.fi" <> Date: Mon, 15 Jan 2007 16:01:00 +0200 Subject: [PATCH 02/15] Bug #16567 binlog_format option does not show when doing ./mysqd --help --verbose Implementing this feature connected to wl#3368 mixed binlog_format default. Supplied by my.cnf or explicitly in command line option gets be displayed. When not supplied `(No default value)' is displayed, even though --log-bin might be supplied. The option is different object from @@global.binlog_format variable. The default `mixed' for the latter is dependant on presence of `--log-bin' option, otherwise the value of the var is set to NULL (undefined): var := opt | MIXED when binlog-in-use var := NULL otherwise (no binlog, no format) Comments on NDB and mixed format updated, also dependency the option on --log-bin aka binlog-in-use is worded. Making t/rpl_switch_stm_row_mixed.test to interprete DEFAULT for binlog_format as MIXED. Comments on what the test covers are added. todo/fixme: turning @@global.binlog_format to be read-only when it's set to NULL (no binlog). todo/fixme: options dependacy (acyclic) graph, particularly to solve a task of setting defaults values for the leaf nodes only when parents' nodes are set. --- mysql-test/r/rpl_switch_stm_row_mixed.result | 36 ++++++++++++---- mysql-test/t/rpl_switch_stm_row_mixed.test | 40 ++++++++++++++---- sql/mysqld.cc | 44 ++++++++++++-------- 3 files changed, 87 insertions(+), 33 deletions(-) diff --git a/mysql-test/r/rpl_switch_stm_row_mixed.result b/mysql-test/r/rpl_switch_stm_row_mixed.result index 258adc83b04..48b228550a7 100644 --- a/mysql-test/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/r/rpl_switch_stm_row_mixed.result @@ -7,8 +7,31 @@ start slave; drop database if exists mysqltest1; create database mysqltest1; use mysqltest1; +set session binlog_format=mixed; +show session variables like "binlog_format%"; +Variable_name Value +binlog_format MIXED +set session binlog_format=statement; +show session variables like "binlog_format%"; +Variable_name Value +binlog_format STATEMENT set session binlog_format=row; -set global binlog_format=row; +show session variables like "binlog_format%"; +Variable_name Value +binlog_format ROW +set global binlog_format=DEFAULT; +show global variables like "binlog_format%"; +Variable_name Value +binlog_format MIXED +set global binlog_format=MIXED; +show global variables like "binlog_format%"; +Variable_name Value +binlog_format MIXED +set global binlog_format=STATEMENT; +show global variables like "binlog_format%"; +Variable_name Value +binlog_format STATEMENT +set global binlog_format=ROW; show global variables like "binlog_format%"; Variable_name Value binlog_format ROW @@ -67,12 +90,11 @@ execute stmt1 using @string; deallocate prepare stmt1; insert into t1 values("for_10_"); insert into t1 select "yesterday_11_"; -set binlog_format=default; +set binlog_format=statement; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format STATEMENT STATEMENT -set global binlog_format=default; -ERROR 42000: Variable 'binlog_format' doesn't have a default value +set global binlog_format=statement; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format STATEMENT STATEMENT @@ -87,11 +109,11 @@ execute stmt1 using @string; deallocate prepare stmt1; insert into t1 values("for_15_"); insert into t1 select "yesterday_16_"; -set binlog_format=mixed; +set global binlog_format=mixed; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format -STATEMENT MIXED -set global binlog_format=mixed; +MIXED STATEMENT +set binlog_format=default; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format MIXED MIXED diff --git a/mysql-test/t/rpl_switch_stm_row_mixed.test b/mysql-test/t/rpl_switch_stm_row_mixed.test index ccd505941c8..bffa5905f1f 100644 --- a/mysql-test/t/rpl_switch_stm_row_mixed.test +++ b/mysql-test/t/rpl_switch_stm_row_mixed.test @@ -1,3 +1,14 @@ +# +# rpl_switch_stm_row_mixed tests covers +# +# - switching explicitly between STATEMENT, ROW, and MIXED binlog format +# showing when it is possible and when not. +# - switching from MIXED to RBR implicitly listing all use cases, +# e.g a query invokes UUID(), thereafter to serve as the definition +# of MIXED binlog format +# - correctness of execution + + -- source include/not_ndb_default.inc -- source include/master-slave.inc @@ -8,9 +19,22 @@ create database mysqltest1; --enable_warnings use mysqltest1; -set session binlog_format=row; -set global binlog_format=row; +# play with switching +set session binlog_format=mixed; +show session variables like "binlog_format%"; +set session binlog_format=statement; +show session variables like "binlog_format%"; +set session binlog_format=row; +show session variables like "binlog_format%"; + +set global binlog_format=DEFAULT; +show global variables like "binlog_format%"; +set global binlog_format=MIXED; +show global variables like "binlog_format%"; +set global binlog_format=STATEMENT; +show global variables like "binlog_format%"; +set global binlog_format=ROW; show global variables like "binlog_format%"; show session variables like "binlog_format%"; select @@global.binlog_format, @@session.binlog_format; @@ -63,12 +87,10 @@ deallocate prepare stmt1; insert into t1 values("for_10_"); insert into t1 select "yesterday_11_"; -# test SET DEFAULT (=statement at this point of test) -set binlog_format=default; +# test statement (is not default after wl#3368) +set binlog_format=statement; select @@global.binlog_format, @@session.binlog_format; -# due to cluster it's hard to set back to default ---error ER_NO_DEFAULT -set global binlog_format=default; +set global binlog_format=statement; select @@global.binlog_format, @@session.binlog_format; prepare stmt1 from 'insert into t1 select ?'; @@ -87,10 +109,10 @@ insert into t1 select "yesterday_16_"; # and now the mixed mode -set binlog_format=mixed; -select @@global.binlog_format, @@session.binlog_format; set global binlog_format=mixed; select @@global.binlog_format, @@session.binlog_format; +set binlog_format=default; +select @@global.binlog_format, @@session.binlog_format; prepare stmt1 from 'insert into t1 select concat(UUID(),?)'; set @string="emergency_17_"; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 7bf6035020f..e4bee275842 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -444,9 +444,10 @@ my_bool sp_automatic_privileges= 1; ulong opt_binlog_rows_event_max_size; const char *binlog_format_names[]= {"STATEMENT", "ROW", "MIXED", NullS}; TYPELIB binlog_format_typelib= - { array_elements(binlog_format_names)-1,"", + { array_elements(binlog_format_names) - 1, "", binlog_format_names, NULL }; - +ulong opt_binlog_format_id= (ulong) BINLOG_FORMAT_UNSPEC; +const char *opt_binlog_format= binlog_format_names[opt_binlog_format_id]; #ifdef HAVE_INITGROUPS static bool calling_initgroups= FALSE; /* Used in SIGSEGV handler. */ #endif @@ -3148,17 +3149,24 @@ with --log-bin instead."); "--log-slave-updates work."); unireg_abort(1); } - - if (!opt_bin_log && (global_system_variables.binlog_format != BINLOG_FORMAT_UNSPEC)) - { - sql_print_error("You need to use --log-bin to make " - "--binlog-format work."); - unireg_abort(1); - } - if (global_system_variables.binlog_format == BINLOG_FORMAT_UNSPEC) - { + if (!opt_bin_log) + if (opt_binlog_format_id != BINLOG_FORMAT_UNSPEC) + { + sql_print_error("You need to use --log-bin to make " + "--binlog-format work."); + unireg_abort(1); + } + else + { + global_system_variables.binlog_format= BINLOG_FORMAT_UNSPEC; + } + else + if (opt_binlog_format_id == BINLOG_FORMAT_UNSPEC) global_system_variables.binlog_format= BINLOG_FORMAT_MIXED; - } + else + { + DBUG_ASSERT(global_system_variables.binlog_format != BINLOG_FORMAT_UNSPEC); + } /* Check that we have not let the format to unspecified at this point */ DBUG_ASSERT((uint)global_system_variables.binlog_format <= @@ -4905,6 +4913,7 @@ struct my_option my_long_options[] = (gptr*) &my_bind_addr_str, (gptr*) &my_bind_addr_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"binlog_format", OPT_BINLOG_FORMAT, + "Does not have any effect without '--log-bin'. " "Tell the master the form of binary logging to use: either 'row' for " "row-based binary logging, or 'statement' for statement-based binary " "logging, or 'mixed'. 'mixed' is statement-based binary logging except " @@ -4912,11 +4921,12 @@ struct my_option my_long_options[] = "involve user-defined functions (i.e. UDFs) or the UUID() function; for " "those, row-based binary logging is automatically used. " #ifdef HAVE_NDB_BINLOG - "If ndbcluster is enabled, the default is 'row'." + "If ndbcluster is enabled and binlog_format is `mixed', the format switches" + " to 'row' and back implicitly per each query accessing a NDB table." #endif - , 0, 0, 0, GET_STR, REQUIRED_ARG, - BINLOG_FORMAT_MIXED - , 0, 0, 0, 0, 0 }, + ,(gptr*) &opt_binlog_format, (gptr*) &opt_binlog_format, + 0, GET_STR, REQUIRED_ARG, BINLOG_FORMAT_MIXED, BINLOG_FORMAT_STMT, + BINLOG_FORMAT_MIXED, 0, 0, 0}, {"binlog-do-db", OPT_BINLOG_DO_DB, "Tells the master it should log updates for the specified database, and exclude all others not explicitly mentioned.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -7267,7 +7277,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), binlog_format_names[BINLOG_FORMAT_MIXED]); exit(1); } - global_system_variables.binlog_format= id-1; + global_system_variables.binlog_format= opt_binlog_format_id= id - 1; break; } case (int)OPT_BINLOG_DO_DB: From 6ace4889205c07d76a4dd0eb34281128a01a132f Mon Sep 17 00:00:00 2001 From: "cbell/Chuck@mysql_cab_desk." <> Date: Thu, 18 Jan 2007 13:20:38 -0500 Subject: [PATCH 03/15] BUG#21490 - No warning issued for deprecated replication parameters This patch deprecates the replication startup options in the configuration file and on the command line. The options deprecated include: MASTER_HOST, MASTER_USER, MASTER_PASSWORD, MASTER_PORT, MASTER_CONNECT_RETRY, MASTER_SSL, MASTER_SSL_CA, MASTER_SSL_CAPATH, MASTER_SSL_CERT, MASTER_SSL_KEY, and MASTER_SSL_CIPHER The code is designed to print the warning message once. --- sql/mysql_priv.h | 17 ++++++++++------- sql/mysqld.cc | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 43b3605ad1a..42cecd1b89d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -91,15 +91,18 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define PREV_BITS(type,A) ((type) (((type) 1 << (A)) -1)) #define all_bits_set(A,B) ((A) & (B) != (B)) -#define WARN_DEPRECATED(Thd,Ver,Old,New) \ - do { \ - DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) >= 0); \ - push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ - ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ - (Old), (Ver), (New)); \ +#define WARN_DEPRECATED(Thd,Ver,Old,New) \ + do { \ + DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0); \ + if (Thd != NULL) \ + push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ + ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ + (Old), (Ver), (New)); \ + else \ + sql_print_warning("The syntax %s is deprecated and will be removed " \ + "in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \ } while(0) - extern CHARSET_INFO *system_charset_info, *files_charset_info ; extern CHARSET_INFO *national_charset_info, *table_alias_charset; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 4901cbf3b17..c849f24f86a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -361,6 +361,7 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0; my_bool opt_innodb; +bool slave_warning_issued = false; /* Legacy global handlerton. These will be removed (please do not add more). @@ -7470,6 +7471,29 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case (int) OPT_STANDALONE: /* Dummy option for NT */ break; #endif + /* + The following change issues a deprecation warning if the slave + configuration is specified either in the my.cnf file or on + the command-line. See BUG#21490. + */ + case OPT_MASTER_HOST: + case OPT_MASTER_USER: + case OPT_MASTER_PASSWORD: + case OPT_MASTER_PORT: + case OPT_MASTER_CONNECT_RETRY: + case OPT_MASTER_SSL: + case OPT_MASTER_SSL_KEY: + case OPT_MASTER_SSL_CERT: + case OPT_MASTER_SSL_CAPATH: + case OPT_MASTER_SSL_CIPHER: + case OPT_MASTER_SSL_CA: + if (!slave_warning_issued) //only show the warning once + { + slave_warning_issued = true; + WARN_DEPRECATED(0, "5.2", "for replication startup options", + "'CHANGE MASTER'"); + } + break; case OPT_CONSOLE: if (opt_console) opt_error_log= 0; // Force logs to stdout From fc0adebe0eb34e94eacc7a677a1d55b349131953 Mon Sep 17 00:00:00 2001 From: "cbell/Chuck@mysql_cab_desk." <> Date: Thu, 18 Jan 2007 13:35:49 -0500 Subject: [PATCH 04/15] BUG#21490 - No warning issued for deprecated replication parameters This patch deprecates the replication startup options in the configuration file and on the command line. The options deprecated include: MASTER_HOST, MASTER_USER, MASTER_PASSWORD, MASTER_PORT, MASTER_CONNECT_RETRY, MASTER_SSL, MASTER_SSL_CA, MASTER_SSL_CAPATH, MASTER_SSL_CERT, MASTER_SSL_KEY, and MASTER_SSL_CIPHER The code is designed to print the warning message once. --- sql/mysql_priv.h | 12 ++++++++++++ sql/mysqld.cc | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index cad42320db4..06a02978994 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -91,6 +91,18 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define PREV_BITS(type,A) ((type) (((type) 1 << (A)) -1)) #define all_bits_set(A,B) ((A) & (B) != (B)) +#define WARN_DEPRECATED(Thd,Ver,Old,New) \ + do { \ + DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0); \ + if (Thd != NULL) \ + push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ + ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ + (Old), (Ver), (New)); \ + else \ + sql_print_warning("The syntax %s is deprecated and will be removed " \ + "in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \ + } while(0) + extern CHARSET_INFO *system_charset_info, *files_charset_info ; extern CHARSET_INFO *national_charset_info, *table_alias_charset; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 488751eb015..a7dc8bede84 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -356,6 +356,8 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0; my_bool opt_innodb; +bool slave_warning_issued = false; + #ifdef HAVE_NDBCLUSTER_DB const char *opt_ndbcluster_connectstring= 0; const char *opt_ndb_connectstring= 0; @@ -6888,6 +6890,29 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case (int) OPT_STANDALONE: /* Dummy option for NT */ break; #endif + /* + The following change issues a deprecation warning if the slave + configuration is specified either in the my.cnf file or on + the command-line. See BUG#21490. + */ + case OPT_MASTER_HOST: + case OPT_MASTER_USER: + case OPT_MASTER_PASSWORD: + case OPT_MASTER_PORT: + case OPT_MASTER_CONNECT_RETRY: + case OPT_MASTER_SSL: + case OPT_MASTER_SSL_KEY: + case OPT_MASTER_SSL_CERT: + case OPT_MASTER_SSL_CAPATH: + case OPT_MASTER_SSL_CIPHER: + case OPT_MASTER_SSL_CA: + if (!slave_warning_issued) //only show the warning once + { + slave_warning_issued = true; + WARN_DEPRECATED(0, "5.2", "for replication startup options", + "'CHANGE MASTER'"); + } + break; case OPT_CONSOLE: if (opt_console) opt_error_log= 0; // Force logs to stdout From f3156a18408edc5f794b64675497ef034e89bd0b Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Tue, 23 Jan 2007 14:32:10 +0400 Subject: [PATCH 05/15] Bug#25081 SHOW FULL TABLES on table with latin chars in name fails Problem: The Table_type column of "SHOW FULL TABLES" displayed "ERROR" instead of "BASE TABLE" for tables having non-ascii characters in their names. Reason: tablename to filename encoding was missing, so mysql_frm_type() tried to open a wrong file name. Fix: adding tablename to filename encoding --- mysql-test/r/show_check.result | 8 ++++++++ mysql-test/t/show_check.test | 12 ++++++++++++ sql/sql_show.cc | 8 ++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 73450c9a773..a8f524e07dd 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -730,4 +730,12 @@ show keys from `mysqlttest\1`.`a\b`; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment drop table `mysqlttest\1`.`a\b`; drop database `mysqlttest\1`; +set names utf8; +drop table if exists `été`; +create table `été` (field1 int); +show full tables; +Tables_in_test Table_type +été BASE TABLE +drop table `été`; +set names latin1; End of 5.1 tests diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index be803a74c7b..fb21b5ecaa0 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -563,4 +563,16 @@ show keys from `mysqlttest\1`.`a\b`; drop table `mysqlttest\1`.`a\b`; drop database `mysqlttest\1`; +# +# Bug#25081 SHOW FULL TABLES on table with latin chars in name fails +# +set names utf8; +--disable_warnings +drop table if exists `été`; +--enable_warnings +create table `été` (field1 int); +show full tables; +drop table `été`; +set names latin1; + --echo End of 5.1 tests diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 0ebccba43ca..29a8aa28da2 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2490,7 +2490,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ST_SCHEMA_TABLE *schema_table= tables->schema_table; SELECT_LEX sel; INDEX_FIELD_VALUES idx_field_vals; - char path[FN_REFLEN], *end, *base_name, *orig_base_name, *file_name; + char path[FN_REFLEN], *base_name, *orig_base_name, *file_name; uint len; bool with_i_schema; enum enum_schema_tables schema_table_idx; @@ -2507,7 +2507,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) lex->view_prepare_mode= TRUE; DBUG_ENTER("get_all_tables"); - LINT_INIT(end); LINT_INIT(len); lex->reset_n_backup_query_tables_list(&query_tables_list_backup); @@ -2598,7 +2597,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) else { len= build_table_filename(path, sizeof(path), base_name, "", "", 0); - end= path + len; len= FN_LEN - len; find_files_result res= find_files(thd, &files, base_name, path, idx_field_vals.table_value, 0); @@ -2648,7 +2646,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) } else { - my_snprintf(end, len, "/%s%s", file_name, reg_ext); + build_table_filename(path, sizeof(path), + base_name, file_name, reg_ext, 0); + switch (mysql_frm_type(thd, path, ¬_used)) { case FRMTYPE_ERROR: table->field[3]->store(STRING_WITH_LEN("ERROR"), From ffeaffc26fc27e3d068210966254d3189d4868c5 Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Wed, 24 Jan 2007 16:37:38 +0400 Subject: [PATCH 06/15] Bug#25815 Data truncated for column TEXT Problem: "Data truncated" warning was incorrectly generated when storing a Japanese character encoded in utf8 into a cp932 column. Reason: Incorrect wrong warning condition compared the original length of the character in bytes (which is 3 in utf8) to the converted length of the character in bytes (which is 2 in cp932). Fix: use "how many bytes were scanned from input" instead of "how many bytes were put to the column" in the condition. --- mysql-test/r/ctype_cp932.result | 12 ++++++++++++ mysql-test/t/ctype_cp932.test | 11 +++++++++++ sql/field.cc | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/ctype_cp932.result b/mysql-test/r/ctype_cp932.result index ed57b87c1ba..10451686e2c 100755 --- a/mysql-test/r/ctype_cp932.result +++ b/mysql-test/r/ctype_cp932.result @@ -11353,3 +11353,15 @@ a a a drop table t1; +set names utf8; +create table t1 (a text) default character set cp932; +insert into t1 values (_utf8 0xE38182); +show warnings; +Level Code Message +select * from t1; +a +あ +select hex(a) from t1; +hex(a) +82A0 +drop table t1; diff --git a/mysql-test/t/ctype_cp932.test b/mysql-test/t/ctype_cp932.test index 688d06c4dde..c6196b928b0 100644 --- a/mysql-test/t/ctype_cp932.test +++ b/mysql-test/t/ctype_cp932.test @@ -413,3 +413,14 @@ select * from t1; insert into t1 values ('abc'); select * from t1; drop table t1; + +# +# Bug#25815 Data truncated for column TEXT +# +set names utf8; +create table t1 (a text) default character set cp932; +insert into t1 values (_utf8 0xE38182); +show warnings; +select * from t1; +select hex(a) from t1; +drop table t1; diff --git a/sql/field.cc b/sql/field.cc index 257c7846468..61a687567fc 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7024,7 +7024,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) cannot_convert_error_pos, from + length)) return 2; - if (copy_length < length) + if (from_end_pos < from + length) { report_data_too_long(this); return 2; From 59ada157182b89818a9ce80f81ef833e0f1fdbfe Mon Sep 17 00:00:00 2001 From: "mats@romeo.(none)" <> Date: Fri, 26 Jan 2007 19:29:57 +0100 Subject: [PATCH 07/15] BUG#19033 (RBR: slave does not handle schema changes correctly): Since checking table compatibility before locking the table, there were potential that a table could be locked that did not have a definition that was compatible with the table on the slave. This patch adds a check just after the table was locked to ensure that the table is (still) compatible with the table on the slave. --- sql/log.cc | 17 ++------ sql/log_event.cc | 78 ++++++++++++++++++++++++++-------- sql/log_event.h | 31 ++++++++++---- sql/rpl_utility.h | 105 +++++++++++++++++++++++++--------------------- sql/slave.h | 9 ++++ sql/sql_insert.cc | 2 +- 6 files changed, 155 insertions(+), 87 deletions(-) diff --git a/sql/log.cc b/sql/log.cc index 801b2849121..9f408c4c939 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -32,15 +32,6 @@ #include -/* - Define placement versions of operator new and operator delete since - we cannot be sure that the include exists. - */ -inline void *operator new(size_t, void *ptr) { return ptr; } -inline void *operator new[](size_t, void *ptr) { return ptr; } -inline void operator delete(void*, void*) { /* Do nothing */ } -inline void operator delete[](void*, void*) { /* Do nothing */ } - /* max size of the log message */ #define MAX_LOG_BUFFER_SIZE 1024 #define MAX_USER_HOST_SIZE 512 @@ -148,8 +139,8 @@ public: */ void truncate(my_off_t pos) { - DBUG_PRINT("info", ("truncating to position %lu", pos)); - DBUG_PRINT("info", ("before_stmt_pos=%lu", pos)); + DBUG_PRINT("info", ("truncating to position %ld", pos)); + DBUG_PRINT("info", ("before_stmt_pos=%lu", (void*) pos)); delete pending(); set_pending(0); reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0); @@ -3481,9 +3472,9 @@ int THD::binlog_flush_transaction_cache() { DBUG_ENTER("binlog_flush_transaction_cache"); binlog_trx_data *trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot]; - DBUG_PRINT("enter", ("trx_data=0x%lu", trx_data)); + DBUG_PRINT("enter", ("trx_data=0x%lu", (void*) trx_data)); if (trx_data) - DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%u", + DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%d", trx_data->before_stmt_pos)); /* diff --git a/sql/log_event.cc b/sql/log_event.cc index 80f79bc2698..507aa3d7688 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5753,15 +5753,45 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) DBUG_RETURN(error); } } + /* - When the open and locking succeeded, we add all the tables to - the table map and remove them from tables to lock. + When the open and locking succeeded, we check all tables to + ensure that they still have the correct type. + + We can use a down cast here since we know that every table added + to the tables_to_lock is a RPL_TABLE_LIST. + */ + + { + RPL_TABLE_LIST *ptr= static_cast(rli->tables_to_lock); + for ( ; ptr ; ptr= static_cast(ptr->next_global)) + { + if (ptr->m_tabledef.compatible_with(rli, ptr->table)) + { + mysql_unlock_tables(thd, thd->lock); + thd->lock= 0; + thd->query_error= 1; + rli->clear_tables_to_lock(); + DBUG_RETURN(ERR_BAD_TABLE_DEF); + } + } + } + + /* + ... and then we add all the tables to the table map and remove + them from tables to lock. We also invalidate the query cache for all the tables, since they will now be changed. + + TODO [/Matz]: Maybe the query cache should not be invalidated + here? It might be that a table is not changed, even though it + was locked for the statement. We do know that each + Rows_log_event contain at least one row, so after processing one + Rows_log_event, we can invalidate the query cache for the + associated table. */ - TABLE_LIST *ptr; - for (ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) + for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) { rli->m_table_map.set_table(ptr->table_id, ptr->table); } @@ -6214,11 +6244,11 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) thd->query_id= next_query_id(); pthread_mutex_unlock(&LOCK_thread_count); - TABLE_LIST *table_list; + RPL_TABLE_LIST *table_list; char *db_mem, *tname_mem; void *const memory= my_multi_malloc(MYF(MY_WME), - &table_list, sizeof(TABLE_LIST), + &table_list, sizeof(RPL_TABLE_LIST), &db_mem, NAME_LEN + 1, &tname_mem, NAME_LEN + 1, NULL); @@ -6264,11 +6294,27 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) } /* - Open the table if it is not already open and add the table to table map. - Note that for any table that should not be replicated, a filter is needed. + Open the table if it is not already open and add the table to + table map. Note that for any table that should not be + replicated, a filter is needed. + + The creation of a new TABLE_LIST is used to up-cast the + table_list consisting of RPL_TABLE_LIST items. This will work + since the only case where the argument to open_tables() is + changed, is when thd->lex->query_tables == table_list, i.e., + when the statement requires prelocking. Since this is not + executed when a statement is executed, this case will not occur. + As a precaution, an assertion is added to ensure that the bad + case is not a fact. + + Either way, the memory in the list is *never* released + internally in the open_tables() function, hence we take a copy + of the pointer to make sure that it's not lost. */ uint count; - if ((error= open_tables(thd, &table_list, &count, 0))) + DBUG_ASSERT(thd->lex->query_tables != table_list); + TABLE_LIST *tmp_table_list= table_list; + if ((error= open_tables(thd, &tmp_table_list, &count, 0))) { if (thd->query_error || thd->is_fatal_error) { @@ -6295,14 +6341,12 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) */ DBUG_ASSERT(m_table->in_use); - table_def const def(m_coltype, m_colcnt); - if (def.compatible_with(rli, m_table)) - { - thd->query_error= 1; - error= ERR_BAD_TABLE_DEF; - goto err; - /* purecov: end */ - } + /* + Use placement new to construct the table_def instance in the + memory allocated for it inside table_list. + */ + const table_def *const def= + new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt); /* We record in the slave's information that the table should be diff --git a/sql/log_event.h b/sql/log_event.h index 5a7959a01cc..5d7330e7259 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1721,14 +1721,17 @@ public: TYPE_CODE = TABLE_MAP_EVENT }; + /** + Enumeration of the errors that can be returned. + */ enum enum_error { - ERR_OPEN_FAILURE = -1, /* Failure to open table */ - ERR_OK = 0, /* No error */ - ERR_TABLE_LIMIT_EXCEEDED = 1, /* No more room for tables */ - ERR_OUT_OF_MEM = 2, /* Out of memory */ - ERR_BAD_TABLE_DEF = 3, /* Table definition does not match */ - ERR_RBR_TO_SBR = 4 /* daisy-chanining RBR to SBR not allowed */ + ERR_OPEN_FAILURE = -1, /**< Failure to open table */ + ERR_OK = 0, /**< No error */ + ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */ + ERR_OUT_OF_MEM = 2, /**< Out of memory */ + ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */ + ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */ }; enum enum_flag @@ -1808,7 +1811,7 @@ private: Row level log event class. - Common base class for all row-level log events. + Common base class for all row-containing log events. RESPONSIBILITIES @@ -1822,6 +1825,19 @@ private: class Rows_log_event : public Log_event { public: + /** + Enumeration of the errors that can be returned. + */ + enum enum_error + { + ERR_OPEN_FAILURE = -1, /**< Failure to open table */ + ERR_OK = 0, /**< No error */ + ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */ + ERR_OUT_OF_MEM = 2, /**< Out of memory */ + ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */ + ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */ + }; + /* These definitions allow you to combine the flags into an appropriate flag set using the normal bitwise operators. The @@ -1829,7 +1845,6 @@ public: accepted by the compiler, which is then used to set the real set of flags. */ - enum enum_flag { /* Last event of a statement */ diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index df0b0cd2ee1..f36661d42dd 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -24,97 +24,96 @@ #include "mysql_priv.h" uint32 -field_length_from_packed(enum_field_types const field_type, - byte const *const data); +field_length_from_packed(enum_field_types field_type, byte const *data); -/* +/** A table definition from the master. - RESPONSIBILITIES - + The responsibilities of this class is: - Extract and decode table definition data from the table map event - Check if table definition in table map is compatible with table definition on slave - DESCRIPTION + Currently, the only field type data available is an array of the + type operators that are present in the table map event. - Currently, the only field type data available is an array of the - type operators that are present in the table map event. - - TODO - - Add type operands to this structure to allow detection of - difference between, e.g., BIT(5) and BIT(10). + @todo Add type operands to this structure to allow detection of + difference between, e.g., BIT(5) and BIT(10). */ class table_def { public: - /* + /** Convenience declaration of the type of the field type data in a table map event. */ typedef unsigned char field_type; - /* + /** Constructor. - SYNOPSIS - table_def() - types Array of types - size Number of elements in array 'types' + @param types Array of types + @param size Number of elements in array 'types' */ table_def(field_type *types, my_size_t size) - : m_type(types), m_size(size) + : m_type(new unsigned char [size]), m_size(size) { + if (m_type) + memcpy(m_type, types, size); + else + m_size= 0; } - /* + ~table_def() { + if (m_type) + delete [] m_type; +#ifndef DBUG_OFF + m_type= 0; + m_size= 0; +#endif + } + + /** Return the number of fields there is type data for. - SYNOPSIS - size() - - RETURN VALUE - The number of fields that there is type data for. + @return The number of fields that there is type data for. */ my_size_t size() const { return m_size; } + /* Return a representation of the type data for one field. - SYNOPSIS - type() - i Field index to return data for + @param index Field index to return data for - RETURN VALUE - - Will return a representation of the type data for field - 'i'. Currently, only the type identifier is returned. + @return Will return a representation of the type data for field + index. Currently, only the type identifier is + returned. */ - field_type type(my_ptrdiff_t i) const { return m_type[i]; } + field_type type(my_ptrdiff_t index) const + { + DBUG_ASSERT(0 <= index); + DBUG_ASSERT(static_cast(index) < m_size); + return m_type[index]; + } - /* + /** Decide if the table definition is compatible with a table. - SYNOPSIS - compatible_with() - rli Pointer to relay log info - table Pointer to table to compare with. - - DESCRIPTION - - Compare the definition with a table to see if it is compatible - with it. A table definition is compatible with a table if: + Compare the definition with a table to see if it is compatible + with it. + A table definition is compatible with a table if: - the columns types of the table definition is a (not necessarily proper) prefix of the column type of the table, or - - the other way around - RETURN VALUE - 1 if the table definition is not compatible with 'table' - 0 if the table definition is compatible with 'table' + @param rli Pointer to relay log info + @param table Pointer to table to compare with. + + @retval 1 if the table definition is not compatible with @c table + @retval 0 if the table definition is compatible with @c table */ int compatible_with(RELAY_LOG_INFO *rli, TABLE *table) const; @@ -123,4 +122,14 @@ private: field_type *m_type; // Array of type descriptors }; +/** + Extend the normal table list with a few new fields needed by the + slave thread, but nowhere else. + */ +struct RPL_TABLE_LIST + : public st_table_list +{ + table_def m_tabledef; +}; + #endif /* RPL_UTILITY_H */ diff --git a/sql/slave.h b/sql/slave.h index 24ba09d78d3..0fa67578202 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -214,6 +214,15 @@ extern I_List threads; #define SLAVE_IO 1 #define SLAVE_SQL 2 +/* + Define placement versions of operator new and operator delete since + we cannot be sure that the include exists. + */ +inline void *operator new(size_t, void *ptr) { return ptr; } +inline void *operator new[](size_t, void *ptr) { return ptr; } +inline void operator delete(void*, void*) { /* Do nothing */ } +inline void operator delete[](void*, void*) { /* Do nothing */ } + #endif diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 8c4d2e537e5..99ea3236362 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3097,7 +3097,7 @@ void select_create::send_error(uint errcode,const char *err) thd->current_stmt_binlog_row_based ? "is" : "is NOT")); DBUG_PRINT("info", ("Current table (at 0x%lu) %s a temporary (or non-existant) table", - table, + (void*) table, table && !table->s->tmp_table ? "is NOT" : "is")); DBUG_PRINT("info", ("Table %s prior to executing this statement", From faf05bf314ce07697ca832a0a9b2ef092df74840 Mon Sep 17 00:00:00 2001 From: "bar@mysql.com" <> Date: Thu, 1 Feb 2007 10:11:45 +0400 Subject: [PATCH 08/15] Additional fix for bug 25815 for test results when running with --binlog-format=row --- mysql-test/r/binlog_row_ctype_cp932.result | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mysql-test/r/binlog_row_ctype_cp932.result b/mysql-test/r/binlog_row_ctype_cp932.result index ed57b87c1ba..10451686e2c 100644 --- a/mysql-test/r/binlog_row_ctype_cp932.result +++ b/mysql-test/r/binlog_row_ctype_cp932.result @@ -11353,3 +11353,15 @@ a a a drop table t1; +set names utf8; +create table t1 (a text) default character set cp932; +insert into t1 values (_utf8 0xE38182); +show warnings; +Level Code Message +select * from t1; +a +あ +select hex(a) from t1; +hex(a) +82A0 +drop table t1; From b3a03dada994b5e257ce880c0d4f478b1fd989bf Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 8 Feb 2007 15:53:14 +0100 Subject: [PATCH 09/15] Fix for BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values". When in an INSERT ON DUPLICATE KEY UPDATE, using an autoincrement column, we inserted some autogenerated values and also updated some rows, some autogenerated values were not used (for example, even if 10 was the largest autoinc value in the table at the start of the statement, 12 could be the first autogenerated value inserted by the statement, instead of 11). One autogenerated value was lost per updated row. Led to exhausting the range of the autoincrement column faster. Bug introduced by fix of BUG#20188; present since 5.0.24 and 5.1.12. This bug breaks replication from a pre-5.0.24 master. But the present bugfix, as it makes INSERT ON DUP KEY UPDATE behave like pre-5.0.24, breaks replication from a [5.0.24,5.0.34] master to a fixed (5.0.36) slave! To warn users against this when they upgrade their slave, as agreed with the support team, we add code for a fixed slave to detect that it is connected to a buggy master in a situation (INSERT ON DUP KEY UPDATE into autoinc column) likely to break replication, in which case it cannot replicate so stops and prints a message to the slave's error log and to SHOW SLAVE STATUS. For 5.0.36->[5.0.24,5.0.34] replication we cannot warn as master does not know the slave's version (but we always recommended to users to have slave at least as new as master). As agreed with support, I'll also ask for an alert to be put into the MySQL Network Monitoring and Advisory Service. --- mysql-test/r/rpl_insert_id.result | 58 ++++++++ mysql-test/r/rpl_known_bugs_detection.result | 133 ++++++++++++++++++ mysql-test/t/rpl_insert_id.test | 53 +++++++ .../t/rpl_known_bugs_detection-master.opt | 1 + mysql-test/t/rpl_known_bugs_detection.test | 90 ++++++++++++ sql/log_event.cc | 37 +++++ sql/log_event.h | 2 + sql/slave.cc | 64 +++++++++ sql/slave.h | 1 + sql/sql_insert.cc | 20 ++- 10 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 mysql-test/r/rpl_known_bugs_detection.result create mode 100644 mysql-test/t/rpl_known_bugs_detection-master.opt create mode 100644 mysql-test/t/rpl_known_bugs_detection.test diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index d133a2ae8ed..a5c8e17f67e 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -234,6 +234,64 @@ n b 2 100 3 350 drop table t1; +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +a b +1 10 +2 2 +SELECT * FROM t1; +a b +1 10 +2 2 +drop table t1; +CREATE TABLE t1 ( +id bigint(20) unsigned NOT NULL auto_increment, +field_1 int(10) unsigned NOT NULL, +field_2 varchar(255) NOT NULL, +field_3 varchar(255) NOT NULL, +PRIMARY KEY (id), +UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( +field_a int(10) unsigned NOT NULL, +field_b varchar(255) NOT NULL, +field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +id field_1 field_2 field_3 +1 1 a 1a +2 2 b 2b +3 3 c 3c +4 4 d 4d +5 5 e 5e +6 6 f 6f +SELECT * FROM t1; +id field_1 field_2 field_3 +1 1 a 1a +2 2 b 2b +3 3 c 3c +4 4 d 4d +5 5 e 5e +6 6 f 6f +drop table t1, t2; DROP PROCEDURE IF EXISTS p1; DROP TABLE IF EXISTS t1, t2; SELECT LAST_INSERT_ID(0); diff --git a/mysql-test/r/rpl_known_bugs_detection.result b/mysql-test/r/rpl_known_bugs_detection.result new file mode 100644 index 00000000000..c23586c6f61 --- /dev/null +++ b/mysql-test/r/rpl_known_bugs_detection.result @@ -0,0 +1,133 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +a b +1 10 +2 2 +show slave status;; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port # +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1105 +Last_Error Error 'master may suffer from http://bugs.mysql.com/bug.php?id=24432 so slave stops; check error log on slave for more info' on query. Default database: 'test'. Query: 'INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10' +Skip_Counter 0 +Exec_Master_Log_Pos 238 +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SELECT * FROM t1; +a b +stop slave; +reset slave; +reset master; +drop table t1; +start slave; +CREATE TABLE t1 ( +id bigint(20) unsigned NOT NULL auto_increment, +field_1 int(10) unsigned NOT NULL, +field_2 varchar(255) NOT NULL, +field_3 varchar(255) NOT NULL, +PRIMARY KEY (id), +UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( +field_a int(10) unsigned NOT NULL, +field_b varchar(255) NOT NULL, +field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +id field_1 field_2 field_3 +1 1 a 1a +2 2 b 2b +3 3 c 3c +4 4 d 4d +5 5 e 5e +6 6 f 6f +show slave status;; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port # +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1105 +Last_Error Error 'master may suffer from http://bugs.mysql.com/bug.php?id=24432 so slave stops; check error log on slave for more info' on query. Default database: 'test'. Query: 'INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c' +Skip_Counter 0 +Exec_Master_Log_Pos 1270 +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SELECT * FROM t1; +id field_1 field_2 field_3 +drop table t1, t2; +drop table t1, t2; diff --git a/mysql-test/t/rpl_insert_id.test b/mysql-test/t/rpl_insert_id.test index 331a913256c..be2948e9678 100644 --- a/mysql-test/t/rpl_insert_id.test +++ b/mysql-test/t/rpl_insert_id.test @@ -245,6 +245,59 @@ select * from t1 order by n; connection master; drop table t1; +# +# BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" +# + +# testcase with INSERT VALUES +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +sync_slave_with_master; +SELECT * FROM t1; +connection master; +drop table t1; + +# tescase with INSERT SELECT +CREATE TABLE t1 ( + id bigint(20) unsigned NOT NULL auto_increment, + field_1 int(10) unsigned NOT NULL, + field_2 varchar(255) NOT NULL, + field_3 varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( + field_a int(10) unsigned NOT NULL, + field_b varchar(255) NOT NULL, + field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +# Updating table t1 based on values from table t2 +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +# Inserting new record into t2 +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +# Updating t1 again +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +sync_slave_with_master; +SELECT * FROM t1; +connection master; +drop table t1, t2; + # # BUG#20339: stored procedure using LAST_INSERT_ID() does not # replicate statement-based diff --git a/mysql-test/t/rpl_known_bugs_detection-master.opt b/mysql-test/t/rpl_known_bugs_detection-master.opt new file mode 100644 index 00000000000..d4ba386a1a0 --- /dev/null +++ b/mysql-test/t/rpl_known_bugs_detection-master.opt @@ -0,0 +1 @@ +--loose-debug=d,pretend_version_50034_in_binlog diff --git a/mysql-test/t/rpl_known_bugs_detection.test b/mysql-test/t/rpl_known_bugs_detection.test new file mode 100644 index 00000000000..4719716d4a1 --- /dev/null +++ b/mysql-test/t/rpl_known_bugs_detection.test @@ -0,0 +1,90 @@ +# Test to see if slave can detect certain known bugs present +# on the master, and appropriately decides to stop +# (assuming the bug is fixed in the slave, slave cannot of course +# imitate the bug, so it has to stop). + +source include/have_debug.inc; +source include/master-slave.inc; + +# +# This is to test that slave properly detects if +# master may suffer from: +# BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" +# (i.e. on master, INSERT ON DUPLICATE KEY UPDATE is used and manipulates +# an auto_increment column, and is binlogged statement-based). +# + +# testcase with INSERT VALUES +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +sync_slave_with_master; +connection master; +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +connection slave; +wait_for_slave_to_stop; +# show the error message +--replace_column 1 # 4 # 7 # 8 # 9 # 23 # 33 # +--query_vertical show slave status; +# show that it was not replicated +SELECT * FROM t1; + +# restart replication for the next testcase +stop slave; +reset slave; +connection master; +reset master; +drop table t1; +connection slave; +start slave; + +# testcase with INSERT SELECT +connection master; +CREATE TABLE t1 ( + id bigint(20) unsigned NOT NULL auto_increment, + field_1 int(10) unsigned NOT NULL, + field_2 varchar(255) NOT NULL, + field_3 varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( + field_a int(10) unsigned NOT NULL, + field_b varchar(255) NOT NULL, + field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +sync_slave_with_master; +connection master; +# Updating table t1 based on values from table t2 +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +# Inserting new record into t2 +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +# Updating t1 again +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +connection slave; +wait_for_slave_to_stop; +# show the error message +--replace_column 1 # 4 # 7 # 8 # 9 # 23 # 33 # +--query_vertical show slave status; +# show that it was not replicated +SELECT * FROM t1; +connection master; +drop table t1, t2; +connection slave; +drop table t1, t2; + +# End of 5.0 tests diff --git a/sql/log_event.cc b/sql/log_event.cc index 657fd510e78..a14cd79461d 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2047,6 +2047,8 @@ Start_log_event_v3::Start_log_event_v3(const char* buf, binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET); memcpy(server_version, buf+ST_SERVER_VER_OFFSET, ST_SERVER_VER_LEN); + // prevent overrun if log is corrupted on disk + server_version[ST_SERVER_VER_LEN-1]= 0; created= uint4korr(buf+ST_CREATED_OFFSET); /* We use log_pos to mark if this was an artificial event or not */ artificial_event= (log_pos == 0); @@ -2170,6 +2172,8 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) switch (binlog_ver) { case 4: /* MySQL 5.0 */ memcpy(server_version, ::server_version, ST_SERVER_VER_LEN); + DBUG_EXECUTE_IF("pretend_version_50034_in_binlog", + strmov(server_version, "5.0.34");); common_header_len= LOG_EVENT_HEADER_LEN; number_of_event_types= LOG_EVENT_TYPES; /* we'll catch my_malloc() error in is_valid() */ @@ -2241,6 +2245,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) post_header_len= 0; /* will make is_valid() fail */ break; } + calc_server_version_split(); } @@ -2280,6 +2285,7 @@ Format_description_log_event(const char* buf, post_header_len= (uint8*) my_memdup((byte*)buf+ST_COMMON_HEADER_LEN_OFFSET+1, number_of_event_types* sizeof(*post_header_len), MYF(0)); + calc_server_version_split(); DBUG_VOID_RETURN; } @@ -2380,6 +2386,37 @@ int Format_description_log_event::exec_event(struct st_relay_log_info* rli) } #endif + +/** + Splits the event's 'server_version' string into three numeric pieces stored + into 'server_version_split': + X.Y.Zabc (X,Y,Z numbers, a not a digit) -> {X,Y,Z} + X.Yabc -> {X,Y,0} + Xabc -> {X,0,0} + 'server_version_split' is then used for lookups to find if the server which + created this event has some known bug. +*/ +void Format_description_log_event::calc_server_version_split() +{ + char *p= server_version, *r; + ulong number; + for (uint i= 0; i<=2; i++) + { + number= strtoul(p, &r, 10); + server_version_split[i]= (uchar)number; + DBUG_ASSERT(number < 256); // fit in uchar + p= r; + DBUG_ASSERT(!((i == 0) && (*r != '.'))); // should be true in practice + if (*r == '.') + p++; // skip the dot + } + DBUG_PRINT("info",("Format_description_log_event::server_version_split:" + " '%s' %d %d %d", server_version, + server_version_split[0], + server_version_split[1], server_version_split[2])); +} + + /************************************************************************** Load_log_event methods General note about Load_log_event: the binlogging of LOAD DATA INFILE is diff --git a/sql/log_event.h b/sql/log_event.h index fd2a5e5fb63..d3ebe6860f5 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1102,6 +1102,7 @@ public: uint8 number_of_event_types; /* The list of post-headers' lengthes */ uint8 *post_header_len; + uchar server_version_split[3]; Format_description_log_event(uint8 binlog_ver, const char* server_ver=0); @@ -1133,6 +1134,7 @@ public: */ return FORMAT_DESCRIPTION_HEADER_LEN; } + void calc_server_version_split(); }; diff --git a/sql/slave.cc b/sql/slave.cc index 8805f950d50..37c782a42c3 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -5165,6 +5165,70 @@ end: } +/** + Detects, based on master's version (as found in the relay log), if master + has a certain bug. + @param rli RELAY_LOG_INFO which tells the master's version + @param bug_id Number of the bug as found in bugs.mysql.com + @return TRUE if master has the bug, FALSE if it does not. +*/ +bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id) +{ + struct st_version_range_for_one_bug { + uint bug_id; + const uchar introduced_in[3]; // first version with bug + const uchar fixed_in[3]; // first version with fix + }; + static struct st_version_range_for_one_bug versions_for_all_bugs[]= + { + {24432, { 5, 0, 24 }, { 5, 0, 36 } }, + {24432, { 5, 1, 12 }, { 5, 1, 16 } } + }; + const uchar *master_ver= + rli->relay_log.description_event_for_exec->server_version_split; + + DBUG_ASSERT(sizeof(rli->relay_log.description_event_for_exec->server_version_split) == 3); + + for (uint i= 0; + i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++) + { + const uchar *introduced_in= versions_for_all_bugs[i].introduced_in, + *fixed_in= versions_for_all_bugs[i].fixed_in; + if ((versions_for_all_bugs[i].bug_id == bug_id) && + (memcmp(introduced_in, master_ver, 3) <= 0) && + (memcmp(fixed_in, master_ver, 3) > 0)) + { + // a verbose message for the error log + slave_print_error(rli, ER_UNKNOWN_ERROR, + "According to the master's version ('%s')," + " it is probable that master suffers from this bug:" + " http://bugs.mysql.com/bug.php?id=%u" + " and thus replicating the current binary log event" + " may make the slave's data become different from the" + " master's data." + " To take no risk, slave refuses to replicate" + " this event and stops." + " We recommend that all updates be stopped on the" + " master and slave, that the data of both be" + " manually synchronized," + " that master's binary logs be deleted," + " that master be upgraded to a version at least" + " equal to '%d.%d.%d'. Then replication can be" + " restarted.", + rli->relay_log.description_event_for_exec->server_version, + bug_id, + fixed_in[0], fixed_in[1], fixed_in[2]); + // a short message for SHOW SLAVE STATUS (message length constraints) + my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from" + " http://bugs.mysql.com/bug.php?id=%u" + " so slave stops; check error log on slave" + " for more info", MYF(0), bug_id); + return TRUE; + } + } + return FALSE; +} + #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION template class I_List_iterator; template class I_List_iterator; diff --git a/sql/slave.h b/sql/slave.h index bbf450bab75..e7d4456ccd9 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -533,6 +533,7 @@ void table_rule_ent_hash_to_str(String* s, HASH* h); void table_rule_ent_dynamic_array_to_str(String* s, DYNAMIC_ARRAY* a); bool show_master_info(THD* thd, MASTER_INFO* mi); bool show_binlog_info(THD* thd); +bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id); /* See if the query uses any tables that should not be replicated */ bool tables_ok(THD* thd, TABLE_LIST* tables); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index c60d3c307d0..c5f1524c556 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -58,6 +58,7 @@ #include "sp_head.h" #include "sql_trigger.h" #include "sql_select.h" +#include "slave.h" static int check_null_fields(THD *thd,TABLE *entry); #ifndef EMBEDDED_LIBRARY @@ -468,6 +469,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->cuted_fields = 0L; table->next_number_field=table->found_next_number_field; +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + goto abort; +#endif + error=0; id=0; thd->proc_info="update"; @@ -1133,11 +1142,11 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (res == VIEW_CHECK_ERROR) goto before_trg_err; + table->file->restore_auto_increment(); if ((error=table->file->update_row(table->record[1],table->record[0]))) { if ((error == HA_ERR_FOUND_DUPP_KEY) && info->ignore) { - table->file->restore_auto_increment(); goto ok_or_after_trg_err; } goto err; @@ -2369,6 +2378,15 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) } restore_record(table,s->default_values); // Get empty record table->next_number_field=table->found_next_number_field; + +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + DBUG_RETURN(1); +#endif + thd->cuted_fields=0; if (info.ignore || info.handle_duplicates != DUP_ERROR) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); From 8b1609a63717227b8931bb3a7fdaf45a43b831c7 Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 15 Feb 2007 15:39:03 +0100 Subject: [PATCH 10/15] Fix for BUG#25507 "multi-row insert delayed + auto increment causes duplicate key entries on slave" (two concurrrent connections doing multi-row INSERT DELAYED to insert into an auto_increment column, caused replication slave to stop with "duplicate key error" (and binlog was wrong)), and BUG#26116 "If multi-row INSERT DELAYED has errors, statement-based binlogging breaks" (the binlog was not accounting for all rows inserted, or slave could stop). The fix is that: if (statement-based) binlogging is on, a multi-row INSERT DELAYED is silently converted to a non-delayed INSERT. Note: it is not possible to test BUG#25507 in 5.0 (requires mysqlslap), so it is tested only in the changeset for 5.1. However, BUG#26116 is tested here, and the fix for BUG#25507 is the same code change. --- mysql-test/r/innodb-replace.result | 4 +- mysql-test/r/rpl_insert_delayed.result | 31 ++++++++++++ mysql-test/t/innodb-replace.test | 4 +- mysql-test/t/rpl_insert_delayed.test | 67 ++++++++++++++++++++++++++ sql/sql_insert.cc | 21 ++++++++ 5 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 mysql-test/r/rpl_insert_delayed.result create mode 100644 mysql-test/t/rpl_insert_delayed.test diff --git a/mysql-test/r/innodb-replace.result b/mysql-test/r/innodb-replace.result index b7edcc49e56..77e0aeb38fd 100644 --- a/mysql-test/r/innodb-replace.result +++ b/mysql-test/r/innodb-replace.result @@ -2,11 +2,11 @@ drop table if exists t1; create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) engine=innodb; select * from t1; c1 c2 stamp -replace delayed into t1 (c1, c2) values ( "text1","11"),( "text2","12"); +replace delayed into t1 (c1, c2) values ( "text1","11"); ERROR HY000: Table storage engine for 't1' doesn't have this option select * from t1; c1 c2 stamp -replace delayed into t1 (c1, c2) values ( "text1","12"),( "text2","13"),( "text3","14", "a" ),( "text4","15", "b" ); +replace delayed into t1 (c1, c2) values ( "text1","12"); ERROR HY000: Table storage engine for 't1' doesn't have this option select * from t1; c1 c2 stamp diff --git a/mysql-test/r/rpl_insert_delayed.result b/mysql-test/r/rpl_insert_delayed.result new file mode 100644 index 00000000000..38e2cddd650 --- /dev/null +++ b/mysql-test/r/rpl_insert_delayed.result @@ -0,0 +1,31 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +truncate table t1; +insert delayed into t1 values(10, "my name"); +insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +ERROR 23000: Duplicate entry '10' for key 1 +flush table t1; +select * from t1; +id name +10 my name +select * from t1; +id name +10 my name +delete from t1 where id!=10; +insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +ERROR 23000: Duplicate entry '10' for key 1 +flush table t1; +select * from t1; +id name +10 my name +20 is Bond +select * from t1; +id name +10 my name +20 is Bond +drop table t1; diff --git a/mysql-test/t/innodb-replace.test b/mysql-test/t/innodb-replace.test index 51b70f34b65..d44ede65ce8 100644 --- a/mysql-test/t/innodb-replace.test +++ b/mysql-test/t/innodb-replace.test @@ -12,10 +12,10 @@ drop table if exists t1; create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) engine=innodb; select * from t1; --error 1031 -replace delayed into t1 (c1, c2) values ( "text1","11"),( "text2","12"); +replace delayed into t1 (c1, c2) values ( "text1","11"); select * from t1; --error 1031 -replace delayed into t1 (c1, c2) values ( "text1","12"),( "text2","13"),( "text3","14", "a" ),( "text4","15", "b" ); +replace delayed into t1 (c1, c2) values ( "text1","12"); select * from t1; drop table t1; diff --git a/mysql-test/t/rpl_insert_delayed.test b/mysql-test/t/rpl_insert_delayed.test new file mode 100644 index 00000000000..3f72f3a3625 --- /dev/null +++ b/mysql-test/t/rpl_insert_delayed.test @@ -0,0 +1,67 @@ +--source include/master-slave.inc +--source include/not_embedded.inc +--source include/not_windows.inc + +connection master; + +let $binlog_format_statement=1; + +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); + +sync_slave_with_master; + +# +# BUG#26116 "If multi-row INSERT DELAYED has errors, +# statement-based binlogging breaks"; +# happened only in statement-based binlogging. +# + +connection master; +truncate table t1; +# first scenario: duplicate on first row +insert delayed into t1 values(10, "my name"); +if ($binlog_format_statement) +{ + # statement below will be converted to non-delayed INSERT and so + # will stop at first error, guaranteeing replication. + --error ER_DUP_ENTRY + insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +} +if (!$binlog_format_statement) +{ + insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +} +flush table t1; # to wait for INSERT DELAYED to be done +select * from t1; +sync_slave_with_master; +# when bug existed in statement-based binlogging, t1 on slave had +# different content from on master +select * from t1; + +# second scenario: duplicate on second row +connection master; +delete from t1 where id!=10; +if ($binlog_format_statement) +{ + # statement below will be converted to non-delayed INSERT and so + # will be binlogged with its ER_DUP_ENTRY error code, guaranteeing + # replication (slave will hit the same error code and so be fine). + --error ER_DUP_ENTRY + insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +} +if (!$binlog_format_statement) +{ + insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +} +flush table t1; # to wait for INSERT DELAYED to be done +select * from t1; +sync_slave_with_master; +# when bug existed in statement-based binlogging, query was binlogged +# with error_code=0 so slave stopped +select * from t1; + +# clean up +connection master; +drop table t1; +sync_slave_with_master; +connection master; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index c5f1524c556..f0708ee548a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -356,6 +356,27 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, (duplic == DUP_UPDATE)) lock_type=TL_WRITE; #endif + if ((lock_type == TL_WRITE_DELAYED) && + log_on && mysql_bin_log.is_open() && + (values_list.elements > 1)) + { + /* + Statement-based binary logging does not work in this case, because: + a) two concurrent statements may have their rows intermixed in the + queue, leading to autoincrement replication problems on slave (because + the values generated used for one statement don't depend only on the + value generated for the first row of this statement, so are not + replicable) + b) if first row of the statement has an error the full statement is + not binlogged, while next rows of the statement may be inserted. + c) if first row succeeds, statement is binlogged immediately with a + zero error code (i.e. "no error"), if then second row fails, query + will fail on slave too and slave will stop (wrongly believing that the + master got no error). + So we fallback to non-delayed INSERT. + */ + lock_type= TL_WRITE; + } table_list->lock_type= lock_type; #ifndef EMBEDDED_LIBRARY From 2f75c9cd69412de5d3736e775dd302ebdbc1e36d Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 15 Feb 2007 15:50:56 +0100 Subject: [PATCH 11/15] Backport from the Falcon tree. When opening/creating the transaction coordinator's log, if binlog is used, the tc log is the binlog so we use the binlog's name; otherwise we use the mmap-based log, named after the mandatory argument of the --log-tc option (meant for that). --- sql/log.cc | 2 +- sql/mysqld.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/log.cc b/sql/log.cc index 1961a5b6f88..aef8e2eff2c 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2523,7 +2523,7 @@ int TC_LOG_MMAP::open(const char *opt_name) goto err; if (using_heuristic_recover()) return 1; - if ((fd= my_create(logname, O_RDWR, 0, MYF(MY_WME))) < 0) + if ((fd= my_create(logname, CREATE_MODE, O_RDWR, MYF(MY_WME))) < 0) goto err; inited=1; file_length= opt_tc_log_size; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index d4f9791dade..672f585e721 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3171,7 +3171,7 @@ server."); (TC_LOG *) &tc_log_mmap) : (TC_LOG *) &tc_log_dummy); - if (tc_log->open(opt_bin_logname)) + if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file)) { sql_print_error("Can't init tc log"); unireg_abort(1); From 39de08fddc0abfc86965d754671014c4482ab759 Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 15 Feb 2007 20:28:58 +0100 Subject: [PATCH 12/15] Manual merge from 5.0-rpl, of fixes for: 1) BUG#25507 "multi-row insert delayed + auto increment causes duplicate key entries on slave" (two concurrrent connections doing multi-row INSERT DELAYED to insert into an auto_increment column, caused replication slave to stop with "duplicate key error" (and binlog was wrong), and BUG#26116 "If multi-row INSERT DELAYED has errors, statement-based binlogging breaks" (the binlog was not accounting for all rows inserted, or slave could stop). The fix is that: in statement-based binlogging, a multi-row INSERT DELAYED is silently converted to a non-delayed INSERT. This is supposed to not affect many 5.1 users as in 5.1, the default binlog format is "mixed", which does not have the bug (the bug is only with binlog_format=STATEMENT). We should document how the system delayed_insert thread decides of its binlog format (which is not modified by this patch): this decision is taken when the thread is created and holds until it is terminated (is not affected by any later change via SET GLOBAL BINLOG_FORMAT). It is also not affected by the binlog format of the connection which issues INSERT DELAYED (this binlog format does not affect how the row will be binlogged). If one wants to change the binlog format of its server with SET GLOBAL BINLOG_FORMAT, it should do FLUSH TABLES to be sure all delayed_insert threads terminate and thus new threads are created, taking into account the new format. 2) BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values". When in an INSERT ON DUPLICATE KEY UPDATE, using an autoincrement column, we inserted some autogenerated values and also updated some rows, some autogenerated values were not used (for example, even if 10 was the largest autoinc value in the table at the start of the statement, 12 could be the first autogenerated value inserted by the statement, instead of 11). One autogenerated value was lost per updated row. Led to exhausting the range of the autoincrement column faster. Bug introduced by fix of BUG#20188; present since 5.0.24 and 5.1.12. This bug breaks replication from a pre-5.0.24/pre-5.1.12 master. But the present bugfix, as it makes INSERT ON DUP KEY UPDATE behave like pre-5.0.24/pre-5.1.12, breaks replication from a [5.0.24,5.0.34]/[5.1.12,5.1.15] master to a fixed (5.0.36/5.1.16) slave! To warn users against this when they upgrade their slave, as agreed with the support team, we add code for a fixed slave to detect that it is connected to a buggy master in a situation (INSERT ON DUP KEY UPDATE into autoinc column) likely to break replication, in which case it cannot replicate so stops and prints a message to the slave's error log and to SHOW SLAVE STATUS. For 5.0.36->[5.0.24,5.0.34] replication or 5.1.16->[5.1.12,5.1.15] replication we cannot warn as master does not know the slave's version (but we always recommended to users to have slave at least as new as master). As agreed with support, I have asked for an alert to be put into the MySQL Network Monitoring and Advisory Service. 3) note that I'll re-enable rpl_insert_id as soon as 5.1-rpl gets the changes from the main 5.1. --- .../rpl_tests}/rpl_insert_delayed.test | 29 ++++-- mysql-test/extra/rpl_tests/rpl_insert_id.test | 55 +++++++++++- mysql-test/r/rpl_insert_id.result | 2 +- mysql-test/r/rpl_known_bugs_detection.result | 4 +- ...d.result => rpl_row_insert_delayed.result} | 23 ++++- mysql-test/r/rpl_stm_insert_delayed.result | 88 +++++++++++++++++++ mysql-test/t/disabled.def | 1 + mysql-test/t/rpl_known_bugs_detection.test | 3 + mysql-test/t/rpl_row_insert_delayed.test | 14 +++ mysql-test/t/rpl_stm_insert_delayed.test | 20 +++++ sql/slave.cc | 40 ++++----- sql/sql_insert.cc | 50 ++++++++++- 12 files changed, 296 insertions(+), 33 deletions(-) rename mysql-test/{t => extra/rpl_tests}/rpl_insert_delayed.test (64%) rename mysql-test/r/{rpl_insert_delayed.result => rpl_row_insert_delayed.result} (60%) create mode 100644 mysql-test/r/rpl_stm_insert_delayed.result create mode 100644 mysql-test/t/rpl_row_insert_delayed.test create mode 100644 mysql-test/t/rpl_stm_insert_delayed.test diff --git a/mysql-test/t/rpl_insert_delayed.test b/mysql-test/extra/rpl_tests/rpl_insert_delayed.test similarity index 64% rename from mysql-test/t/rpl_insert_delayed.test rename to mysql-test/extra/rpl_tests/rpl_insert_delayed.test index 3f72f3a3625..11856953959 100644 --- a/mysql-test/t/rpl_insert_delayed.test +++ b/mysql-test/extra/rpl_tests/rpl_insert_delayed.test @@ -1,14 +1,32 @@ ---source include/master-slave.inc ---source include/not_embedded.inc ---source include/not_windows.inc +# The two bugs below (BUG#25507 and BUG#26116) existed only in +# statement-based binlogging; we test that now they are fixed; +# we also test that mixed and row-based binlogging work too, +# for completeness. connection master; +--disable_warnings +CREATE SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +--enable_warnings -let $binlog_format_statement=1; +select @@global.binlog_format; + +# +# BUG#25507 "multi-row insert delayed + auto increment causes +# duplicate key entries on slave"; +# happened only in statement-based binlogging. +# CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +let $query = "INSERT DELAYED INTO t1 VALUES (null, 'Dr. No'), (null, 'From Russia With Love'), (null, 'Goldfinger'), (null, 'Thunderball'), (null, 'You Only Live Twice')"; +--exec $MYSQL_SLAP --silent --concurrency=5 --iterations=200 --query=$query --delimiter=";" +FLUSH TABLE t1; # another way to be sure INSERT DELAYED has inserted +SELECT COUNT(*) FROM t1; +# when bug existed slave failed below ("duplicate key" error at random INSERT) sync_slave_with_master; +use mysqlslap; +SELECT COUNT(*) FROM t1; # # BUG#26116 "If multi-row INSERT DELAYED has errors, @@ -62,6 +80,7 @@ select * from t1; # clean up connection master; -drop table t1; +USE test; +DROP SCHEMA mysqlslap; sync_slave_with_master; connection master; diff --git a/mysql-test/extra/rpl_tests/rpl_insert_id.test b/mysql-test/extra/rpl_tests/rpl_insert_id.test index 33194270d37..428de5f8b47 100644 --- a/mysql-test/extra/rpl_tests/rpl_insert_id.test +++ b/mysql-test/extra/rpl_tests/rpl_insert_id.test @@ -209,7 +209,7 @@ connection master; drop function bug15728; drop function bug15728_insert; -drop table t1; +drop table t1,t2; drop procedure foo; # test of BUG#20188 REPLACE or ON DUPLICATE KEY UPDATE in @@ -276,6 +276,59 @@ connection master; drop table t1; sync_slave_with_master; +# +# BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" +# + +connection master; +# testcase with INSERT VALUES +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +sync_slave_with_master; +SELECT * FROM t1; +connection master; +drop table t1; + +# tescase with INSERT SELECT +CREATE TABLE t1 ( + id bigint(20) unsigned NOT NULL auto_increment, + field_1 int(10) unsigned NOT NULL, + field_2 varchar(255) NOT NULL, + field_3 varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( + field_a int(10) unsigned NOT NULL, + field_b varchar(255) NOT NULL, + field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +# Updating table t1 based on values from table t2 +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +# Inserting new record into t2 +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +# Updating t1 again +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +sync_slave_with_master; +SELECT * FROM t1; +connection master; +drop table t1, t2; # # BUG#20339: stored procedure using LAST_INSERT_ID() does not diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index a88864b59c4..b0c1b6cfd73 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -196,7 +196,7 @@ id last_id 3 5 drop function bug15728; drop function bug15728_insert; -drop table t1; +drop table t1,t2; drop procedure foo; create table t1 (n int primary key auto_increment not null, b int, unique(b)); diff --git a/mysql-test/r/rpl_known_bugs_detection.result b/mysql-test/r/rpl_known_bugs_detection.result index c23586c6f61..eabc6185780 100644 --- a/mysql-test/r/rpl_known_bugs_detection.result +++ b/mysql-test/r/rpl_known_bugs_detection.result @@ -33,7 +33,7 @@ Replicate_Wild_Ignore_Table Last_Errno 1105 Last_Error Error 'master may suffer from http://bugs.mysql.com/bug.php?id=24432 so slave stops; check error log on slave for more info' on query. Default database: 'test'. Query: 'INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10' Skip_Counter 0 -Exec_Master_Log_Pos 238 +Exec_Master_Log_Pos 242 Relay_Log_Space # Until_Condition None Until_Log_File @@ -115,7 +115,7 @@ FROM t2 ON DUPLICATE KEY UPDATE t1.field_3 = t2.field_c' Skip_Counter 0 -Exec_Master_Log_Pos 1270 +Exec_Master_Log_Pos 1274 Relay_Log_Space # Until_Condition None Until_Log_File diff --git a/mysql-test/r/rpl_insert_delayed.result b/mysql-test/r/rpl_row_insert_delayed.result similarity index 60% rename from mysql-test/r/rpl_insert_delayed.result rename to mysql-test/r/rpl_row_insert_delayed.result index 38e2cddd650..2044672f49d 100644 --- a/mysql-test/r/rpl_insert_delayed.result +++ b/mysql-test/r/rpl_row_insert_delayed.result @@ -4,21 +4,36 @@ reset master; reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; +set @old_global_binlog_format = @@global.binlog_format; +set @@global.binlog_format = row; +CREATE SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +select @@global.binlog_format; +@@global.binlog_format +ROW CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +FLUSH TABLE t1; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +use mysqlslap; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 truncate table t1; insert delayed into t1 values(10, "my name"); insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); -ERROR 23000: Duplicate entry '10' for key 1 flush table t1; select * from t1; id name 10 my name +20 James Bond select * from t1; id name 10 my name +20 James Bond delete from t1 where id!=10; insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); -ERROR 23000: Duplicate entry '10' for key 1 flush table t1; select * from t1; id name @@ -28,4 +43,6 @@ select * from t1; id name 10 my name 20 is Bond -drop table t1; +USE test; +DROP SCHEMA mysqlslap; +set @@global.binlog_format = @old_global_binlog_format; diff --git a/mysql-test/r/rpl_stm_insert_delayed.result b/mysql-test/r/rpl_stm_insert_delayed.result new file mode 100644 index 00000000000..1c003856eb9 --- /dev/null +++ b/mysql-test/r/rpl_stm_insert_delayed.result @@ -0,0 +1,88 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +set @old_global_binlog_format = @@global.binlog_format; +set @@global.binlog_format = statement; +CREATE SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +select @@global.binlog_format; +@@global.binlog_format +STATEMENT +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +FLUSH TABLE t1; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +use mysqlslap; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +truncate table t1; +insert delayed into t1 values(10, "my name"); +insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +ERROR 23000: Duplicate entry '10' for key 'PRIMARY' +flush table t1; +select * from t1; +id name +10 my name +select * from t1; +id name +10 my name +delete from t1 where id!=10; +insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +ERROR 23000: Duplicate entry '10' for key 'PRIMARY' +flush table t1; +select * from t1; +id name +10 my name +20 is Bond +select * from t1; +id name +10 my name +20 is Bond +USE test; +DROP SCHEMA mysqlslap; +set @@global.binlog_format = mixed; +CREATE SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +select @@global.binlog_format; +@@global.binlog_format +MIXED +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +FLUSH TABLE t1; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +use mysqlslap; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +truncate table t1; +insert delayed into t1 values(10, "my name"); +insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +flush table t1; +select * from t1; +id name +10 my name +20 James Bond +select * from t1; +id name +10 my name +20 James Bond +delete from t1 where id!=10; +insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +flush table t1; +select * from t1; +id name +10 my name +20 is Bond +select * from t1; +id name +10 my name +20 is Bond +USE test; +DROP SCHEMA mysqlslap; +set @@global.binlog_format = @old_global_binlog_format; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index ce7bb345aad..08448a6bb30 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -38,4 +38,5 @@ synchronization : Bug#24529 Test 'synchronization' fails on Mac pushb flush2 : Bug#24805 Pushbuild can't handle test with --disable-log-bin mysql_upgrade : Bug#25074 mysql_upgrade gives inconsisten results +rpl_insert_id : Guilhem will enable as soon as replication team tree has merged with main tree and so got version number 5.1.16 diff --git a/mysql-test/t/rpl_known_bugs_detection.test b/mysql-test/t/rpl_known_bugs_detection.test index 4719716d4a1..ce3debf3c5b 100644 --- a/mysql-test/t/rpl_known_bugs_detection.test +++ b/mysql-test/t/rpl_known_bugs_detection.test @@ -6,6 +6,9 @@ source include/have_debug.inc; source include/master-slave.inc; +# Currently only statement-based-specific bugs are here +-- source include/have_binlog_format_mixed_or_statement.inc + # # This is to test that slave properly detects if # master may suffer from: diff --git a/mysql-test/t/rpl_row_insert_delayed.test b/mysql-test/t/rpl_row_insert_delayed.test new file mode 100644 index 00000000000..9aeb57c4fa2 --- /dev/null +++ b/mysql-test/t/rpl_row_insert_delayed.test @@ -0,0 +1,14 @@ +--source include/have_binlog_format_row.inc +--source include/master-slave.inc +--source include/not_embedded.inc +--source include/not_windows.inc + +connection master; +set @old_global_binlog_format = @@global.binlog_format; + +let $binlog_format_statement=0; +set @@global.binlog_format = row; +--source extra/rpl_tests/rpl_insert_delayed.test + +connection master; +set @@global.binlog_format = @old_global_binlog_format; diff --git a/mysql-test/t/rpl_stm_insert_delayed.test b/mysql-test/t/rpl_stm_insert_delayed.test new file mode 100644 index 00000000000..d55e3a4da2c --- /dev/null +++ b/mysql-test/t/rpl_stm_insert_delayed.test @@ -0,0 +1,20 @@ +# we run first in statement-based then in mixed binlogging + +--source include/have_binlog_format_mixed_or_statement.inc +--source include/master-slave.inc +--source include/not_embedded.inc +--source include/not_windows.inc + +connection master; +set @old_global_binlog_format = @@global.binlog_format; + +let $binlog_format_statement=1; +set @@global.binlog_format = statement; +--source extra/rpl_tests/rpl_insert_delayed.test + +let $binlog_format_statement=0; +set @@global.binlog_format = mixed; +--source extra/rpl_tests/rpl_insert_delayed.test + +connection master; +set @@global.binlog_format = @old_global_binlog_format; diff --git a/sql/slave.cc b/sql/slave.cc index 022647afcee..e51fca39fd8 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3684,31 +3684,31 @@ bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id) (memcmp(introduced_in, master_ver, 3) <= 0) && (memcmp(fixed_in, master_ver, 3) > 0)) { - // a verbose message for the error log - slave_print_error(rli, ER_UNKNOWN_ERROR, - "According to the master's version ('%s')," - " it is probable that master suffers from this bug:" - " http://bugs.mysql.com/bug.php?id=%u" - " and thus replicating the current binary log event" - " may make the slave's data become different from the" - " master's data." - " To take no risk, slave refuses to replicate" - " this event and stops." - " We recommend that all updates be stopped on the" - " master and slave, that the data of both be" - " manually synchronized," - " that master's binary logs be deleted," - " that master be upgraded to a version at least" - " equal to '%d.%d.%d'. Then replication can be" - " restarted.", - rli->relay_log.description_event_for_exec->server_version, - bug_id, - fixed_in[0], fixed_in[1], fixed_in[2]); // a short message for SHOW SLAVE STATUS (message length constraints) my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from" " http://bugs.mysql.com/bug.php?id=%u" " so slave stops; check error log on slave" " for more info", MYF(0), bug_id); + // a verbose message for the error log + slave_print_msg(ERROR_LEVEL, rli, ER_UNKNOWN_ERROR, + "According to the master's version ('%s')," + " it is probable that master suffers from this bug:" + " http://bugs.mysql.com/bug.php?id=%u" + " and thus replicating the current binary log event" + " may make the slave's data become different from the" + " master's data." + " To take no risk, slave refuses to replicate" + " this event and stops." + " We recommend that all updates be stopped on the" + " master and slave, that the data of both be" + " manually synchronized," + " that master's binary logs be deleted," + " that master be upgraded to a version at least" + " equal to '%d.%d.%d'. Then replication can be" + " restarted.", + rli->relay_log.description_event_for_exec->server_version, + bug_id, + fixed_in[0], fixed_in[1], fixed_in[2]); return TRUE; } } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 989b78f3517..ecb2d450d30 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -59,6 +59,7 @@ #include "sql_trigger.h" #include "sql_select.h" #include "sql_show.h" +#include "slave.h" static int check_null_fields(THD *thd,TABLE *entry); #ifndef EMBEDDED_LIBRARY @@ -341,6 +342,36 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, (duplic == DUP_UPDATE)) lock_type=TL_WRITE; #endif + if ((lock_type == TL_WRITE_DELAYED) && + (global_system_variables.binlog_format == BINLOG_FORMAT_STMT) && + log_on && mysql_bin_log.is_open() && + (values_list.elements > 1)) + { + /* + Statement-based binary logging does not work in this case, because: + a) two concurrent statements may have their rows intermixed in the + queue, leading to autoincrement replication problems on slave (because + the values generated used for one statement don't depend only on the + value generated for the first row of this statement, so are not + replicable) + b) if first row of the statement has an error the full statement is + not binlogged, while next rows of the statement may be inserted. + c) if first row succeeds, statement is binlogged immediately with a + zero error code (i.e. "no error"), if then second row fails, query + will fail on slave too and slave will stop (wrongly believing that the + master got no error). + So we fallback to non-delayed INSERT. + Note that to be fully correct, we should test the "binlog format which + the delayed thread is going to use for this row". But in the common case + where the global binlog format is not changed and the session binlog + format may be changed, that is equal to the global binlog format. + We test it without mutex for speed reasons (condition rarely true), and + in the common case (global not changed) it is as good as without mutex; + if global value is changed, anyway there is uncertainty as the delayed + thread may be old and use the before-the-change value. + */ + lock_type= TL_WRITE; + } table_list->lock_type= lock_type; #ifndef EMBEDDED_LIBRARY @@ -454,6 +485,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->cuted_fields = 0L; table->next_number_field=table->found_next_number_field; +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + goto abort; +#endif + error=0; thd->proc_info="update"; if (duplic != DUP_ERROR || ignore) @@ -1146,13 +1185,13 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (res == VIEW_CHECK_ERROR) goto before_trg_err; + table->file->restore_auto_increment(prev_insert_id); if ((error=table->file->ha_update_row(table->record[1], table->record[0]))) { if (info->ignore && !table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) { - table->file->restore_auto_increment(prev_insert_id); goto ok_or_after_trg_err; } goto err; @@ -2489,6 +2528,15 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) } restore_record(table,s->default_values); // Get empty record table->next_number_field=table->found_next_number_field; + +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + DBUG_RETURN(1); +#endif + thd->cuted_fields=0; if (info.ignore || info.handle_duplicates != DUP_ERROR) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); From 44c6c4cc0540365ea865123a3965235bba00e530 Mon Sep 17 00:00:00 2001 From: "gbichot@dl145h.mysql.com" <> Date: Fri, 23 Feb 2007 15:32:51 +0100 Subject: [PATCH 13/15] the fix for BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" didn't make it into 5.0.36 and 5.1.16, so we need to adjust the bug-detection-based-on-version-number code. Because the rpl tree has a too old version, rpl_insert_id cannot pass, so I disable it (like is already the case in 5.1-rpl for the same reason), and the repl team will re-enable it when they merge 5.0 and 5.1 into their trees (thus getting the right version number). --- mysql-test/t/disabled.def | 1 + sql/slave.cc | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 3213bd4eb5b..1464ddaa6b0 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -14,4 +14,5 @@ im_daemon_life_cycle : Bug#24415 see note: [19 Dec 23:17] Trudy Pelzer ndb_load : Bug#17233 user_limits : Bug#23921 random failure of user_limits.test flush2 : Bug#24805 Pushbuild can't handle test with --disable-log-bin +rpl_insert_id : replication team will enable as soon as they merge their tree with main tree and so got version number 5.0.38 diff --git a/sql/slave.cc b/sql/slave.cc index 37c782a42c3..f61d1e2ed69 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -5181,8 +5181,8 @@ bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id) }; static struct st_version_range_for_one_bug versions_for_all_bugs[]= { - {24432, { 5, 0, 24 }, { 5, 0, 36 } }, - {24432, { 5, 1, 12 }, { 5, 1, 16 } } + {24432, { 5, 0, 24 }, { 5, 0, 38 } }, + {24432, { 5, 1, 12 }, { 5, 1, 17 } } }; const uchar *master_ver= rli->relay_log.description_event_for_exec->server_version_split; From 54b04ff5c80f3e56d900fc68750ef7b78b11d61d Mon Sep 17 00:00:00 2001 From: "mats@romeo.(none)" <> Date: Fri, 23 Feb 2007 18:54:26 +0100 Subject: [PATCH 14/15] BUG#19033 (RBR: slave does not handle schema changes correctly): Post-merge fixes. --- include/my_global.h | 11 +++++++++++ sql/slave.h | 9 --------- storage/ndb/include/ndb_global.h.in | 2 -- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/my_global.h b/include/my_global.h index 1c238f0392f..798f195ce80 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -1512,4 +1512,15 @@ do { doubleget_union _tmp; \ #define dlerror() "" #endif +/* + Define placement versions of operator new and operator delete since + we cannot be sure that the include exists. + */ +#ifdef __cplusplus +inline void *operator new(size_t, void *ptr) { return ptr; } +inline void *operator new[](size_t, void *ptr) { return ptr; } +inline void operator delete(void*, void*) { /* Do nothing */ } +inline void operator delete[](void*, void*) { /* Do nothing */ } +#endif + #endif /* my_global_h */ diff --git a/sql/slave.h b/sql/slave.h index 226f19fcd0c..a0febe3fd73 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -214,15 +214,6 @@ extern I_List threads; #define SLAVE_IO 1 #define SLAVE_SQL 2 -/* - Define placement versions of operator new and operator delete since - we cannot be sure that the include exists. - */ -inline void *operator new(size_t, void *ptr) { return ptr; } -inline void *operator new[](size_t, void *ptr) { return ptr; } -inline void operator delete(void*, void*) { /* Do nothing */ } -inline void operator delete[](void*, void*) { /* Do nothing */ } - #endif diff --git a/storage/ndb/include/ndb_global.h.in b/storage/ndb/include/ndb_global.h.in index 60d32f62ee3..dd4303f949c 100644 --- a/storage/ndb/include/ndb_global.h.in +++ b/storage/ndb/include/ndb_global.h.in @@ -115,8 +115,6 @@ static const char table_name_separator = '/'; #endif #ifdef __cplusplus -inline void* operator new(size_t, void* __p) { return __p; } -inline void* operator new[](size_t, void* __p) { return __p; } extern "C" { #endif From ba2452f092acdb519043a23d72065d559a92ec21 Mon Sep 17 00:00:00 2001 From: "gbichot@dl145h.mysql.com" <> Date: Fri, 23 Feb 2007 22:23:54 +0100 Subject: [PATCH 15/15] Fix for BUG#25628: "mysqlbinlog crashes while processing binary logs". mysqlbinlog prints all row-based events of a single statement as a single "BINLOG" statement containing the concatenation of those events. Big (i.e. >64k) concatenations of row-based events (e.g. Write_rows_log_event) caused mysqlbinlog's IO_CACHE to overflow to a temporary file but the IO_CACHE had not been inited with open_cached_file(), so it tried to create a temporary file in an uninitialized directory (thus failing to create, then to write; some OS errors were printed, and it finally segfaulted). After fixing this, it appeared that mysqlbinlog was printing only a piece of big concatenations of row-based events (it printed at most the size of the IO_CACHE's buffer i.e. 64k); that caused data loss at restore. We fix and test that. Last, mysqlbinlog's printouts looked a bit strange with the informative header (#-prefixed) of groupped Rows_log_event all on one line, so we insert \n. After that, a small bug in the --hexdump code appeared (only if the string to hex-print had its length a multiple of 16), we fix it. --- client/mysqlbinlog.cc | 10 ++++----- mysql-test/r/mysqlbinlog_base64.result | 20 ++++++++++++++++++ mysql-test/t/mysqlbinlog_base64.test | 28 ++++++++++++++++++++++++++ mysys/mf_iocache2.c | 17 +++++++--------- sql/log_event.cc | 9 +++++++-- sql/log_event.h | 15 ++++++++------ 6 files changed, 76 insertions(+), 23 deletions(-) diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 8242a481c5b..8d63ec56a30 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -487,18 +487,15 @@ write_event_header_and_base64(Log_event *ev, FILE *result_file, DBUG_ENTER("write_event_header_and_base64"); /* Write header and base64 output to cache */ IO_CACHE result_cache; - if (init_io_cache(&result_cache, -1, 0, WRITE_CACHE, 0L, FALSE, - MYF(MY_WME | MY_NABP))) - { + if (open_cached_file(&result_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP))) return 1; - } ev->print_header(&result_cache, print_event_info, FALSE); ev->print_base64(&result_cache, print_event_info, FALSE); /* Read data from cache and write to result file */ my_b_copy_to_file(&result_cache, result_file); - end_io_cache(&result_cache); + close_cached_file(&result_cache); DBUG_RETURN(0); } @@ -1016,6 +1013,9 @@ static int dump_log_entries(const char* logname) { int rc; PRINT_EVENT_INFO print_event_info; + + if (!print_event_info.init_ok()) + return 1; /* Set safe delimiter, to dump things like CREATE PROCEDURE safely diff --git a/mysql-test/r/mysqlbinlog_base64.result b/mysql-test/r/mysqlbinlog_base64.result index 33047a8774d..b62023e0ccf 100644 --- a/mysql-test/r/mysqlbinlog_base64.result +++ b/mysql-test/r/mysqlbinlog_base64.result @@ -86,5 +86,25 @@ Aberdeen Abernathy aberrant aberration +flush logs; +drop table t2; +create table t2 (word varchar(20)); +load data infile '../std_data_ln/words.dat' into table t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +select count(*) from t2; +count(*) +35840 +flush logs; +select count(*) from t2; +count(*) +35840 drop table t1; drop table t2; diff --git a/mysql-test/t/mysqlbinlog_base64.test b/mysql-test/t/mysqlbinlog_base64.test index 1ca018218b2..1b5dc67c150 100644 --- a/mysql-test/t/mysqlbinlog_base64.test +++ b/mysql-test/t/mysqlbinlog_base64.test @@ -31,6 +31,34 @@ drop table t2; select * from t1; select * from t2; +# +# Verify that events larger than the default IO_CACHE buffer +# are handled correctly (BUG#25628). +# +flush logs; +drop table t2; +create table t2 (word varchar(20)); +load data infile '../std_data_ln/words.dat' into table t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +select count(*) from t2; + +flush logs; +--exec $MYSQL_BINLOG --hexdump $MYSQLTEST_VARDIR/log/master-bin.000003 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_base64.sql +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/mysqlbinlog_base64.sql + +# +# Verify that all binlog events have been executed +# +select count(*) from t2; + # # Test cleanup # diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c index 7b3393da4f5..a85f741bd67 100644 --- a/mysys/mf_iocache2.c +++ b/mysys/mf_iocache2.c @@ -50,7 +50,6 @@ int my_b_copy_to_file(IO_CACHE *cache, FILE *file) { - byte buf[IO_SIZE]; uint bytes_in_cache; DBUG_ENTER("my_b_copy_to_file"); @@ -58,19 +57,17 @@ my_b_copy_to_file(IO_CACHE *cache, FILE *file) if (reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE)) DBUG_RETURN(1); bytes_in_cache= my_b_bytes_in_cache(cache); - while (bytes_in_cache > 0) { - uint const read_bytes= min(bytes_in_cache, sizeof(buf)); - DBUG_PRINT("debug", ("Remaining %u bytes - Reading %u bytes", - bytes_in_cache, read_bytes)); - if (my_b_read(cache, buf, read_bytes)) + do + { + if (my_fwrite(file, cache->read_pos, bytes_in_cache, + MYF(MY_WME | MY_NABP)) == (uint) -1) DBUG_RETURN(1); - if (my_fwrite(file, buf, read_bytes, MYF(MY_WME | MY_NABP)) == (uint) -1) - DBUG_RETURN(1); - bytes_in_cache -= read_bytes; - } + cache->read_pos= cache->read_end; + } while ((bytes_in_cache= my_b_fill(cache))); DBUG_RETURN(0); } + my_off_t my_b_append_tell(IO_CACHE* info) { /* diff --git a/sql/log_event.cc b/sql/log_event.cc index a326e226320..e4df8d71dc6 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1144,13 +1144,18 @@ void Log_event::print_header(IO_CACHE* file, char emit_buf[256]; int const bytes_written= my_snprintf(emit_buf, sizeof(emit_buf), - "# %8.8lx %-48.48s |%s|\n# ", + "# %8.8lx %-48.48s |%s|\n", (unsigned long) (hexdump_from + (i & 0xfffffff0)), hex_string, char_string); DBUG_ASSERT(bytes_written >= 0); DBUG_ASSERT(static_cast(bytes_written) < sizeof(emit_buf)); my_b_write(file, (byte*) emit_buf, bytes_written); } + /* + need a # to prefix the rest of printouts for example those of + Rows_log_event::print_helper(). + */ + my_b_write(file, "# ", 2); } DBUG_VOID_RETURN; } @@ -6106,7 +6111,7 @@ void Rows_log_event::print_helper(FILE *file, { bool const last_stmt_event= get_flags(STMT_END_F); print_header(head, print_event_info, !last_stmt_event); - my_b_printf(head, "\t%s: table id %lu", name, m_table_id); + my_b_printf(head, "\t%s: table id %lu\n", name, m_table_id); print_base64(body, print_event_info, !last_stmt_event); } diff --git a/sql/log_event.h b/sql/log_event.h index 20a3ca1b97e..ac7bc0cb082 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -546,16 +546,19 @@ typedef struct st_print_event_info bzero(db, sizeof(db)); bzero(charset, sizeof(charset)); bzero(time_zone_str, sizeof(time_zone_str)); - strcpy(delimiter, ";"); - uint const flags = MYF(MY_WME | MY_NABP); - init_io_cache(&head_cache, -1, 0, WRITE_CACHE, 0L, FALSE, flags); - init_io_cache(&body_cache, -1, 0, WRITE_CACHE, 0L, FALSE, flags); + delimiter[0]= ';'; + delimiter[1]= 0; + myf const flags = MYF(MY_WME | MY_NABP); + open_cached_file(&head_cache, NULL, NULL, 0, flags); + open_cached_file(&body_cache, NULL, NULL, 0, flags); } ~st_print_event_info() { - end_io_cache(&head_cache); - end_io_cache(&body_cache); + close_cached_file(&head_cache); + close_cached_file(&body_cache); } + bool init_ok() /* tells if construction was successful */ + { return my_b_inited(&head_cache) && my_b_inited(&body_cache); } /* Settings on how to print the events */