From 38fd186721416536b0d61b816920005c8ac5a1f7 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Dec 2006 18:14:28 +0400 Subject: [PATCH 01/46] Bug#24747 XPath error with the node name "Text" mysql-test/r/xml.result: Adding test case mysql-test/t/xml.test: Adding test case sql/item_xmlfunc.cc: Bug#24747 XPath error with the node name "Text" Problem: keywords, nodetype names and axis names didn't work in node name context. Fix: 1. Changes in lexem scanner behaviour when an identifier has been read: - search through axis names and return MY_XPATH_AXIS_xxx only when identifier is followed by two semicolons. Don't check axis names and return MY_XPATH_IDENT of not followed by two semicolons. - search through nodetype names and return MY_XPATH_LEX_NODETYPE only when identifier is followed by left parenthesis. Don't check nodetype names and return MY_XPATH_LEX_IDENT when not followed by parenthesis 2. Change in syntax analizer: QName scanner now accepts keywords AND, OR, MOD, DIV as valid node names. --- 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 df1226ba3ff63190ae2335562cc7143c90ba3f3b Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Jan 2007 16:01:00 +0200 Subject: [PATCH 02/46] 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: changed mysql-test/t/rpl_switch_stm_row_mixed.test: correcting interpretation of DEFAULT for binlog_format, comments on test purpose, switching binlog_format section is formed. sql/mysqld.cc: introducing command line option parser's opt_binlog_format and its default. necessary code for transfering text value from command line arg into the system variable @@global.binlog_format. --- 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 def55ff9c1539ad2d75bb69dba6c9fc1e789b2f7 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 18 Jan 2007 13:20:38 -0500 Subject: [PATCH 03/46] 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: BUG#21490 - No warning issued for deprecated replication parameters This patch modifies the WARN_DEPRECATED macro to permit displaying a deprecated warning to the user at startup when the replication startup options are detected. It displays the same messae as the original version of the macro but allows the macro to be called before a thread is created and the ER() macro is defined. --- 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 cf0b194d6cbaa4a064a0fc0fcef1c74d921e1e33 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 18 Jan 2007 13:35:49 -0500 Subject: [PATCH 04/46] 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: BUG#21490 - No warning issued for deprecated replication parameters This patch modifies the WARN_DEPRECATED macro to permit displaying a deprecated warning to the user at startup when the replication startup options are detected. It displays the same messae as the original version of the macro but allows the macro to be called before a thread is created and the ER() macro is defined. --- 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 6a7933cd848c004744fab34be214733396460404 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Jan 2007 14:32:10 +0400 Subject: [PATCH 05/46] 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: Adding test case mysql-test/t/show_check.test: Adding test case sql/sql_show.cc: Adding tablename to filename encoding. Removing variable "end" with is not used anymore. --- 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 fb33da5f569bd3fac9d658ba5eaaeeab285c7085 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 Jan 2007 16:37:38 +0400 Subject: [PATCH 06/46] 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: Adding test case mysql-test/t/ctype_cp932.test: Adding test case sql/field.cc: Change warning condition from: "if number of bytes written to destination is less than full source length" to "if number of bytes read from source is less than full source length" --- 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 8a8bb772d4ca23ca01340c8445cf66353bfb58ea Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 26 Jan 2007 19:29:57 +0100 Subject: [PATCH 07/46] 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: Moving placement new operators to slave.h since they are used in several places in the replication code. Removing some compile warnings. sql/log_event.cc: Moving code to check table compatibility to after tables are locked for writing. sql/log_event.h: Doxygenifying comments. Copying error codes to Rows_log_event hierarchy since they are now used there as well. sql/rpl_utility.h: Doxygenifying some comments. Changing class table_def to copy the column types given to it. Adding structure RPL_TABLE_LIST as a subclass of TABLE_LIST to represent lists of tables for the slave. sql/slave.h: Adding placement new and delete operators since the slave uses them in several places. sql/sql_insert.cc: Removing a compiler warning. --- 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 fa22d34ea2ffb74409569df0525ac5c22d4ddc0d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Feb 2007 10:11:45 +0400 Subject: [PATCH 08/46] Additional fix for bug 25815 for test results when running with --binlog-format=row mysql-test/r/binlog_row_ctype_cp932.result: Test result fix --- 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 d51d6adca7947c3fde0263b9f8da78788dc5b3f5 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 3 Feb 2007 20:14:16 +0100 Subject: [PATCH 09/46] BUG#25306 (Race conditions during replication slave shutdown (valgrind stacks)): The possibility of the race is removed by changing sequence of calls pthread_mutex_unlock(&mi->run_lock); pthread_cond_broadcast(&mi->stop_cond); into pthread_cond_broadcast(&mi->stop_cond); pthread_mutex_unlock(&mi->run_lock); at the end of I/O thread (similar change at the end of SQL thread). This ensures that no thread waiting on the condition executes between the broadcast and the unlock and thus can't delete the mi structure which caused the bug. sql/slave.cc: Change order of condition broadcast and mutex unlock at the end of slave's I/O thread and SQL thread. sql/sql_repl.cc: Add DBUG_ENTER() call --- sql/slave.cc | 19 ++++++++++++++----- sql/sql_repl.cc | 8 +++++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index 8805f950d50..13793705b9a 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3758,8 +3758,13 @@ err: mi->abort_slave= 0; mi->slave_running= 0; mi->io_thd= 0; - pthread_mutex_unlock(&mi->run_lock); + /* + Note: the order of the two following calls (first broadcast, then unlock) + is important. Otherwise a killer_thread can execute between the calls and + delete the mi structure leading to a crash! (see BUG#25306 for details) + */ pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done + pthread_mutex_unlock(&mi->run_lock); #ifndef DBUG_OFF if (abort_slave_event_count && !events_till_abort) goto slave_begin; @@ -3977,8 +3982,13 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); - pthread_cond_broadcast(&rli->stop_cond); + /* + Note: the order of the broadcast and unlock calls below (first broadcast, then unlock) + is important. Otherwise a killer_thread can execute between the calls and + delete the mi structure leading to a crash! (see BUG#25306 for details) + */ + pthread_cond_broadcast(&rli->stop_cond); #ifndef DBUG_OFF /* Bug #19938 Valgrind error (race) in handle_slave_sql() @@ -3986,9 +3996,8 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ */ const int eta= rli->events_till_abort; #endif - - // tell the world we are done - pthread_mutex_unlock(&rli->run_lock); + pthread_mutex_unlock(&rli->run_lock); // tell the world we are done + #ifndef DBUG_OFF // TODO: reconsider the code below if (abort_slave_event_count && !eta) goto slave_begin; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 88a752f7acb..004abc775bf 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -882,12 +882,14 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { + DBUG_ENTER("stop_slave"); + int slave_errno; if (!thd) thd = current_thd; if (check_access(thd, SUPER_ACL, any_db,0,0,0,0)) - return 1; + DBUG_RETURN(1); thd->proc_info = "Killing slave"; int thread_mask; lock_slave_threads(mi); @@ -921,12 +923,12 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { if (net_report) my_message(slave_errno, ER(slave_errno), MYF(0)); - return 1; + DBUG_RETURN(1); } else if (net_report) send_ok(thd); - return 0; + DBUG_RETURN(0); } From 914ae41f3310d6fe8994a86b38930c1c3375dc0e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 8 Feb 2007 15:53:14 +0100 Subject: [PATCH 10/46] 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: results to check the bugfix; without the bugfix, you would see, in master and slave: "3,2" instead of "2,2" for the INSERT VALUES testcase, "11,6,..." instead of "6,6,..." for the INSERT SELECT testcase. mysql-test/t/rpl_insert_id.test: testing that BUG#24432 is fixed sql/log_event.cc: A trick to force the master to pretend it is old and features BUG#24432. To do fast lookups in the list of known bugs by version, we compute the 3 X.Y.Z numbers from the master's version string and cache that into a new member Format_description_log_event::server_version_split. We do this computation in the event's two constructors. A simple prevention against buffer overrun when reading the master's version from a binlog event (assume the event is corrupted on disk, and so the version string on disk is longer than ST_SERVER_VER_LEN (50), then we would not get a closing 0 at the end of the class member. sql/log_event.h: new member to hold the "split server version" (3 numbers X.Y.Z), and a method to compute this from the version string. sql/slave.cc: a function which tells, based on master's version (as found in the Format_description event in the relay log being executed), if master can have a certain bug. This function uses a list of bug_id / first_version_with_bug / first_version_with_fix. If the test is positive, a short error message is put into SHOW SLAVE STATUS, and a verbose message is put into the slave's error log. The caller is expected to stop the slave in this case. sql/slave.h: new function to test if the replication master has a bug sql/sql_insert.cc: Fix for BUG#24432:t he reason was a misplaced restore_auto_increment() (misplaced when fixing BUG#20188). Indeed, when updating the row, it is clear that the autogenerated auto_increment value will not be used for this row (and if by "chance" the autoinc value present in the updated row is >= to the not used autogenerated value, adjust_next_insert_id_after_explicit_value() will fix next_insert_id). We also add code for a fixed slave to detect that it is connected to a buggy master (in which case it cannot replicate so stops). mysql-test/r/rpl_known_bugs_detection.result: see that SHOW SLAVE STATUS prints information that slave found a bug in master, and does not execute the dangerous event (table stays empty). mysql-test/t/rpl_known_bugs_detection-master.opt: pass debug symbol to make the master pretend it has BUG#24432 mysql-test/t/rpl_known_bugs_detection.test: new test to see if bug detection by slave works --- 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 33f24f2c58b97397c872616b1d5bf692dffa86fd Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 8 Feb 2007 15:57:28 +0100 Subject: [PATCH 11/46] BUG#25463 (Memory allocation problems in replication slave thread): The problem is (most probably) caused by whole server shutting down before a slave thread terminates correctly. Fixing this requires fixing server shutdown code which has been done in 5.1 tree. Thus we ignore the issue in 5.0 assuming that it is fixed in 5.1. mysql-test/valgrind.supp: Added a suppresion rule. --- mysql-test/valgrind.supp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index f7eb9baa4c7..e71f4541cfd 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -346,3 +346,20 @@ fun:_ZN19TransporterRegistry11performSendEv fun:_ZN19TransporterRegistry14forceSendCheckEi } + +# +# BUG#25463: Probable cause - slave thread doesn't have time to terminate properly +# because rest of the server shuts down before that. The server shutdown code has +# been fixed in 5.1 and there this leak should not happen. Suppression has been +# approved by Monty. +# +# Important: do not propagate this rule to 5.1 tree! +# + +{ + Slave thread shutdown (BUG#25463) + Memcheck:Leak + fun:calloc + fun:my_thread_init + fun:handle_slave_io +} From f6834c02ca00ba2e07bebb61efa4eec4afad67ca Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Feb 2007 15:39:03 +0100 Subject: [PATCH 12/46] 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: result update mysql-test/t/innodb-replace.test: now that multi-row delayed inserts are converted to normal inserts if the statement-based binlog is enabled, no error is issued even if this engine does not support INSERT DELAYED, as the insert does not go through the INSERT DELAYED code. To preserve the goal of this test, we change the statements to single- row inserts. sql/sql_insert.cc: A multi-row INSERT DELAYED cannot be recorded to a statement-based binlog in a way that describes the insertions actually done; in that case we fallback to a non-delayed INSERT. mysql-test/r/rpl_insert_delayed.result: result. Master and slave match. mysql-test/t/rpl_insert_delayed.test: Test for BUG#26116 (see if one error at first row on master makes the slave's data incorrect, see if one error at second row on master makes slave stop). --- 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 9dfb1d9011cf5b31d139d3904a0bf3afae91ad24 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Feb 2007 15:50:56 +0100 Subject: [PATCH 13/46] 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: fixing wrong order of arguments to my_create() (3rd param is the desired modes of the file; "0" lead to no rights for anybody on the file). sql/mysqld.cc: 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 af71cac0e44da5e1671c9f8fca2cd4b99f9eb02e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Feb 2007 20:28:58 +0100 Subject: [PATCH 14/46] 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. BitKeeper/deleted/.del-rpl_insert_delayed.result: Delete: mysql-test/r/rpl_insert_delayed.result mysql-test/extra/rpl_tests/rpl_insert_delayed.test: tests for BUG#25507 (lauch many concurrent INSERT DELAYED into an auto_inc column and see if they cause duplicates) and BUG#26116 (see if one error at first row on master makes the slave's data incorrect). It is then incorporated into a statement-based and mixed binlogging test, and into a row-based test. It is in fact mysql-test/t/rpl_insert_delayed.test from 5.0, renamed and extended to test BUG#25507. mysql-test/extra/rpl_tests/rpl_insert_id.test: manual merge of test for BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" from 5.0 mysql-test/r/rpl_insert_id.result: result update (it automerged from 5.0, contrary to the test file) mysql-test/r/rpl_known_bugs_detection.result: result update (binlog event sizes differ from 5.0) mysql-test/t/disabled.def: rpl_insert_id tests statement-based replication of INSERT ON DUPLICATE KEY UPDATE. This type of INSERT had BUG#24432, which is fixed in 5.1.16; we made the slave detect if it is connected to a <5.1.16 master and if so refuse to replicate. The problem is that this 5.1-rpl tree, even though it will produce the 5.1.16 release, still has a 5.1.15 version in configure.in. Thus rpl_insert_id fails. So I disable it. As soon as the 5.1-rpl tree gets the changesets from the main 5.1, its version will change to 5.1.16 and so I'll be able to re-enable the test. mysql-test/t/rpl_known_bugs_detection.test: only in statement-based; row-based has no bug so test would fail. sql/slave.cc: slave_print_msg(ERROR_LEVEL) calls my_error(ER_UNKNOWN_ERROR); so, for our my_printf_error(), which has a nicer message than this my_error(), to have any effect, it must be called before slave_print_msg() sql/sql_insert.cc: manual merge from 5.0 of a piece of the fixes for BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" BUG#25507 "multi-row insert delayed + auto increment causes duplicate key entries on slave" BUG#26116 "If multi-row INSERT DELAYED has errors, statement-based binlogging breaks". The other pieces (e.g. log_event.cc) automerged from 5.0. mysql-test/r/rpl_row_insert_delayed.result: result mysql-test/r/rpl_stm_insert_delayed.result: result. Note how "mixed" and "statement" insert different data in the table. mysql-test/t/rpl_row_insert_delayed.test: wrapper to test INSERT DELAYED binlogging in row-based mode mysql-test/t/rpl_stm_insert_delayed.test: wrapper to test INSERT DELAYED binlogging in statement-based and mixed mode --- .../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 8c07265ce1444b0c6cf0d6938ef934ae7c9f8587 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 23 Feb 2007 15:32:51 +0100 Subject: [PATCH 15/46] 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: rpl_insert_id tests statement-based replication of INSERT ON DUPLICATE KEY UPDATE. This type of INSERT had BUG#24432, which is fixed in 5.0.38; we made the slave detect if it is connected to a <5.0.38 master and if so refuse to replicate. The problem is that this 5.0-rpl tree, even though it will produce the 5.0.38 release, still has a 5.0.36 version in configure.in. Thus rpl_insert_id fails. So I disable it. As soon as the 5.0-rpl tree gets the changesets from the main 5.0, its version will change to 5.0.38 and so the repl team will re-enable the test. sql/slave.cc: the fix for BUG#24432 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. --- 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 f60346193e20d6ac6fc9b25d66acc1c9d0c5ac0c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 23 Feb 2007 18:54:26 +0100 Subject: [PATCH 16/46] BUG#19033 (RBR: slave does not handle schema changes correctly): Post-merge fixes. include/my_global.h: Post-merge fixes. Moving placement versions of operator new and operator delete to general server-wide header. sql/slave.h: Post-merge fixes. Moving placement versions of operator new and operator delete to general server-wide header. storage/ndb/include/ndb_global.h.in: Removing local definition of placement versions of operator new and operator delete. --- 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 e52ec3e6d5adae554bc343d56a04bf6ea55d170e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 23 Feb 2007 12:58:56 -0500 Subject: [PATCH 17/46] BUG#20141 "User-defined variables are not replicated properly for SF/ Triggers in SBR mode." BUG#14914 "SP: Uses of session variables in routines are not always replicated" BUG#25167 "Dupl. usage of user-variables in trigger/function is not replicated correctly" User-defined variables used inside of stored functions/triggers in statements which did not update tables directly were not replicated. We also had problems with replication of user-defined variables which were used in triggers (or stored functions called from table-updating statements) more than once. This patch addresses the first issue by enabling logging of all references to user-defined variables in triggers/stored functions and not only references from table-updating statements. The second issue stemmed from the fact that for user-defined variables used from triggers or stored functions called from table-updating statements we were writing binlog events for each reference instead of only one event for the first reference. This problem is already solved for stored functions called from non-updating statements with help of "event unioning" mechanism. So the patch simply extends this mechanism to the case affected. It also fixes small problem in this mechanism which caused wrong logging of references to user-variables in cases when non-updating statement called several stored functions which used the same variable and some of these function calls were omitted from binlog as they were not updating any tables. mysql-test/r/rpl_user_variables.result: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch adds the correct results for execution of the added test procedures to the rpl_user_variables test. mysql-test/t/rpl_user_variables.test: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch adds additional tests to the rpl_user_variables test that test many of the different ways user-defined variables can be required to be replicated. sql/item_func.cc: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. To properly log accesses to user-defined variables from stored functions/triggers, the get_var_with_binlog() method needs to log references to such variables even from non-table-updating statements within them. sql/log.cc: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch modifies the start_union_events method to accept the query id from a parameter. This allows callers to set the query_id to the id of the sub statement such as a trigger or stored function. Which permits the code to identify when a user defined variable has been used by the statement and this already present in THD::user_var_event. Note: The changes to sql_class.cc, sp_head.cc, and log.cc are designed to allow the proper replication of access to user-defined variables under a special test case (the last case shown in rpl_user_variables.test). sql/sp_head.cc: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch modifies the code to allow for cases where events for function calls have a separate union for each event and thus cannot use the query_id of the caller as the start of the union. Thus, we use an artifically created query_id to set the start of the events. Note: The changes to sql_class.cc, sp_head.cc, and log.cc are designed to allow the proper replication of access to user-defined variables under a special test case (the last case shown in rpl_user_variables.test). sql/sql_class.cc: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch adds the query_id parameter to the calls to mysql_bin_log.start_union_events(). Note: The changes to sql_class.cc, sp_head.cc, and log.cc are designed to allow the proper replication of access to user-defined variables under a special test case (the last case shown in rpl_user_variables.test). sql/sql_class.h: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch adds the query_id parameter to the calls to mysql_bin_log.start_union_events(). --- mysql-test/r/rpl_user_variables.result | 176 ++++++++++++++ mysql-test/t/rpl_user_variables.test | 303 ++++++++++++++++++++++++- sql/item_func.cc | 9 +- sql/log.cc | 4 +- sql/sp_head.cc | 18 +- sql/sql_class.cc | 7 + sql/sql_class.h | 2 +- 7 files changed, 511 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/rpl_user_variables.result b/mysql-test/r/rpl_user_variables.result index 45618688a33..05f2b68042e 100644 --- a/mysql-test/r/rpl_user_variables.result +++ b/mysql-test/r/rpl_user_variables.result @@ -108,4 +108,180 @@ slave-bin.000001 # User var 2 # @`a`=NULL slave-bin.000001 # Query 1 # use `test`; insert into t1 values (@a),(@a),(@a*5) insert into t1 select * FROM (select @var1 union select @var2) AS t2; drop table t1; +End of 4.1 tests. +DROP TABLE IF EXISTS t20; +DROP TABLE IF EXISTS t21; +DROP PROCEDURE IF EXISTS test.insert; +CREATE TABLE t20 (a VARCHAR(20)); +CREATE TABLE t21 (a VARCHAR(20)); +CREATE PROCEDURE test.insert() +BEGIN +IF (@VAR) +THEN +INSERT INTO test.t20 VALUES ('SP_TRUE'); +ELSE +INSERT INTO test.t20 VALUES ('SP_FALSE'); +END IF; +END| +CREATE TRIGGER test.insert_bi BEFORE INSERT +ON test.t20 FOR EACH ROW +BEGIN +IF (@VAR) +THEN +INSERT INTO test.t21 VALUES ('TRIG_TRUE'); +ELSE +INSERT INTO test.t21 VALUES ('TRIG_FALSE'); +END IF; +END| +SET @VAR=0; +CALL test.insert(); +SET @VAR=1; +CALL test.insert(); +On master: Check the tables for correct data +SELECT * FROM t20; +a +SP_FALSE +SP_TRUE +SELECT * FROM t21; +a +TRIG_FALSE +TRIG_TRUE +On slave: Check the tables for correct data and it matches master +SELECT * FROM t20; +a +SP_FALSE +SP_TRUE +SELECT * FROM t21; +a +TRIG_FALSE +TRIG_TRUE +DROP TABLE t20; +DROP TABLE t21; +DROP PROCEDURE test.insert; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS test.square; +CREATE TABLE t1 (i INT); +CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN (@var * @var); +SET @var = 1; +INSERT INTO t1 VALUES (square()); +SET @var = 2; +INSERT INTO t1 VALUES (square()); +SET @var = 3; +INSERT INTO t1 VALUES (square()); +SET @var = 4; +INSERT INTO t1 VALUES (square()); +SET @var = 5; +INSERT INTO t1 VALUES (square()); +On master: Retrieve the values from the table +SELECT * FROM t1; +i +1 +4 +9 +16 +25 +On slave: Retrieve the values from the table and verify they are the same as on master +SELECT * FROM t1; +i +1 +4 +9 +16 +25 +DROP TABLE t1; +DROP FUNCTION test.square; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +CREATE TABLE t1(a int); +CREATE FUNCTION f1() returns int deterministic +BEGIN +return @a; +END | +CREATE FUNCTION f2() returns int deterministic +BEGIN +IF (@b > 0) then +SET @c = (@a + @b); +else +SET @c = (@a - 1); +END if; +return @c; +END | +SET @a=500; +INSERT INTO t1 values(f1()); +SET @b = 125; +SET @c = 1; +INSERT INTO t1 values(f2()); +On master: Retrieve the values from the table +SELECT * from t1; +a +500 +625 +On slave: Check the tables for correct data and it matches master +SELECT * from t1; +a +500 +625 +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +CREATE TABLE t1 (i int); +CREATE TABLE t2 (k int); +CREATE trigger t1_bi before INSERT on t1 for each row +BEGIN +INSERT INTO t2 values (@a); +SET @a:=42; +INSERT INTO t2 values (@a); +END | +SET @a:=100; +INSERT INTO t1 values (5); +On master: Check to see that data was inserted correctly in both tables +SELECT * from t1; +i +5 +SELECT * from t2; +k +100 +42 +On slave: Check the tables for correct data and it matches master +SELECT * from t1; +i +5 +SELECT * from t2; +k +100 +42 +End of 5.0 tests. +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +CREATE TABLE t1 (i INT); +CREATE FUNCTION f1() RETURNS INT RETURN @a; +CREATE FUNCTION f2() RETURNS INT +BEGIN +INSERT INTO t1 VALUES (10 + @a); +RETURN 0; +END| +SET @a:=123; +SELECT f1(), f2(); +f1() f2() +123 0 +On master: Check to see that data was inserted correctly +INSERT INTO t1 VALUES(f1()); +SELECT * FROM t1; +i +133 +123 +On slave: Check the table for correct data and it matches master +SELECT * FROM t1; +i +133 +123 +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1; stop slave; diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test index dbe75a15038..b2f4bfbb29d 100644 --- a/mysql-test/t/rpl_user_variables.test +++ b/mysql-test/t/rpl_user_variables.test @@ -57,8 +57,305 @@ insert into t1 select * FROM (select @var1 union select @var2) AS t2; drop table t1; save_master_pos; -connection slave; -sync_with_master; +--echo End of 4.1 tests. + +# BUG#20141 +# The following tests ensure that if user-defined variables are used in SF/Triggers +# that they are replicated correctly. These tests should be run in both SBR and RBR +# modes. + +# This test uses a procedure that inserts data values based on the value of a +# user-defined variable. It also has a trigger that inserts data based on the +# same variable. Successful test runs show that the @var is replicated +# properly and that the procedure and trigger insert the correct data on the +# slave. +# +# The test of stored procedure was included for completeness. Replication of stored +# procedures was not directly affected by BUG#20141. +# +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t20; +DROP TABLE IF EXISTS t21; +DROP PROCEDURE IF EXISTS test.insert; +--enable_warnings + +CREATE TABLE t20 (a VARCHAR(20)); +CREATE TABLE t21 (a VARCHAR(20)); +DELIMITER |; + +# Create a procedure that uses the @var for flow control + +CREATE PROCEDURE test.insert() +BEGIN + IF (@VAR) + THEN + INSERT INTO test.t20 VALUES ('SP_TRUE'); + ELSE + INSERT INTO test.t20 VALUES ('SP_FALSE'); + END IF; +END| + +# Create a trigger that uses the @var for flow control + +CREATE TRIGGER test.insert_bi BEFORE INSERT + ON test.t20 FOR EACH ROW + BEGIN + IF (@VAR) + THEN + INSERT INTO test.t21 VALUES ('TRIG_TRUE'); + ELSE + INSERT INTO test.t21 VALUES ('TRIG_FALSE'); + END IF; + END| +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set @var and call the procedure, repeat with different values + +SET @VAR=0; +CALL test.insert(); +SET @VAR=1; +CALL test.insert(); + +--echo On master: Check the tables for correct data + +SELECT * FROM t20; +SELECT * FROM t21; + +sync_slave_with_master; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * FROM t20; +SELECT * FROM t21; +connection master; + +# Cleanup + +DROP TABLE t20; +DROP TABLE t21; +DROP PROCEDURE test.insert; + +# This test uses a stored function that uses user-defined variables to return data +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS test.square; +--enable_warnings + +CREATE TABLE t1 (i INT); + +# Create function that returns a value from @var. In this case, the square function + +CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN (@var * @var); + +sync_slave_with_master; +connection master; + +# Set the @var to different values and insert them into a table + +SET @var = 1; +INSERT INTO t1 VALUES (square()); +SET @var = 2; +INSERT INTO t1 VALUES (square()); +SET @var = 3; +INSERT INTO t1 VALUES (square()); +SET @var = 4; +INSERT INTO t1 VALUES (square()); +SET @var = 5; +INSERT INTO t1 VALUES (square()); + +--echo On master: Retrieve the values from the table + +SELECT * FROM t1; + +sync_slave_with_master; + +--echo On slave: Retrieve the values from the table and verify they are the same as on master + +SELECT * FROM t1; + +connection master; + +# Cleanup + +DROP TABLE t1; +DROP FUNCTION test.square; + +# This test uses stored functions that uses user-defined variables to return data +# based on the use of @vars inside a function body. +# This test was constructed for BUG#14914 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +--enable_warnings + +CREATE TABLE t1(a int); +DELIMITER |; + +# Create a function that simply returns the value of an @var. +# Create a function that uses an @var for flow control, creates and uses another +# @var and sets its value to a value based on another @var. + +CREATE FUNCTION f1() returns int deterministic +BEGIN + return @a; +END | + +CREATE FUNCTION f2() returns int deterministic +BEGIN + IF (@b > 0) then + SET @c = (@a + @b); + else + SET @c = (@a - 1); + END if; + return @c; +END | +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set an @var to a value and insert data into a table using the first function. +# Set two more @vars to some values and insert data into a table using the second function. + +SET @a=500; +INSERT INTO t1 values(f1()); +SET @b = 125; +SET @c = 1; +INSERT INTO t1 values(f2()); + +sync_slave_with_master; + +--echo On master: Retrieve the values from the table + +SELECT * from t1; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * from t1; + +connection master; + +# Cleanup + +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; + +# This test uses a function that changes a user-defined variable in its body. This test +# will ensure the @vars are replicated when needed and not interrupt the normal execution +# of the function on the slave. This also applies to triggers. +# +# This test was constructed for BUG#25167 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +--enable_warnings +CREATE TABLE t1 (i int); +CREATE TABLE t2 (k int); +DELIMITER |; + +# Create a trigger that inserts data into another table, changes the @var then inserts +# another row with the modified value. + +CREATE trigger t1_bi before INSERT on t1 for each row +BEGIN + INSERT INTO t2 values (@a); + SET @a:=42; + INSERT INTO t2 values (@a); +END | +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set the @var to a value then insert data into first table. + +SET @a:=100; +INSERT INTO t1 values (5); + +--echo On master: Check to see that data was inserted correctly in both tables + +SELECT * from t1; +SELECT * from t2; + +sync_slave_with_master; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * from t1; +SELECT * from t2; + +connection master; + +--echo End of 5.0 tests. + +# Cleanup + +DROP TABLE t1; +DROP TABLE t2; + +# This test uses a stored function that uses user-defined variables to return data +# The test ensures the value of the user-defined variable is replicated correctly +# and in the correct order of assignment. +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +--enable_warnings + +CREATE TABLE t1 (i INT); + +# Create two functions. One simply returns the user-defined variable. The other +# returns a value based on the user-defined variable. + +CREATE FUNCTION f1() RETURNS INT RETURN @a; +DELIMITER |; +CREATE FUNCTION f2() RETURNS INT +BEGIN + INSERT INTO t1 VALUES (10 + @a); + RETURN 0; +END| +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set the variable and execute the functions. + +SET @a:=123; +SELECT f1(), f2(); + +--echo On master: Check to see that data was inserted correctly + +INSERT INTO t1 VALUES(f1()); +SELECT * FROM t1; + +sync_slave_with_master; + +--echo On slave: Check the table for correct data and it matches master + +SELECT * FROM t1; + +connection master; + +# Cleanup + +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1; + +sync_slave_with_master; stop slave; -# End of 4.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 1ef77208469..99640e9185c 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4223,7 +4223,14 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, user_var_entry *var_entry; var_entry= get_variable(&thd->user_vars, name, 0); - if (!(opt_bin_log && is_update_query(sql_command))) + /* + Any reference to user-defined variable which is done from stored + function or trigger affects their execution and the execution of the + calling statement. We must log all such variables even if they are + not involved in table-updating statements. + */ + if (!(opt_bin_log && + (is_update_query(sql_command) || thd->in_sub_stmt))) { *out_entry= var_entry; return 0; diff --git a/sql/log.cc b/sql/log.cc index 1961a5b6f88..ba46490b100 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1577,13 +1577,13 @@ bool MYSQL_LOG::flush_and_sync() return err; } -void MYSQL_LOG::start_union_events(THD *thd) +void MYSQL_LOG::start_union_events(THD *thd, query_id_t query_id_param) { DBUG_ASSERT(!thd->binlog_evt_union.do_union); thd->binlog_evt_union.do_union= TRUE; thd->binlog_evt_union.unioned_events= FALSE; thd->binlog_evt_union.unioned_events_trans= FALSE; - thd->binlog_evt_union.first_query_id= thd->query_id; + thd->binlog_evt_union.first_query_id= query_id_param; } void MYSQL_LOG::stop_union_events(THD *thd) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index de0edabda3e..d1a39422c7f 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1464,8 +1464,24 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, binlog_save_options= thd->options; if (need_binlog_call) { + query_id_t q; reset_dynamic(&thd->user_var_events); - mysql_bin_log.start_union_events(thd); + /* + In case of artificially constructed events for function calls + we have separate union for each such event and hence can't use + query_id of real calling statement as the start of all these + unions (this will break logic of replication of user-defined + variables). So we use artifical value which is guaranteed to + be greater than all query_id's of all statements belonging + to previous events/unions. + Possible alternative to this is logging of all function invocations + as one select and not resetting THD::user_var_events before + each invocation. + */ + VOID(pthread_mutex_lock(&LOCK_thread_count)); + q= ::query_id; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + mysql_bin_log.start_union_events(thd, q + 1); } /* diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0794d4c797a..8e1a0c5f1e3 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2051,6 +2051,10 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, if (!lex->requires_prelocking() || is_update_query(lex->sql_command)) options&= ~OPTION_BIN_LOG; + + if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)) + mysql_bin_log.start_union_events(this, this->query_id); + /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; @@ -2097,6 +2101,9 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) sent_row_count= backup->sent_row_count; client_capabilities= backup->client_capabilities; + if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)) + mysql_bin_log.stop_union_events(this); + /* The following is added to the old values as we are interested in the total complexity of the query diff --git a/sql/sql_class.h b/sql/sql_class.h index 05034ebd573..05ea9ac215e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -311,7 +311,7 @@ public: bool write(Log_event* event_info); // binary log write bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event); - void start_union_events(THD *thd); + void start_union_events(THD *thd, query_id_t query_id_param); void stop_union_events(THD *thd); bool is_query_in_union(THD *thd, query_id_t query_id_param); From 6f6951d21867a2d71de414d21becf55feb3792b1 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 23 Feb 2007 22:23:54 +0100 Subject: [PATCH 18/46] 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: if we write to IO_CACHE more than can fit into its memory buffer, it will try to overflow into a file; for that to work, IO_CACHE must be inited via open_cached_file(). mysql-test/r/mysqlbinlog_base64.result: result update mysql-test/t/mysqlbinlog_base64.test: test for BUG#25628: test that mysqlbinlog does not have OS errors with big concatenations of row-based events (e.g. Write_rows_log_event), and prints those concatenations entirely (testing by piping the output back into the server and comparing data). mysys/mf_iocache2.c: my_b_copy_to_file() had a problem: it assumed that bytes_in_cache are all the bytes to copy to the file, while it only tells how many bytes are in the buffer; so the code forgot to copy what had already overflown into a temporary file. Thus any big event was printed only partially by mysqlbinlog (loss of data at restore). The fix is inspired by MYSQL_BIN_LOG::write_cache(). sql/log_event.cc: Several Table_map/Write_rows events generated by one single statement get groupped together in mysqlbinlog's output; it printed things like #718 7:30:51 server id 12 end_log_pos 988 Write_rows: table id 17#718 7:30:51 server id 12 #718 7:30:51 server id 12 end_log_pos 988 Write_rows: table id 17#718 7:30:51 server id 12 end_log_pos 1413 It didn't look nice to have printouts glued like this without line breaks. Adding a line break. Doing this, when using --hexdump the result was: #718 7:30:51 server id 12 end_log_pos 988 # # Write_rows: table id 17 which is correct; unfortunately if the hex dump had only full lines (i.e the string to print in hex had its length a multiple of 16), then the # in front of Write_rows was not printed. Fixed. sql/log_event.h: removing strcpy() (one less function call). If we write to IO_CACHE more than can fit into its memory buffer, it will try to overflow into a file; for that to work, IO_CACHE must be inited via open_cached_file(). open_cached_file(), like init_io_cache(), can fail; we make sure to catch this constructor's problem via the init_ok() method. --- 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 */ From f3af12381204863f348856354fe51d66d06bc436 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 24 Feb 2007 12:30:15 +0100 Subject: [PATCH 19/46] log_on variable needed also in embedded library --- sql/sql_insert.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index f4a999f918a..00463b53aa9 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -362,7 +362,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, List_item *values; Name_resolution_context *context; Name_resolution_context_state ctx_state; -#ifndef EMBEDDED_LIBRARY char *query= thd->query; /* log_on is about delayed inserts only. @@ -371,7 +370,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, */ bool log_on= ((thd->options & OPTION_BIN_LOG) || (!(thd->security_ctx->master_access & SUPER_ACL))); -#endif thr_lock_type lock_type = table_list->lock_type; Item *unused_conds= 0; DBUG_ENTER("mysql_insert"); From 13bee852c5e3238cf3670c758b1c79061c140143 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 24 Feb 2007 14:33:45 +0100 Subject: [PATCH 20/46] BUG#26286 (row-based logging scales worse than statement-based logging): Submitting patch on Guilhem's behalf (he found the solution). Correcting a typo that caused very big increases in memory usage when more memory needed to be allocated for row-based events. Also correcting a border case check when more memory needed to be allocated. sql/log_event.cc: Correcting typo that caused very big increases in memory allocation. Correcting border case for when more memory should be allocated. --- sql/log_event.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index a326e226320..ec1dd973f3f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5515,13 +5515,13 @@ int Rows_log_event::do_add_row_data(byte *const row_data, DBUG_ASSERT(m_rows_cur <= m_rows_end); /* The cast will always work since m_rows_cur <= m_rows_end */ - if (static_cast(m_rows_end - m_rows_cur) < length) + if (static_cast(m_rows_end - m_rows_cur) <= length) { my_size_t const block_size= 1024; my_ptrdiff_t const old_alloc= m_rows_end - m_rows_buf; my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf; my_ptrdiff_t const new_alloc= - block_size * ((cur_size + length) / block_size + block_size - 1); + block_size * ((cur_size + length + block_size - 1) / block_size); byte* const new_buf= (byte*)my_realloc((gptr)m_rows_buf, new_alloc, MYF(MY_ALLOW_ZERO_PTR|MY_WME)); @@ -5542,7 +5542,7 @@ int Rows_log_event::do_add_row_data(byte *const row_data, m_rows_end= m_rows_buf + new_alloc; } - DBUG_ASSERT(m_rows_cur + length < m_rows_end); + DBUG_ASSERT(m_rows_cur + length <= m_rows_end); memcpy(m_rows_cur, row_data, length); m_rows_cur+= length; m_row_count++; From 072d62c7b09148beb941896583fc4a73defc138d Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 24 Feb 2007 16:35:15 +0100 Subject: [PATCH 21/46] rpl_insert_delayed.test: fix after merge: server now returns ER_DUP_ENTRY_WITH_KEY_NAME, not ER_DUP_ENTRY mysql-test/extra/rpl_tests/rpl_insert_delayed.test: fix after merge: server now returns ER_DUP_ENTRY_WITH_KEY_NAME, not ER_DUP_ENTRY --- mysql-test/extra/rpl_tests/rpl_insert_delayed.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_insert_delayed.test b/mysql-test/extra/rpl_tests/rpl_insert_delayed.test index 11856953959..2cba7ab64c8 100644 --- a/mysql-test/extra/rpl_tests/rpl_insert_delayed.test +++ b/mysql-test/extra/rpl_tests/rpl_insert_delayed.test @@ -42,7 +42,7 @@ 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 + --error ER_DUP_ENTRY_WITH_KEY_NAME insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); } if (!$binlog_format_statement) @@ -64,7 +64,7 @@ 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 + --error ER_DUP_ENTRY_WITH_KEY_NAME insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); } if (!$binlog_format_statement) From c13e1694adf58aacc7705b696690c781575d9b21 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 24 Feb 2007 17:33:58 +0100 Subject: [PATCH 22/46] Changes to fix building on Windows. include/my_global.h: Using Standard C++ header file instead of defining all the versions of operator new and operator delete ourself. --- include/my_global.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/include/my_global.h b/include/my_global.h index d92e5f07948..acf19fef8c0 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -1479,14 +1479,10 @@ do { doubleget_union _tmp; \ #endif /* - Define placement versions of operator new and operator delete since - we cannot be sure that the include exists. + Include standard definitions of operator new and delete. */ #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 */ } +#include #endif #endif /* my_global_h */ From a74cdb2396f90b0c62b2424142706ba52ab52842 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 24 Feb 2007 19:51:45 +0100 Subject: [PATCH 23/46] Fixes to make it compile on Windows. sql/log_event.cc: The type byte is not equivalent to char on Windows, so compile fails. --- sql/log_event.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 76996d3c2d2..e9c80874c7d 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1141,7 +1141,7 @@ void Log_event::print_header(IO_CACHE* file, need a # to prefix the rest of printouts for example those of Rows_log_event::print_helper(). */ - my_b_write(file, "# ", 2); + my_b_write(file, reinterpret_cast("# "), 2); } DBUG_VOID_RETURN; } From dc8b4bacc5bb76540cb33cfdcaecf916b716fa89 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 25 Feb 2007 00:31:26 +0100 Subject: [PATCH 24/46] log_on needed also in embedded server --- sql/sql_insert.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index cd5f424fc14..613b28faf63 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -381,11 +381,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, List_item *values; Name_resolution_context *context; Name_resolution_context_state ctx_state; -#ifndef EMBEDDED_LIBRARY char *query= thd->query; bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->security_ctx->master_access & SUPER_ACL)); -#endif thr_lock_type lock_type = table_list->lock_type; Item *unused_conds= 0; DBUG_ENTER("mysql_insert"); From cdfc516d46353477ac3ec79aa3474a4520e66e72 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Feb 2007 10:19:08 +0100 Subject: [PATCH 25/46] BUG#25091 (A DELETE statement to mysql database is not logged in ROW format): With this patch, statements that change metadata (in the mysql database) is logged as statements, while normal changes (e.g., using INSERT, DELETE, and/or UPDATE) is logged according to the format in effect. The log tables (i.e., general_log and slow_log) are not replicated at all. With this patch, the following statements are replicated as statements: GRANT, REVOKE (ALL), CREATE USER, DROP USER, and RENAME USER. mysql-test/extra/binlog_tests/binlog.test: Added test to check that normal INSERT, DELETE, and UPDATE to a table in the mysql database is replicated both under row-based and statement-based replication. mysql-test/r/binlog_row_binlog.result: Result change. mysql-test/r/binlog_stm_binlog.result: Result change. sql/handler.cc: Removed hardcoded check for mysql database. Added table-specific flag for non-replication (used by log tables). sql/log.cc: Adding flag that a table shall not be replicated and set it for log tables. sql/sp.cc: Turning row-based replication off for statements that change metadata. sql/sql_acl.cc: Turning row-based replication off for statements that change metadata. sql/table.h: Adding flag that a table shall not be replicated. --- mysql-test/extra/binlog_tests/binlog.test | 13 ++++++ mysql-test/r/binlog_row_binlog.result | 17 +++++++ mysql-test/r/binlog_stm_binlog.result | 11 +++++ sql/handler.cc | 2 +- sql/log.cc | 1 + sql/sp.cc | 21 +++++++++ sql/sql_acl.cc | 56 +++++++++++++++++++++++ sql/table.h | 5 ++ 8 files changed, 125 insertions(+), 1 deletion(-) diff --git a/mysql-test/extra/binlog_tests/binlog.test b/mysql-test/extra/binlog_tests/binlog.test index 48d9b859c26..834edcff474 100644 --- a/mysql-test/extra/binlog_tests/binlog.test +++ b/mysql-test/extra/binlog_tests/binlog.test @@ -67,6 +67,19 @@ create table if not exists t2 select * from t1; create temporary table tt1 (a int); create table if not exists t3 like tt1; +# BUG#25091 (A DELETE statement to mysql database is not logged with +# ROW mode format): Checking that some basic operations on tables in +# the mysql database is replicated even when the current database is +# 'mysql'. + +--disable_warnings +USE mysql; +INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test'); +UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@'; +DELETE FROM user WHERE host='localhost' AND user='@#@'; +--enable_warnings + +use test; --replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; diff --git a/mysql-test/r/binlog_row_binlog.result b/mysql-test/r/binlog_row_binlog.result index aee270bd7f6..769f23ea86c 100644 --- a/mysql-test/r/binlog_row_binlog.result +++ b/mysql-test/r/binlog_row_binlog.result @@ -249,6 +249,11 @@ create table t1 (a int); create table if not exists t2 select * from t1; create temporary table tt1 (a int); create table if not exists t3 like tt1; +USE mysql; +INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test'); +UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@'; +DELETE FROM user WHERE host='localhost' AND user='@#@'; +use test; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_increment primary key) @@ -262,6 +267,12 @@ master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t2` ( master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t3` ( `a` int(11) DEFAULT NULL ) +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Update_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Delete_rows 1 # table_id: # flags: STMT_END_F drop table t1,t2,t3,tt1; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam; set @@session.auto_increment_increment=1, @@session.auto_increment_offset=1; @@ -281,6 +292,12 @@ master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t2` ( master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t3` ( `a` int(11) DEFAULT NULL ) +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Update_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Delete_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `test`; DROP TABLE `t1`,`t2`,`t3` /* generated by server */ master-bin.000001 # Query 1 # use `test`; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam master-bin.000001 # Table_map 1 # table_id: # (test.t1) diff --git a/mysql-test/r/binlog_stm_binlog.result b/mysql-test/r/binlog_stm_binlog.result index a9b3f69ecee..fb0453b5b68 100644 --- a/mysql-test/r/binlog_stm_binlog.result +++ b/mysql-test/r/binlog_stm_binlog.result @@ -159,6 +159,11 @@ create table t1 (a int); create table if not exists t2 select * from t1; create temporary table tt1 (a int); create table if not exists t3 like tt1; +USE mysql; +INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test'); +UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@'; +DELETE FROM user WHERE host='localhost' AND user='@#@'; +use test; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_increment primary key) @@ -169,6 +174,9 @@ master-bin.000001 # Query 1 # use `test`; create table t1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t2 select * from t1 master-bin.000001 # Query 1 # use `test`; create temporary table tt1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t3 like tt1 +master-bin.000001 # Query 1 # use `mysql`; INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test') +master-bin.000001 # Query 1 # use `mysql`; UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@' +master-bin.000001 # Query 1 # use `mysql`; DELETE FROM user WHERE host='localhost' AND user='@#@' drop table t1,t2,t3,tt1; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam; set @@session.auto_increment_increment=1, @@session.auto_increment_offset=1; @@ -185,6 +193,9 @@ master-bin.000001 # Query 1 # use `test`; create table t1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t2 select * from t1 master-bin.000001 # Query 1 # use `test`; create temporary table tt1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t3 like tt1 +master-bin.000001 # Query 1 # use `mysql`; INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test') +master-bin.000001 # Query 1 # use `mysql`; UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@' +master-bin.000001 # Query 1 # use `mysql`; DELETE FROM user WHERE host='localhost' AND user='@#@' master-bin.000001 # Query 1 # use `test`; drop table t1,t2,t3,tt1 master-bin.000001 # Query 1 # use `test`; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam master-bin.000001 # Table_map 1 # table_id: # (test.t1) diff --git a/sql/handler.cc b/sql/handler.cc index 360d528f0ad..24cf5afb5c7 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3481,7 +3481,7 @@ namespace { { int const check(table->s->tmp_table == NO_TMP_TABLE && binlog_filter->db_ok(table->s->db.str) && - strcmp("mysql", table->s->db.str) != 0); + !table->no_replicate); table->s->cached_row_logging_check= check; } diff --git a/sql/log.cc b/sql/log.cc index 1b432ca15c0..99431304b96 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -312,6 +312,7 @@ bool Log_to_csv_event_handler::open_log_table(uint log_table_type) { table->table->use_all_columns(); table->table->locked_by_logger= TRUE; + table->table->no_replicate= TRUE; } /* restore thread settings */ if (curr) diff --git a/sql/sp.cc b/sql/sp.cc index 14703e3aa42..b2bb1654176 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -500,6 +500,13 @@ db_create_routine(THD *thd, int type, sp_head *sp) DBUG_PRINT("enter", ("type: %d name: %.*s",type,sp->m_name.length, sp->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) ret= SP_OPEN_TABLE_FAILED; else @@ -636,6 +643,13 @@ db_drop_routine(THD *thd, int type, sp_name *name) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) @@ -668,6 +682,13 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 0d9653172e0..bec632889c1 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3001,6 +3001,13 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -3218,6 +3225,13 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -3357,6 +3371,13 @@ bool mysql_grant(THD *thd, const char *db, List &list, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -5399,6 +5420,13 @@ bool mysql_create_user(THD *thd, List &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_create_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* CREATE USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5471,6 +5499,13 @@ bool mysql_drop_user(THD *thd, List &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_drop_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* DROP USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5535,6 +5570,13 @@ bool mysql_rename_user(THD *thd, List &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_rename_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* RENAME USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5610,6 +5652,13 @@ bool mysql_revoke_all(THD *thd, List &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_revoke_all"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5800,6 +5849,13 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, rw_wrlock(&LOCK_grant); VOID(pthread_mutex_lock(&acl_cache->lock)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* Remove procedure access */ do { diff --git a/sql/table.h b/sql/table.h index 80add0e0b91..7fd2b405073 100644 --- a/sql/table.h +++ b/sql/table.h @@ -389,6 +389,10 @@ struct st_table { /* If true, the current table row is considered to have all columns set to NULL, including columns declared as "not null" (see maybe_null). + + TODO: Each of these flags take up 8 bits. They can just as easily + be put into one single unsigned long and instead of taking up 18 + bytes, it would take up 4. */ my_bool null_row; my_bool force_index; @@ -396,6 +400,7 @@ struct st_table { my_bool key_read, no_keyread; my_bool locked_by_flush; my_bool locked_by_logger; + my_bool no_replicate; my_bool locked_by_name; my_bool fulltext_searched; my_bool no_cache; From 7d883c0defe80a210a4b3cb2a9d1b2195497f5b8 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Feb 2007 15:25:43 +0400 Subject: [PATCH 26/46] Bug#24478 DROP TRIGGER is not caught by replicate-*-table filters Problem: DROP TRIGGER was not properly handled in combination with slave filters, which made replication stop Fix: loading table name before checking slave filters when dropping a trigger. mysql-test/r/rpl_replicate_do.result: Adding test case mysql-test/t/rpl_replicate_do.test: Adding test case sql/sql_parse.cc: Loading table name when dropping a trigger before checking slave filtering rules. sql/sql_trigger.cc: Making add_table_for_trigger() public sql/sql_trigger.h: Making add_table_for_trigger() public --- mysql-test/r/rpl_replicate_do.result | 34 ++++++++++++++++++++++++++++ mysql-test/t/rpl_replicate_do.test | 32 ++++++++++++++++++++++++++ sql/sql_parse.cc | 25 ++++++++++++++++++++ sql/sql_trigger.cc | 6 +---- sql/sql_trigger.h | 4 ++++ 5 files changed, 96 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/rpl_replicate_do.result b/mysql-test/r/rpl_replicate_do.result index f79f6305342..9bacffb0609 100644 --- a/mysql-test/r/rpl_replicate_do.result +++ b/mysql-test/r/rpl_replicate_do.result @@ -41,3 +41,37 @@ select * from t1; ts 2005-08-12 00:00:00 drop table t1; +*** master *** +create table t1 (a int, b int); +create trigger trg1 before insert on t1 for each row set new.b=2; +create table t2 (a int, b int); +create trigger trg2 before insert on t2 for each row set new.b=2; +show tables; +Tables_in_test +t1 +t2 +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +trg1 INSERT t1 set new.b=2 BEFORE NULL root@localhost +trg2 INSERT t2 set new.b=2 BEFORE NULL root@localhost +*** slave *** +show tables; +Tables_in_test +t1 +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +trg1 INSERT t1 set new.b=2 BEFORE NULL root@localhost +*** master *** +drop trigger trg1; +drop trigger trg2; +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +*** slave *** +show tables; +Tables_in_test +t1 +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +*** master *** +drop table t1; +drop table t2; diff --git a/mysql-test/t/rpl_replicate_do.test b/mysql-test/t/rpl_replicate_do.test index 9dec8c06c79..0e95d71514b 100644 --- a/mysql-test/t/rpl_replicate_do.test +++ b/mysql-test/t/rpl_replicate_do.test @@ -59,3 +59,35 @@ drop table t1; sync_slave_with_master; # End of 4.1 tests + +# +# Bug#24478 DROP TRIGGER is not caught by replicate-*-table filters +# +--echo *** master *** +connection master; +create table t1 (a int, b int); +create trigger trg1 before insert on t1 for each row set new.b=2; +create table t2 (a int, b int); +create trigger trg2 before insert on t2 for each row set new.b=2; +show tables; +show triggers; +sync_slave_with_master; +--echo *** slave *** +connection slave; +show tables; +show triggers; +--echo *** master *** +connection master; +drop trigger trg1; +drop trigger trg2; +show triggers; +sync_slave_with_master; +--echo *** slave *** +connection slave; +show tables; +show triggers; +--echo *** master *** +connection master; +drop table t1; +drop table t2; +sync_slave_with_master; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b0b7afc6528..7226a8e3c86 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -33,6 +33,7 @@ #include "sp_head.h" #include "sp.h" #include "sp_cache.h" +#include "sql_trigger.h" #ifdef HAVE_OPENSSL /* @@ -2485,6 +2486,30 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION if (unlikely(thd->slave_thread)) { + if (lex->sql_command == SQLCOM_DROP_TRIGGER) + { + /* + When dropping a trigger, we need to load its table name + before checking slave filter rules. + */ + add_table_for_trigger(thd, thd->lex->spname, 1, &all_tables); + + if (!all_tables) + { + /* + If table name cannot be loaded, + it means the trigger does not exists possibly because + CREATE TRIGGER was previously skipped for this trigger + according to slave filtering rules. + Returning success without producing any errors in this case. + */ + DBUG_RETURN(0); + } + + // force searching in slave.cc:tables_ok() + all_tables->updating= 1; + } + /* Check if statment should be skipped because of slave filtering rules diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 3569733d064..0b648570b86 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -107,10 +107,6 @@ const LEX_STRING trg_event_type_names[]= }; -static int -add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, - TABLE_LIST ** table); - class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook { private: @@ -1183,7 +1179,7 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, 1 Error */ -static int +int add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, TABLE_LIST **table) { diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 7e0fadfa677..8970986b931 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -139,3 +139,7 @@ private: extern const LEX_STRING trg_action_time_type_names[]; extern const LEX_STRING trg_event_type_names[]; + +int +add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, + TABLE_LIST **table); From 236bb807330446e23733a895b937073cf325fb7e Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Feb 2007 17:44:55 +0100 Subject: [PATCH 27/46] BUG#26634 (Valgrind failure in tree: memory loss for memory allocated in rpl_utility.h): Adding code to release allocated memory when tables_to_lock list is cleared. sql/log_event.cc: Using RPL_TABLE_LIST instead of TABLE_LIST for tables_to_lock. sql/rpl_rli.cc: Moving st_relay_log_info::clear_tables_to_lock() into rpl_rli.cc. Adding code to release memory allocated for saved table definition. sql/rpl_rli.h: Moving st_relay_log_info::clear_tables_to_lock() into rpl_rli.cc. Using RPL_TABLE_LIST instead of TABLE_LIST for tables_to_lock. sql/rpl_utility.h: Adding forward declarations. Adding boolean to tell if tabledef is valid. --- sql/log_event.cc | 14 ++++++++++---- sql/rpl_rli.cc | 20 ++++++++++++++++++++ sql/rpl_rli.h | 15 ++++----------- sql/rpl_utility.h | 4 ++++ 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 8b855409122..1fbe6c3251e 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5783,10 +5783,10 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) need to add code to assert that is the case. */ thd->binlog_flush_pending_rows_event(false); - close_tables_for_reopen(thd, &rli->tables_to_lock); + TABLE_LIST *tables= rli->tables_to_lock; + close_tables_for_reopen(thd, &tables); - if ((error= open_tables(thd, &rli->tables_to_lock, - &rli->tables_to_lock_count, 0))) + if ((error= open_tables(thd, &tables, &rli->tables_to_lock_count, 0))) { if (thd->query_error || thd->is_fatal_error) { @@ -5815,7 +5815,7 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) */ { - RPL_TABLE_LIST *ptr= static_cast(rli->tables_to_lock); + RPL_TABLE_LIST *ptr= rli->tables_to_lock; for ( ; ptr ; ptr= static_cast(ptr->next_global)) { if (ptr->m_tabledef.compatible_with(rli, ptr->table)) @@ -6396,9 +6396,15 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) /* Use placement new to construct the table_def instance in the memory allocated for it inside table_list. + + The memory allocated by the table_def structure (i.e., not the + memory allocated *for* the table_def structure) is released + inside st_relay_log_info::clear_tables_to_lock() by calling the + table_def destructor explicitly. */ const table_def *const def= new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt); + table_list->m_tabledef_valid= TRUE; /* We record in the slave's information that the table should be diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 6a7b22bf23d..8a051195dba 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -18,6 +18,7 @@ #include "rpl_rli.h" #include // For MY_STAT #include "sql_repl.h" // For check_binlog_magic +#include "rpl_utility.h" static int count_relay_log_space(RELAY_LOG_INFO* rli); @@ -1108,4 +1109,23 @@ void st_relay_log_info::cleanup_context(THD *thd, bool error) unsafe_to_stop_at= 0; DBUG_VOID_RETURN; } + +void st_relay_log_info::clear_tables_to_lock() +{ + while (tables_to_lock) + { + gptr to_free= reinterpret_cast(tables_to_lock); + if (tables_to_lock->m_tabledef_valid) + { + tables_to_lock->m_tabledef.table_def::~table_def(); + tables_to_lock->m_tabledef_valid= FALSE; + } + tables_to_lock= + static_cast(tables_to_lock->next_global); + tables_to_lock_count--; + my_free(to_free, MYF(MY_WME)); + } + DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0); +} + #endif diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index cb9894a2125..45c9fb1cf96 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -20,6 +20,8 @@ #include "rpl_tblmap.h" +struct RPL_TABLE_LIST; + /**************************************************************************** @@ -279,7 +281,7 @@ typedef struct st_relay_log_info group_relay_log_pos); } - TABLE_LIST *tables_to_lock; /* RBR: Tables to lock */ + RPL_TABLE_LIST *tables_to_lock; /* RBR: Tables to lock */ uint tables_to_lock_count; /* RBR: Count of tables to lock */ table_mapping m_table_map; /* RBR: Mapping table-id to table */ @@ -295,16 +297,7 @@ typedef struct st_relay_log_info void transaction_end(THD*); void cleanup_context(THD *, bool); - void clear_tables_to_lock() { - while (tables_to_lock) - { - char *to_free= reinterpret_cast(tables_to_lock); - tables_to_lock= tables_to_lock->next_global; - tables_to_lock_count--; - my_free(to_free, MYF(MY_WME)); - } - DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0); - } + void clear_tables_to_lock(); time_t unsafe_to_stop_at; } RELAY_LOG_INFO; diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index 8694b5724f9..b1aa642619c 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -22,6 +22,9 @@ #include "mysql_priv.h" +struct st_relay_log_info; +typedef st_relay_log_info RELAY_LOG_INFO; + uint32 field_length_from_packed(enum_field_types field_type, byte const *data); @@ -128,6 +131,7 @@ private: struct RPL_TABLE_LIST : public st_table_list { + bool m_tabledef_valid; table_def m_tabledef; }; From c140b92cfb1de9d0e9374bef96dc2d24ffb6ae8a Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Feb 2007 18:05:08 +0100 Subject: [PATCH 28/46] Eliminating compiler warnings in the sql/ directory. sql/log.cc: Eliminating some warning from incompatible arguments to DBUG_PRINT(). sql/log_event.cc: Eliminating unused auto variable. sql/sql_insert.cc: Eliminating some warning from incompatible arguments to DBUG_PRINT(). --- sql/log.cc | 10 +++++----- sql/log_event.cc | 3 +-- sql/sql_insert.cc | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/sql/log.cc b/sql/log.cc index ca579a486d6..15cbff16339 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -138,8 +138,8 @@ public: */ void truncate(my_off_t pos) { - DBUG_PRINT("info", ("truncating to position %ld", pos)); - DBUG_PRINT("info", ("before_stmt_pos=%lu", (void*) pos)); + DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos)); + DBUG_PRINT("info", ("before_stmt_pos=%lu", (ulong) pos)); delete pending(); set_pending(0); reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0); @@ -3466,10 +3466,10 @@ 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", (void*) trx_data)); + DBUG_PRINT("enter", ("trx_data=0x%lu", (ulong) trx_data)); if (trx_data) - DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%d", - trx_data->before_stmt_pos)); + DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%lu", + (ulong) trx_data->before_stmt_pos)); /* Write the transaction cache to the binary log. We don't flush and diff --git a/sql/log_event.cc b/sql/log_event.cc index 1fbe6c3251e..bf19c552432 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -6402,8 +6402,7 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) inside st_relay_log_info::clear_tables_to_lock() by calling the table_def destructor explicitly. */ - const table_def *const def= - new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt); + new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt); table_list->m_tabledef_valid= TRUE; /* diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 612c5790e57..6dfa9b85121 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3189,7 +3189,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", - (void*) table, + (ulong) table, table && !table->s->tmp_table ? "is NOT" : "is")); DBUG_PRINT("info", ("Table %s prior to executing this statement", From 9f957f1429df804b35eafe468e20b219e8070d91 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Feb 2007 14:06:10 -0500 Subject: [PATCH 29/46] BUG#20141 "User-defined variables are not replicated properly for SF/Triggers in SBR mode." BUG#14914 "SP: Uses of session variables in routines are not always replicated" BUG#25167 "Dupl. usage of user-variables in trigger/function is not replicated correctly" User-defined variables used inside of stored functions/triggers in statements which did not update tables directly were not replicated. We also had problems with replication of user-defined variables which were used in triggers (or stored functions called from table-updating statements) more than once. This patch addresses the first issue by enabling logging of all references to user-defined variables in triggers/stored functions and not only references from table-updating statements. The second issue stemmed from the fact that for user-defined variables used from triggers or stored functions called from table-updating statements we were writing binlog events for each reference instead of only one event for the first reference. This problem is already solved for stored functions called from non-updating statements with help of "event unioning" mechanism. So the patch simply extends this mechanism to the case affected. It also fixes small problem in this mechanism which caused wrong logging of references to user-variables in cases when non-updating statement called several stored functions which used the same variable and some of these function calls were omitted from binlog as they were not updating any tables. mysql-test/r/rpl_user_variables.result: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch adds the correct results for execution of the added test procedures to the rpl_user_variables test. mysql-test/t/rpl_user_variables.test: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch adds additional tests to the rpl_user_variables test that test many of the different ways user-defined variables can be required to be replicated. sql/item_func.cc: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. To properly log accesses to user-defined variables from stored functions/triggers, the get_var_with_binlog() method needs to log references to such variables even from non-table-updating statements within them. sql/log.cc: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch modifies the start_union_events method to accept the query id from a parameter. This allows callers to set the query_id to the id of the sub statement such as a trigger or stored function. Which permits the code to identify when a user defined variable has been used by the statement and this already present in THD::user_var_event. Note: The changes to sql_class.cc, sp_head.cc, and log.cc are designed to allow the proper replication of access to user-defined variables under a special test case (the last case shown in rpl_user_variables.test). sql/log.h: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch adds the query_id parameter to the calls to mysql_bin_log.start_union_events(). sql/sp_head.cc: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch modifies the code to allow for cases where events for function calls have a separate union for each event and thus cannot use the query_id of the caller as the start of the union. Thus, we use an artifically created query_id to set the start of the events. Note: The changes to sql_class.cc, sp_head.cc, and log.cc are designed to allow the proper replication of access to user-defined variables under a special test case (the last case shown in rpl_user_variables.test). sql/sql_class.cc: BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode. This patch adds the query_id parameter to the calls to mysql_bin_log.start_union_events(). Note: The changes to sql_class.cc, sp_head.cc, and log.cc are designed to allow the proper replication of access to user-defined variables under a special test case (the last case shown in rpl_user_variables.test). --- mysql-test/r/rpl_user_variables.result | 174 +++++++++++++++ mysql-test/t/rpl_user_variables.test | 297 +++++++++++++++++++++++++ sql/item_func.cc | 9 +- sql/log.cc | 4 +- sql/log.h | 2 +- sql/sp_head.cc | 18 +- sql/sql_class.cc | 9 + 7 files changed, 508 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/rpl_user_variables.result b/mysql-test/r/rpl_user_variables.result index ed0d2782394..3b6002a5d2d 100644 --- a/mysql-test/r/rpl_user_variables.result +++ b/mysql-test/r/rpl_user_variables.result @@ -80,4 +80,178 @@ abc\def This is a test insert into t1 select * FROM (select @var1 union select @var2) AS t2; drop table t1; +End of 4.1 tests. +DROP TABLE IF EXISTS t20; +DROP TABLE IF EXISTS t21; +DROP PROCEDURE IF EXISTS test.insert; +CREATE TABLE t20 (a VARCHAR(20)); +CREATE TABLE t21 (a VARCHAR(20)); +CREATE PROCEDURE test.insert() +BEGIN +IF (@VAR) +THEN +INSERT INTO test.t20 VALUES ('SP_TRUE'); +ELSE +INSERT INTO test.t20 VALUES ('SP_FALSE'); +END IF; +END| +CREATE TRIGGER test.insert_bi BEFORE INSERT +ON test.t20 FOR EACH ROW +BEGIN +IF (@VAR) +THEN +INSERT INTO test.t21 VALUES ('TRIG_TRUE'); +ELSE +INSERT INTO test.t21 VALUES ('TRIG_FALSE'); +END IF; +END| +SET @VAR=0; +CALL test.insert(); +SET @VAR=1; +CALL test.insert(); +On master: Check the tables for correct data +SELECT * FROM t20; +a +SP_FALSE +SP_TRUE +SELECT * FROM t21; +a +TRIG_FALSE +TRIG_TRUE +On slave: Check the tables for correct data and it matches master +SELECT * FROM t20; +a +SP_FALSE +SP_TRUE +SELECT * FROM t21; +a +TRIG_FALSE +TRIG_TRUE +DROP TABLE t20; +DROP TABLE t21; +DROP PROCEDURE test.insert; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS test.square; +CREATE TABLE t1 (i INT); +CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN +(@var * @var); +SET @var = 1; +INSERT INTO t1 VALUES (square()); +SET @var = 2; +INSERT INTO t1 VALUES (square()); +SET @var = 3; +INSERT INTO t1 VALUES (square()); +SET @var = 4; +INSERT INTO t1 VALUES (square()); +SET @var = 5; +INSERT INTO t1 VALUES (square()); +On master: Retrieve the values from the table +SELECT * FROM t1; +i +1 +4 +9 +16 +25 +On slave: Retrieve the values from the table and verify they are the same as on master +SELECT * FROM t1; +i +1 +4 +9 +16 +25 +DROP TABLE t1; +DROP FUNCTION test.square; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +CREATE TABLE t1(a int); +CREATE FUNCTION f1() returns int deterministic BEGIN +return @a; +END | +CREATE FUNCTION f2() returns int deterministic BEGIN +IF (@b > 0) then +SET @c = (@a + @b); +else +SET @c = (@a - 1); +END if; +return @c; +END | +SET @a=500; +INSERT INTO t1 values(f1()); +SET @b = 125; +SET @c = 1; +INSERT INTO t1 values(f2()); +On master: Retrieve the values from the table +SELECT * from t1; +a +500 +625 +On slave: Check the tables for correct data and it matches master +SELECT * from t1; +a +500 +625 +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +CREATE TABLE t1 (i int); +CREATE TABLE t2 (k int); +CREATE trigger t1_bi before INSERT on t1 for each row BEGIN +INSERT INTO t2 values (@a); +SET @a:=42; +INSERT INTO t2 values (@a); +END | +SET @a:=100; +INSERT INTO t1 values (5); +On master: Check to see that data was inserted correctly in both tables +SELECT * from t1; +i +5 +SELECT * from t2; +k +100 +42 +On slave: Check the tables for correct data and it matches master +SELECT * from t1; +i +5 +SELECT * from t2; +k +100 +42 +End of 5.0 tests. +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +CREATE TABLE t1 (i INT); +CREATE FUNCTION f1() RETURNS INT RETURN @a; +CREATE +FUNCTION f2() RETURNS INT BEGIN +INSERT INTO t1 VALUES (10 + @a); +RETURN 0; +END| +SET @a:=123; +SELECT f1(), f2(); +f1() f2() +123 0 +On master: Check to see that data was inserted correctly +INSERT INTO t1 VALUES(f1()); +SELECT * FROM t1; +i +133 +123 +On slave: Check the table for correct data and it matches master +SELECT * FROM t1; +i +133 +123 +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1; stop slave; diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test index 08717fce114..226591bd13f 100644 --- a/mysql-test/t/rpl_user_variables.test +++ b/mysql-test/t/rpl_user_variables.test @@ -53,5 +53,302 @@ SELECT * FROM t1 ORDER BY n; connection master; insert into t1 select * FROM (select @var1 union select @var2) AS t2; drop table t1; +--echo End of 4.1 tests. + +# BUG#20141 +# The following tests ensure that if user-defined variables are used in SF/Triggers +# that they are replicated correctly. These tests should be run in both SBR and RBR +# modes. + +# This test uses a procedure that inserts data values based on the value of a +# user-defined variable. It also has a trigger that inserts data based on the +# same variable. Successful test runs show that the @var is replicated +# properly and that the procedure and trigger insert the correct data on the +# slave. +# +# The test of stored procedure was included for completeness. Replication of stored +# procedures was not directly affected by BUG#20141. +# +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t20; +DROP TABLE IF EXISTS t21; +DROP PROCEDURE IF EXISTS test.insert; +--enable_warnings + +CREATE TABLE t20 (a VARCHAR(20)); +CREATE TABLE t21 (a VARCHAR(20)); +DELIMITER |; + +# Create a procedure that uses the @var for flow control + +CREATE PROCEDURE test.insert() +BEGIN + IF (@VAR) + THEN + INSERT INTO test.t20 VALUES ('SP_TRUE'); + ELSE + INSERT INTO test.t20 VALUES ('SP_FALSE'); + END IF; +END| + +# Create a trigger that uses the @var for flow control + +CREATE TRIGGER test.insert_bi BEFORE INSERT + ON test.t20 FOR EACH ROW + BEGIN + IF (@VAR) + THEN + INSERT INTO test.t21 VALUES ('TRIG_TRUE'); + ELSE + INSERT INTO test.t21 VALUES ('TRIG_FALSE'); + END IF; + END| +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set @var and call the procedure, repeat with different values + +SET @VAR=0; +CALL test.insert(); +SET @VAR=1; +CALL test.insert(); + +--echo On master: Check the tables for correct data + +SELECT * FROM t20; +SELECT * FROM t21; + +sync_slave_with_master; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * FROM t20; +SELECT * FROM t21; +connection master; + +# Cleanup + +DROP TABLE t20; +DROP TABLE t21; +DROP PROCEDURE test.insert; + +# This test uses a stored function that uses user-defined variables to return data +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS test.square; +--enable_warnings + +CREATE TABLE t1 (i INT); + +# Create function that returns a value from @var. In this case, the square function + +CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN +(@var * @var); + +# Set the @var to different values and insert them into a table + +SET @var = 1; +INSERT INTO t1 VALUES (square()); +SET @var = 2; +INSERT INTO t1 VALUES (square()); +SET @var = 3; +INSERT INTO t1 VALUES (square()); +SET @var = 4; +INSERT INTO t1 VALUES (square()); +SET @var = 5; +INSERT INTO t1 VALUES (square()); + +--echo On master: Retrieve the values from the table + +SELECT * FROM t1; + +sync_slave_with_master; + +--echo On slave: Retrieve the values from the table and verify they are the same as on master + +SELECT * FROM t1; + +connection master; + +# Cleanup + +DROP TABLE t1; +DROP FUNCTION test.square; + +# This test uses stored functions that uses user-defined variables to return data +# based on the use of @vars inside a function body. +# This test was constructed for BUG#14914 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +--enable_warnings + +CREATE TABLE t1(a int); +DELIMITER |; + +# Create a function that simply returns the value of an @var. +# Create a function that uses an @var for flow control, creates and uses another +# @var and sets its value to a value based on another @var. + +CREATE FUNCTION f1() returns int deterministic BEGIN + return @a; +END | + +CREATE FUNCTION f2() returns int deterministic BEGIN + IF (@b > 0) then + SET @c = (@a + @b); + else + SET @c = (@a - 1); + END if; + return @c; +END | +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set an @var to a value and insert data into a table using the first function. +# Set two more @vars to some values and insert data into a table using the second function. + +SET @a=500; +INSERT INTO t1 values(f1()); +SET @b = 125; +SET @c = 1; +INSERT INTO t1 values(f2()); + +--echo On master: Retrieve the values from the table + +sync_slave_with_master; +connection master; + +SELECT * from t1; + +connection slave; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * from t1; + +connection master; + +# Cleanup + +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; + +# This test uses a function that changes a user-defined variable in its body. This test +# will ensure the @vars are replicated when needed and not interrupt the normal execution +# of the function on the slave. This also applies to procedures and triggers. + +# This test was constructed for BUG#25167 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +--enable_warnings +CREATE TABLE t1 (i int); +CREATE TABLE t2 (k int); +DELIMITER |; + +# Create a trigger that inserts data into another table, changes the @var then inserts +# another row with the modified value. + +CREATE trigger t1_bi before INSERT on t1 for each row BEGIN + INSERT INTO t2 values (@a); + SET @a:=42; + INSERT INTO t2 values (@a); +END | +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set the @var to a value then insert data into first table. + +SET @a:=100; +INSERT INTO t1 values (5); + +--echo On master: Check to see that data was inserted correctly in both tables + +SELECT * from t1; +SELECT * from t2; + +sync_slave_with_master; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * from t1; +SELECT * from t2; + +connection master; + +--echo End of 5.0 tests. + +# Cleanup + +DROP TABLE t1; +DROP TABLE t2; + +# This test uses a stored function that uses user-defined variables to return data +# The test ensures the value of the user-defined variable is replicated correctly +# and in the correct order of assignment. + +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +--enable_warnings + +CREATE TABLE t1 (i INT); + +# Create two functions. One simply returns the user-defined variable. The other +# returns a value based on the user-defined variable. + +CREATE FUNCTION f1() RETURNS INT RETURN @a; DELIMITER |; CREATE +FUNCTION f2() RETURNS INT BEGIN + INSERT INTO t1 VALUES (10 + @a); + RETURN 0; +END| +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set the variable and execute the functions. + +SET @a:=123; +SELECT f1(), f2(); + +--echo On master: Check to see that data was inserted correctly + +INSERT INTO t1 VALUES(f1()); +SELECT * FROM t1; + +sync_slave_with_master; + +--echo On slave: Check the table for correct data and it matches master + +SELECT * FROM t1; + +connection master; + +# Cleanup + +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1; + sync_slave_with_master; stop slave; + diff --git a/sql/item_func.cc b/sql/item_func.cc index 1e89a579420..bde0c67257e 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4220,7 +4220,14 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, user_var_entry *var_entry; var_entry= get_variable(&thd->user_vars, name, 0); - if (!(opt_bin_log && is_update_query(sql_command))) + /* + Any reference to user-defined variable which is done from stored + function or trigger affects their execution and execution of calling + statement. Hence we want to log all accesses to such variables and + not only those that happen from table-updating statement. + */ + if (!(opt_bin_log && + (is_update_query(sql_command) || thd->in_sub_stmt))) { *out_entry= var_entry; return 0; diff --git a/sql/log.cc b/sql/log.cc index 5ec1826b991..0615deed3ca 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3350,13 +3350,13 @@ bool MYSQL_BIN_LOG::flush_and_sync() return err; } -void MYSQL_BIN_LOG::start_union_events(THD *thd) +void MYSQL_BIN_LOG::start_union_events(THD *thd, query_id_t query_id_param) { DBUG_ASSERT(!thd->binlog_evt_union.do_union); thd->binlog_evt_union.do_union= TRUE; thd->binlog_evt_union.unioned_events= FALSE; thd->binlog_evt_union.unioned_events_trans= FALSE; - thd->binlog_evt_union.first_query_id= thd->query_id; + thd->binlog_evt_union.first_query_id= query_id_param; } void MYSQL_BIN_LOG::stop_union_events(THD *thd) diff --git a/sql/log.h b/sql/log.h index 61db7052f75..b7891f94882 100644 --- a/sql/log.h +++ b/sql/log.h @@ -341,7 +341,7 @@ public: int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync); - void start_union_events(THD *thd); + void start_union_events(THD *thd, query_id_t query_id_param); void stop_union_events(THD *thd); bool is_query_in_union(THD *thd, query_id_t query_id_param); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 7c110185a95..7405ea37314 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1485,8 +1485,24 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, if (need_binlog_call) { + query_id_t q; reset_dynamic(&thd->user_var_events); - mysql_bin_log.start_union_events(thd); + /* + In case of artificially constructed events for function calls + we have separate union for each such event and hence can't use + query_id of real calling statement as the start of all these + unions (this will break logic of replication of user-defined + variables). So we use artifical value which is guaranteed to + be greater than all query_id's of all statements belonging + to previous events/unions. + Possible alternative to this is logging of all function invocations + as one select and not resetting THD::user_var_events before + each invocation. + */ + VOID(pthread_mutex_lock(&LOCK_thread_count)); + q= ::query_id; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + mysql_bin_log.start_union_events(thd, q + 1); binlog_save_options= thd->options; thd->options&= ~OPTION_BIN_LOG; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7309e79a666..a0ad3607ead 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2158,6 +2158,11 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, { options&= ~OPTION_BIN_LOG; } + + if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)&& + !current_stmt_binlog_row_based) + mysql_bin_log.start_union_events(this, this->query_id); + /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; @@ -2201,6 +2206,10 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) sent_row_count= backup->sent_row_count; client_capabilities= backup->client_capabilities; + if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command) && + !current_stmt_binlog_row_based) + mysql_bin_log.stop_union_events(this); + /* The following is added to the old values as we are interested in the total complexity of the query From 2e669d8e7b5944ad4a785ccd11424e060c5ce9bc Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Feb 2007 20:35:28 +0100 Subject: [PATCH 30/46] Fix for BUG#26050 "LOAD DATA INFILE breaks ACID"; the ok must be sent to the client only after the binlog write and engine commit. No testcase for this bug, as to reproduce it, we need to "kill -9" mysqld, which we cannot do in the testsuite. But, I tested by hand. sql/sql_load.cc: D in ACID means that once the client got the ok from the server, the data is durable on disk. Implies that the ok must be sent after the binlog write and after the engine commit, not before. --- sql/sql_load.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 5710f9c4c97..10124e5f5ff 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -353,7 +353,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted, (ulong) (info.records - info.copied), (ulong) thd->cuted_fields); - send_ok(thd,info.copied+info.deleted,0L,name); // on the slave thd->query is never initialized if (!thd->slave_thread) mysql_update_log.write(thd,thd->query,thd->query_length); @@ -378,6 +377,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, #endif /*!EMBEDDED_LIBRARY*/ if (transactional_table) error=ha_autocommit_or_rollback(thd,error); + + /* ok to client sent only after binlog write and engine commit */ + send_ok(thd, info.copied + info.deleted, 0L, name); err: if (thd->lock) { From 4be9d3cc0aeef55f0eeeb66add04ba48fc93ad58 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Feb 2007 10:32:45 -0500 Subject: [PATCH 31/46] BUG#20141 "User-defined variables are not replicated properly for SF/Triggers in SBR mode." BUG#14914 "SP: Uses of session variables in routines are not always replicated" BUG#25167 "Dupl. usage of user-variables in trigger/function is not replicated correctly" This patch corrects a minor error in the previous patch for BUG#20141. This patch corrects an errant code change to sp_head.cc. The comments for the first patch follow: User-defined variables used inside of stored functions/triggers in statements which did not update tables directly were not replicated. We also had problems with replication of user-defined variables which were used in triggers (or stored functions called from table-updating statements) more than once. This patch addresses the first issue by enabling logging of all references to user-defined variables in triggers/stored functions and not only references from table-updating statements. The second issue stemmed from the fact that for user-defined variables used from triggers or stored functions called from table-updating statements we were writing binlog events for each reference instead of only one event for the first reference. This problem is already solved for stored functions called from non-updating statements with help of "event unioning" mechanism. So the patch simply extends this mechanism to the case affected. It also fixes small problem in this mechanism which caused wrong logging of references to user-variables in cases when non-updating statement called several stored functions which used the same variable and some of these function calls were omitted from binlog as they were not updating any tables. sql/sp_head.cc: BUG#20141 "User-defined variables are not replicated properly for SF/Triggers in SBR mode." This patch corrects a minor error in the previous patch for BUG#20141. The code: q= ::query_id; was an errant line of code that was not intended to be in the patch. Instead, the correct line of code is: q= global_query_id; --- sql/sp_head.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index cabdd07b711..24bdd2db5b6 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1477,7 +1477,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, each invocation. */ VOID(pthread_mutex_lock(&LOCK_thread_count)); - q= ::query_id; + q= global_query_id; VOID(pthread_mutex_unlock(&LOCK_thread_count)); mysql_bin_log.start_union_events(thd, q + 1); } From 9a14d8832246a78995657efc7f7564dbc65c0559 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Feb 2007 11:36:17 -0500 Subject: [PATCH 32/46] BUG#20141 "User-defined variables are not replicated properly for SF/Triggers in SBR mode." BUG#14914 "SP: Uses of session variables in routines are not always replicated" BUG#25167 "Dupl. usage of user-variables in trigger/function is not replicated correctly" This patch corrects a minor error in the previous patch for BUG#20141. This patch corrects an errant code change to sp_head.cc. The comments for the first patch follow: User-defined variables used inside of stored functions/triggers in statements which did not update tables directly were not replicated. We also had problems with replication of user-defined variables which were used in triggers (or stored functions called from table-updating statements) more than once. This patch addresses the first issue by enabling logging of all references to user-defined variables in triggers/stored functions and not only references from table-updating statements. The second issue stemmed from the fact that for user-defined variables used from triggers or stored functions called from table-updating statements we were writing binlog events for each reference instead of only one event for the first reference. This problem is already solved for stored functions called from non-updating statements with help of "event unioning" mechanism. So the patch simply extends this mechanism to the case affected. It also fixes small problem in this mechanism which caused wrong logging of references to user-variables in cases when non-updating statement called several stored functions which used the same variable and some of these function calls were omitted from binlog as they were not updating any tables. sql/sp_head.cc: BUG#20141 "User-defined variables are not replicated properly for SF/Triggers in SBR mode." This patch corrects a minor error in the previous patch for BUG#20141. The code: q= ::query_id; was an errant line of code that was not intended to be in the patch. Instead, the correct line of code is: q= global_query_id; --- sql/sp_head.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 3d3e5179ad8..84727dc0b34 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1497,7 +1497,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, each invocation. */ VOID(pthread_mutex_lock(&LOCK_thread_count)); - q= ::query_id; + q= global_query_id; VOID(pthread_mutex_unlock(&LOCK_thread_count)); mysql_bin_log.start_union_events(thd, q + 1); binlog_save_options= thd->options; From 71b762bf69c2b0709d597df0537ae409d5020adf Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Feb 2007 20:57:46 +0100 Subject: [PATCH 33/46] Fix of incorrect merge of test case --- mysql-test/r/rpl_user_variables.result | 110 --------- mysql-test/t/rpl_user_variables.test | 303 +------------------------ 2 files changed, 2 insertions(+), 411 deletions(-) diff --git a/mysql-test/r/rpl_user_variables.result b/mysql-test/r/rpl_user_variables.result index 88b65fb0d3a..3b6002a5d2d 100644 --- a/mysql-test/r/rpl_user_variables.result +++ b/mysql-test/r/rpl_user_variables.result @@ -241,116 +241,6 @@ SELECT f1(), f2(); f1() f2() 123 0 On master: Check to see that data was inserted correctly -CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN (@var * @var); -SET @var = 1; -INSERT INTO t1 VALUES (square()); -SET @var = 2; -INSERT INTO t1 VALUES (square()); -SET @var = 3; -INSERT INTO t1 VALUES (square()); -SET @var = 4; -INSERT INTO t1 VALUES (square()); -SET @var = 5; -INSERT INTO t1 VALUES (square()); -On master: Retrieve the values from the table -SELECT * FROM t1; -i -1 -4 -9 -16 -25 -On slave: Retrieve the values from the table and verify they are the same as on master -SELECT * FROM t1; -i -1 -4 -9 -16 -25 -DROP TABLE t1; -DROP FUNCTION test.square; -DROP TABLE IF EXISTS t1; -DROP FUNCTION IF EXISTS f1; -DROP FUNCTION IF EXISTS f2; -CREATE TABLE t1(a int); -CREATE FUNCTION f1() returns int deterministic -BEGIN -return @a; -END | -CREATE FUNCTION f2() returns int deterministic -BEGIN -IF (@b > 0) then -SET @c = (@a + @b); -else -SET @c = (@a - 1); -END if; -return @c; -END | -SET @a=500; -INSERT INTO t1 values(f1()); -SET @b = 125; -SET @c = 1; -INSERT INTO t1 values(f2()); -On master: Retrieve the values from the table -SELECT * from t1; -a -500 -625 -On slave: Check the tables for correct data and it matches master -SELECT * from t1; -a -500 -625 -DROP TABLE t1; -DROP FUNCTION f1; -DROP FUNCTION f2; -DROP TABLE IF EXISTS t1; -DROP TABLE IF EXISTS t2; -CREATE TABLE t1 (i int); -CREATE TABLE t2 (k int); -CREATE trigger t1_bi before INSERT on t1 for each row -BEGIN -INSERT INTO t2 values (@a); -SET @a:=42; -INSERT INTO t2 values (@a); -END | -SET @a:=100; -INSERT INTO t1 values (5); -On master: Check to see that data was inserted correctly in both tables -SELECT * from t1; -i -5 -SELECT * from t2; -k -100 -42 -On slave: Check the tables for correct data and it matches master -SELECT * from t1; -i -5 -SELECT * from t2; -k -100 -42 -End of 5.0 tests. -DROP TABLE t1; -DROP TABLE t2; -DROP TABLE IF EXISTS t1; -DROP FUNCTION IF EXISTS f1; -DROP FUNCTION IF EXISTS f2; -CREATE TABLE t1 (i INT); -CREATE FUNCTION f1() RETURNS INT RETURN @a; -CREATE FUNCTION f2() RETURNS INT -BEGIN -INSERT INTO t1 VALUES (10 + @a); -RETURN 0; -END| -SET @a:=123; -SELECT f1(), f2(); -f1() f2() -123 0 -On master: Check to see that data was inserted correctly INSERT INTO t1 VALUES(f1()); SELECT * FROM t1; i diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test index b3a361261b5..226591bd13f 100644 --- a/mysql-test/t/rpl_user_variables.test +++ b/mysql-test/t/rpl_user_variables.test @@ -291,6 +291,8 @@ SELECT * from t2; connection master; +--echo End of 5.0 tests. + # Cleanup DROP TABLE t1; @@ -347,307 +349,6 @@ DROP FUNCTION f1; DROP FUNCTION f2; DROP TABLE t1; -save_master_pos; -connection slave; -sync_with_master; - -# BUG#20141 -# The following tests ensure that if user-defined variables are used in SF/Triggers -# that they are replicated correctly. These tests should be run in both SBR and RBR -# modes. - -# This test uses a procedure that inserts data values based on the value of a -# user-defined variable. It also has a trigger that inserts data based on the -# same variable. Successful test runs show that the @var is replicated -# properly and that the procedure and trigger insert the correct data on the -# slave. -# -# The test of stored procedure was included for completeness. Replication of stored -# procedures was not directly affected by BUG#20141. -# -# This test was constructed for BUG#20141 - ---disable_warnings -DROP TABLE IF EXISTS t20; -DROP TABLE IF EXISTS t21; -DROP PROCEDURE IF EXISTS test.insert; ---enable_warnings - -CREATE TABLE t20 (a VARCHAR(20)); -CREATE TABLE t21 (a VARCHAR(20)); -DELIMITER |; - -# Create a procedure that uses the @var for flow control - -CREATE PROCEDURE test.insert() -BEGIN - IF (@VAR) - THEN - INSERT INTO test.t20 VALUES ('SP_TRUE'); - ELSE - INSERT INTO test.t20 VALUES ('SP_FALSE'); - END IF; -END| - -# Create a trigger that uses the @var for flow control - -CREATE TRIGGER test.insert_bi BEFORE INSERT - ON test.t20 FOR EACH ROW - BEGIN - IF (@VAR) - THEN - INSERT INTO test.t21 VALUES ('TRIG_TRUE'); - ELSE - INSERT INTO test.t21 VALUES ('TRIG_FALSE'); - END IF; - END| -DELIMITER ;| - -sync_slave_with_master; -connection master; - -# Set @var and call the procedure, repeat with different values - -SET @VAR=0; -CALL test.insert(); -SET @VAR=1; -CALL test.insert(); - ---echo On master: Check the tables for correct data - -SELECT * FROM t20; -SELECT * FROM t21; - -sync_slave_with_master; - ---echo On slave: Check the tables for correct data and it matches master - -SELECT * FROM t20; -SELECT * FROM t21; -connection master; - -# Cleanup - -DROP TABLE t20; -DROP TABLE t21; -DROP PROCEDURE test.insert; - -# This test uses a stored function that uses user-defined variables to return data -# This test was constructed for BUG#20141 - ---disable_warnings -DROP TABLE IF EXISTS t1; -DROP FUNCTION IF EXISTS test.square; ---enable_warnings - -CREATE TABLE t1 (i INT); - -# Create function that returns a value from @var. In this case, the square function - -CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN (@var * @var); - -sync_slave_with_master; -connection master; - -# Set the @var to different values and insert them into a table - -SET @var = 1; -INSERT INTO t1 VALUES (square()); -SET @var = 2; -INSERT INTO t1 VALUES (square()); -SET @var = 3; -INSERT INTO t1 VALUES (square()); -SET @var = 4; -INSERT INTO t1 VALUES (square()); -SET @var = 5; -INSERT INTO t1 VALUES (square()); - ---echo On master: Retrieve the values from the table - -SELECT * FROM t1; - -sync_slave_with_master; - ---echo On slave: Retrieve the values from the table and verify they are the same as on master - -SELECT * FROM t1; - -connection master; - -# Cleanup - -DROP TABLE t1; -DROP FUNCTION test.square; - -# This test uses stored functions that uses user-defined variables to return data -# based on the use of @vars inside a function body. -# This test was constructed for BUG#14914 - ---disable_warnings -DROP TABLE IF EXISTS t1; -DROP FUNCTION IF EXISTS f1; -DROP FUNCTION IF EXISTS f2; ---enable_warnings - -CREATE TABLE t1(a int); -DELIMITER |; - -# Create a function that simply returns the value of an @var. -# Create a function that uses an @var for flow control, creates and uses another -# @var and sets its value to a value based on another @var. - -CREATE FUNCTION f1() returns int deterministic -BEGIN - return @a; -END | - -CREATE FUNCTION f2() returns int deterministic -BEGIN - IF (@b > 0) then - SET @c = (@a + @b); - else - SET @c = (@a - 1); - END if; - return @c; -END | -DELIMITER ;| - -sync_slave_with_master; -connection master; - -# Set an @var to a value and insert data into a table using the first function. -# Set two more @vars to some values and insert data into a table using the second function. - -SET @a=500; -INSERT INTO t1 values(f1()); -SET @b = 125; -SET @c = 1; -INSERT INTO t1 values(f2()); - -sync_slave_with_master; - ---echo On master: Retrieve the values from the table - -SELECT * from t1; - ---echo On slave: Check the tables for correct data and it matches master - -SELECT * from t1; - -connection master; - -# Cleanup - -DROP TABLE t1; -DROP FUNCTION f1; -DROP FUNCTION f2; - -# This test uses a function that changes a user-defined variable in its body. This test -# will ensure the @vars are replicated when needed and not interrupt the normal execution -# of the function on the slave. This also applies to triggers. -# -# This test was constructed for BUG#25167 - ---disable_warnings -DROP TABLE IF EXISTS t1; -DROP TABLE IF EXISTS t2; ---enable_warnings -CREATE TABLE t1 (i int); -CREATE TABLE t2 (k int); -DELIMITER |; - -# Create a trigger that inserts data into another table, changes the @var then inserts -# another row with the modified value. - -CREATE trigger t1_bi before INSERT on t1 for each row -BEGIN - INSERT INTO t2 values (@a); - SET @a:=42; - INSERT INTO t2 values (@a); -END | -DELIMITER ;| - -sync_slave_with_master; -connection master; - -# Set the @var to a value then insert data into first table. - -SET @a:=100; -INSERT INTO t1 values (5); - ---echo On master: Check to see that data was inserted correctly in both tables - -SELECT * from t1; -SELECT * from t2; - -sync_slave_with_master; - ---echo On slave: Check the tables for correct data and it matches master - -SELECT * from t1; -SELECT * from t2; - -connection master; - ---echo End of 5.0 tests. - -# Cleanup - -DROP TABLE t1; -DROP TABLE t2; - -# This test uses a stored function that uses user-defined variables to return data -# The test ensures the value of the user-defined variable is replicated correctly -# and in the correct order of assignment. -# This test was constructed for BUG#20141 - ---disable_warnings -DROP TABLE IF EXISTS t1; -DROP FUNCTION IF EXISTS f1; -DROP FUNCTION IF EXISTS f2; ---enable_warnings - -CREATE TABLE t1 (i INT); - -# Create two functions. One simply returns the user-defined variable. The other -# returns a value based on the user-defined variable. - -CREATE FUNCTION f1() RETURNS INT RETURN @a; -DELIMITER |; -CREATE FUNCTION f2() RETURNS INT -BEGIN - INSERT INTO t1 VALUES (10 + @a); - RETURN 0; -END| -DELIMITER ;| - -sync_slave_with_master; -connection master; - -# Set the variable and execute the functions. - -SET @a:=123; -SELECT f1(), f2(); - ---echo On master: Check to see that data was inserted correctly - -INSERT INTO t1 VALUES(f1()); -SELECT * FROM t1; - -sync_slave_with_master; - ---echo On slave: Check the table for correct data and it matches master - -SELECT * FROM t1; - -connection master; - -# Cleanup - -DROP FUNCTION f1; -DROP FUNCTION f2; -DROP TABLE t1; - sync_slave_with_master; stop slave; From c3a3aff54feeb194e0217249305f74f18021099a Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Feb 2007 17:06:57 +0400 Subject: [PATCH 34/46] Bug#15126 character_set_database is not replicated (LOAD DATA INFILE need it) This patch fixes problem that LOAD DATA could use different character sets when loading files on master and on slave sides: - Adding replication of thd->variables.collation_database - Adding optional character set clause into LOAD DATA Note, the second way, with explicit CHARACTER SET clause should be the recommended way to load data using an alternative character set. The old way, using "SET @@character_set_database=xxx" should be gradually depricated. mysql-test/r/mysqlbinlog.result: Adding test case mysql-test/t/mysqlbinlog.test: Adding test case sql/log_event.cc: Adding logging of thd->variables.collation_database sql/log_event.h: Adding declarations sql/sql_class.cc: Exchange character set is null by default sql/sql_class.h: Adding character set into sql_exchange sql/sql_load.cc: - Using exchange character set (if it was specified in LOAD DATA syntax) - Using thd->variables.collation_database by default sql/sql_yacc.yy: Adding optional character set clause into LOAD DATA syntax mysql-test/r/rpl_loaddata2.result: New BitKeeper file ``mysql-test/r/rpl_loaddata2.result'' mysql-test/std_data/loaddata6.dat: New BitKeeper file ``mysql-test/std_data/loaddata6.dat'' mysql-test/t/rpl_loaddata2.test: New BitKeeper file ``mysql-test/t/rpl_loaddata2.test'' --- mysql-test/r/mysqlbinlog.result | 58 +++++++++++++++++++++++++++++++ mysql-test/r/rpl_loaddata2.result | 37 ++++++++++++++++++++ mysql-test/std_data/loaddata6.dat | 1 + mysql-test/t/mysqlbinlog.test | 24 +++++++++++++ mysql-test/t/rpl_loaddata2.test | 33 ++++++++++++++++++ sql/log_event.cc | 48 ++++++++++++++++++++++--- sql/log_event.h | 5 ++- sql/sql_class.cc | 1 + sql/sql_class.h | 1 + sql/sql_load.cc | 3 +- sql/sql_yacc.yy | 7 ++++ 11 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 mysql-test/r/rpl_loaddata2.result create mode 100644 mysql-test/std_data/loaddata6.dat create mode 100644 mysql-test/t/rpl_loaddata2.test diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index b2571ec5d12..0f34259d11d 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -270,3 +270,61 @@ call p1(); 1 drop procedure p1; drop table t1, t2, t03, t04, t3, t4, t5; +flush logs; +create table t1 (a varchar(64) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; +select hex(a) from t1; +hex(a) +C3BF +D0AA +C3BF +C3BF +D0AA +C3BF +D0AA +drop table t1; +flush logs; +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; +DELIMITER /*!*/; +use test/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1/*!*/; +SET @@session.sql_mode=0/*!*/; +/*!\C latin1 *//*!*/; +SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; +create table t1 (a varchar(64) character set utf8)/*!*/; +SET TIMESTAMP=1000000000/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-6-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=7/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-7-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=DEFAULT/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-8-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-9-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=7/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-a-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=DEFAULT/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-b-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-c-0' INTO table t1 character set koi8r/*!*/; +SET TIMESTAMP=1000000000/*!*/; +drop table t1/*!*/; +DELIMITER ; +# End of log file +ROLLBACK /* added by mysqlbinlog */; +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; diff --git a/mysql-test/r/rpl_loaddata2.result b/mysql-test/r/rpl_loaddata2.result new file mode 100644 index 00000000000..929d06e74cf --- /dev/null +++ b/mysql-test/r/rpl_loaddata2.result @@ -0,0 +1,37 @@ +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 varchar(10) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; +select hex(a) from t1; +hex(a) +C3BF +D0AA +C3BF +C3BF +C3BF +D0AA +D0AA +select hex(a) from t1; +hex(a) +C3BF +D0AA +C3BF +C3BF +C3BF +D0AA +D0AA +drop table t1; diff --git a/mysql-test/std_data/loaddata6.dat b/mysql-test/std_data/loaddata6.dat new file mode 100644 index 00000000000..29e181ebb88 --- /dev/null +++ b/mysql-test/std_data/loaddata6.dat @@ -0,0 +1 @@ + diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index b2bda247cd7..11ddc0d5a01 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -179,4 +179,28 @@ drop procedure p1; # clean up drop table t1, t2, t03, t04, t3, t4, t5; +# +# Bug#15126 character_set_database is not replicated +# (LOAD DATA INFILE need it) +# + +flush logs; +create table t1 (a varchar(64) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; +select hex(a) from t1; +drop table t1; +flush logs; +--exec $MYSQL_BINLOG --short-form $MYSQLTEST_VARDIR/log/master-bin.000011 + # End of 5.0 tests + diff --git a/mysql-test/t/rpl_loaddata2.test b/mysql-test/t/rpl_loaddata2.test new file mode 100644 index 00000000000..7f2389cb9f6 --- /dev/null +++ b/mysql-test/t/rpl_loaddata2.test @@ -0,0 +1,33 @@ +# +# Check LOAD DATA + character sets + replication +# +source include/master-slave.inc; + +# +# Bug#15126 character_set_database is not replicated +# (LOAD DATA INFILE need it) +# +connection master; +create table t1 (a varchar(10) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; + +select hex(a) from t1; + +save_master_pos; +connection slave; +sync_with_master; + +select hex(a) from t1; +connection master; +drop table t1; +sync_slave_with_master; diff --git a/sql/log_event.cc b/sql/log_event.cc index a14cd79461d..daf7fdeab2c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1088,7 +1088,8 @@ bool Query_log_event::write(IO_CACHE* file) 1+4+ // code of autoinc and the 2 autoinc variables 1+6+ // code of charset and charset 1+1+MAX_TIME_ZONE_NAME_LENGTH+ // code of tz and tz length and tz name - 1+2 // code of lc_time_names and lc_time_names_number + 1+2+ // code of lc_time_names and lc_time_names_number + 1+2 // code of charset_database and charset_database_number ], *start, *start_of_status; ulong event_length; @@ -1207,6 +1208,13 @@ bool Query_log_event::write(IO_CACHE* file) int2store(start, lc_time_names_number); start+= 2; } + if (charset_database_number) + { + DBUG_ASSERT(charset_database_number <= 0xFFFF); + *start++= Q_CHARSET_DATABASE_CODE; + int2store(start, charset_database_number); + start+= 2; + } /* Here there could be code like if (command-line-option-which-says-"log_this_variable" && inited) @@ -1272,7 +1280,8 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, sql_mode(thd_arg->variables.sql_mode), auto_increment_increment(thd_arg->variables.auto_increment_increment), auto_increment_offset(thd_arg->variables.auto_increment_offset), - lc_time_names_number(thd_arg->variables.lc_time_names->number) + lc_time_names_number(thd_arg->variables.lc_time_names->number), + charset_database_number(0) { time_t end_time; time(&end_time); @@ -1280,6 +1289,9 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, catalog_len = (catalog) ? (uint32) strlen(catalog) : 0; /* status_vars_len is set just before writing the event */ db_len = (db) ? (uint32) strlen(db) : 0; + if (thd_arg->variables.collation_database != thd_arg->db_charset) + charset_database_number= thd_arg->variables.collation_database->number; + /* If we don't use flags2 for anything else than options contained in thd->options, it would be more efficient to flags2=thd_arg->options @@ -1350,7 +1362,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, db(NullS), catalog_len(0), status_vars_len(0), flags2_inited(0), sql_mode_inited(0), charset_inited(0), auto_increment_increment(1), auto_increment_offset(1), - time_zone_len(0), lc_time_names_number(0) + time_zone_len(0), lc_time_names_number(0), charset_database_number(0) { ulong data_len; uint32 tmp; @@ -1455,6 +1467,10 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, lc_time_names_number= uint2korr(pos); pos+= 2; break; + case Q_CHARSET_DATABASE_CODE: + charset_database_number= uint2korr(pos); + pos+= 2; + break; default: /* That's why you must write status vars in growing order of code */ DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\ @@ -1652,6 +1668,16 @@ void Query_log_event::print_query_header(FILE* file, lc_time_names_number, print_event_info->delimiter); print_event_info->lc_time_names_number= lc_time_names_number; } + if (charset_database_number != print_event_info->charset_database_number) + { + if (charset_database_number) + fprintf(file, "SET @@session.collation_database=%d%s\n", + charset_database_number, print_event_info->delimiter); + else + fprintf(file, "SET @@session.collation_database=DEFAULT%s\n", + print_event_info->delimiter); + print_event_info->charset_database_number= charset_database_number; + } } @@ -1817,7 +1843,21 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, } else thd->variables.lc_time_names= &my_locale_en_US; - + if (charset_database_number) + { + CHARSET_INFO *cs; + if (!(cs= get_charset(charset_database_number, MYF(0)))) + { + char buf[20]; + int10_to_str((int) charset_database_number, buf, -10); + my_error(ER_UNKNOWN_COLLATION, MYF(0), buf); + goto compare_errors; + } + thd->variables.collation_database= cs; + } + else + thd->variables.collation_database= thd->db_charset; + /* Execute the query (note that we bypass dispatch_command()) */ mysql_parse(thd, thd->query, thd->query_length); diff --git a/sql/log_event.h b/sql/log_event.h index d3ebe6860f5..c08306e30d2 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -272,6 +272,7 @@ struct sql_ex_info #define Q_LC_TIME_NAMES_CODE 7 +#define Q_CHARSET_DATABASE_CODE 8 /* Intvar event post-header */ #define I_TYPE_OFFSET 0 @@ -509,10 +510,11 @@ typedef struct st_print_event_info char charset[6]; // 3 variables, each of them storable in 2 bytes char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH]; uint lc_time_names_number; + uint charset_database_number; st_print_event_info() :flags2_inited(0), sql_mode_inited(0), auto_increment_increment(1),auto_increment_offset(1), charset_inited(0), - lc_time_names_number(0) + lc_time_names_number(0), charset_database_number(0) { /* Currently we only use static PRINT_EVENT_INFO objects, so zeroed at @@ -797,6 +799,7 @@ public: uint time_zone_len; /* 0 means uninited */ const char *time_zone_str; uint lc_time_names_number; /* 0 means en_US */ + uint charset_database_number; #ifndef MYSQL_CLIENT diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0794d4c797a..a020c570cb6 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -902,6 +902,7 @@ sql_exchange::sql_exchange(char *name,bool flag) enclosed= line_start= &my_empty_string; line_term= &default_line_term; escaped= &default_escaped; + cs= NULL; } bool select_send::send_fields(List &list, uint flags) diff --git a/sql/sql_class.h b/sql/sql_class.h index 05034ebd573..817a033a526 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1687,6 +1687,7 @@ public: bool opt_enclosed; bool dumpfile; ulong skip_lines; + CHARSET_INFO *cs; sql_exchange(char *name,bool dumpfile_flag); }; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 0e4057d9ae4..d971eae56c0 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -313,7 +313,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, info.handle_duplicates=handle_duplicates; info.escape_char=escaped->length() ? (*escaped)[0] : INT_MAX; - READ_INFO read_info(file,tot_length,thd->variables.collation_database, + READ_INFO read_info(file,tot_length, + ex->cs ? ex->cs : thd->variables.collation_database, *field_term,*ex->line_start, *ex->line_term, *enclosed, info.escape_char, read_file_from_client, is_fifo); if (read_info.error) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 92640ea58d6..8843423c352 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -992,6 +992,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); old_or_new_charset_name_or_default collation_name collation_name_or_default + opt_load_data_charset %type internal_variable_name @@ -3262,6 +3263,10 @@ charset_name_or_default: charset_name { $$=$1; } | DEFAULT { $$=NULL; } ; +opt_load_data_charset: + /* Empty */ { $$= NULL; } + | charset charset_name_or_default { $$= $2; } + ; old_or_new_charset_name: ident_or_text @@ -7214,6 +7219,8 @@ load_data: lex->update_list.empty(); lex->value_list.empty(); } + opt_load_data_charset + { Lex->exchange->cs= $12; } opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec opt_load_data_set_spec {} From 18f1d1a2005156724eb0e633b629ec3df5f5266b Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Feb 2007 17:32:18 +0400 Subject: [PATCH 35/46] After merge fix --- mysql-test/r/mysqlbinlog.result | 1 - mysql-test/t/mysqlbinlog.test | 2 -- 2 files changed, 3 deletions(-) diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index 412b4ad3b85..8ca2422443d 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -272,7 +272,6 @@ call p1(); 1 1 drop procedure p1; -drop table t1, t2, t03, t04, t3, t4, t5; flush logs; create table t1 (a varchar(64) character set utf8); load data infile '../std_data_ln/loaddata6.dat' into table t1; diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index 1545e7f1d99..fe86d850267 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -189,8 +189,6 @@ drop procedure p1; --exec $MYSQL_BINLOG --version 2>&1 > /dev/null --exec $MYSQL_BINLOG --help 2>&1 > /dev/null --enable_query_log -# clean up -drop table t1, t2, t03, t04, t3, t4, t5; # # Bug#15126 character_set_database is not replicated From 4a83cd38eae270a20bbd257cbd3b986dbc6aab31 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Feb 2007 18:13:23 +0400 Subject: [PATCH 36/46] Additional test changes for Bug N 15126 character_set_database is not replicated (LOAD DATA INFILE need it) Positions of some binlog events were changed because of additional logging of @@collation_database. --- mysql-test/r/ctype_cp932_binlog.result | 18 +++++++++--------- mysql-test/r/ctype_cp932_notembedded.result | 6 +++--- mysql-test/t/ctype_cp932_binlog.test | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/ctype_cp932_binlog.result b/mysql-test/r/ctype_cp932_binlog.result index d3d800b7bf0..ff295961008 100644 --- a/mysql-test/r/ctype_cp932_binlog.result +++ b/mysql-test/r/ctype_cp932_binlog.result @@ -8,9 +8,9 @@ SET @var1= x'8300'; EXECUTE stmt1 USING @var1; SHOW BINLOG EVENTS FROM 98; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 98 Query 1 185 use `test`; CREATE TABLE t1(f1 blob) -master-bin.000001 185 User var 1 224 @`var1`=_binary 0x8300 COLLATE binary -master-bin.000001 224 Query 1 317 use `test`; INSERT INTO t1 VALUES(@'var1') +master-bin.000001 98 Query 1 188 use `test`; CREATE TABLE t1(f1 blob) +master-bin.000001 188 User var 1 227 @`var1`=_binary 0x8300 COLLATE binary +master-bin.000001 227 Query 1 323 use `test`; INSERT INTO t1 VALUES(@'var1') SELECT HEX(f1) FROM t1; HEX(f1) 8300 @@ -30,17 +30,17 @@ HEX(s1) HEX(s2) d 466F6F2773206120426172 ED40ED41ED42 47.93 DROP PROCEDURE bug18293| DROP TABLE t4| -SHOW BINLOG EVENTS FROM 393| +SHOW BINLOG EVENTS FROM 402| Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 393 Query 1 556 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, +master-bin.000001 402 Query 1 568 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, s2 CHAR(50) CHARACTER SET cp932, d DECIMAL(10,2)) -master-bin.000001 556 Query 1 801 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), +master-bin.000001 568 Query 1 816 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), IN ins2 CHAR(50) CHARACTER SET cp932, IN ind DECIMAL(10,2)) BEGIN INSERT INTO t4 VALUES (ins1, ins2, ind); END -master-bin.000001 801 Query 1 1017 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) -master-bin.000001 1017 Query 1 1103 use `test`; DROP PROCEDURE bug18293 -master-bin.000001 1103 Query 1 1179 use `test`; DROP TABLE t4 +master-bin.000001 816 Query 1 1035 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) +master-bin.000001 1035 Query 1 1124 use `test`; DROP PROCEDURE bug18293 +master-bin.000001 1124 Query 1 1203 use `test`; DROP TABLE t4 diff --git a/mysql-test/r/ctype_cp932_notembedded.result b/mysql-test/r/ctype_cp932_notembedded.result index d04fce7738c..c58bfc65454 100644 --- a/mysql-test/r/ctype_cp932_notembedded.result +++ b/mysql-test/r/ctype_cp932_notembedded.result @@ -8,9 +8,9 @@ SET @var1= x'8300'; EXECUTE stmt1 USING @var1; SHOW BINLOG EVENTS FROM 98; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 98 Query 1 185 use `test`; CREATE TABLE t1(f1 blob) -master-bin.000001 185 User var 1 224 @`var1`=_binary 0x8300 COLLATE binary -master-bin.000001 224 Query 1 317 use `test`; INSERT INTO t1 VALUES(@'var1') +master-bin.000001 98 Query 1 188 use `test`; CREATE TABLE t1(f1 blob) +master-bin.000001 188 User var 1 227 @`var1`=_binary 0x8300 COLLATE binary +master-bin.000001 227 Query 1 323 use `test`; INSERT INTO t1 VALUES(@'var1') SELECT HEX(f1) FROM t1; HEX(f1) 8300 diff --git a/mysql-test/t/ctype_cp932_binlog.test b/mysql-test/t/ctype_cp932_binlog.test index 3bbbe94f0e3..ee0e588fdae 100644 --- a/mysql-test/t/ctype_cp932_binlog.test +++ b/mysql-test/t/ctype_cp932_binlog.test @@ -51,7 +51,7 @@ CALL bug18293("Foo's a Bar", _cp932 0xED40ED41ED42, 47.93)| SELECT HEX(s1),HEX(s2),d FROM t4| DROP PROCEDURE bug18293| DROP TABLE t4| -SHOW BINLOG EVENTS FROM 393| +SHOW BINLOG EVENTS FROM 402| delimiter ;| # End of 5.0 tests From d93c7f39f8ff8db3a6106a474a3791d7b1b9e9c7 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Mar 2007 11:23:12 +0400 Subject: [PATCH 37/46] Additional fix for bug N 15126 Replacing local directory. mysql-test/r/mysqlbinlog.result: Additional fix for bug N 15126 mysql-test/t/mysqlbinlog.test: Additional fix for bug N 15126 --- mysql-test/r/mysqlbinlog.result | 14 +++++++------- mysql-test/t/mysqlbinlog.test | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index 8ca2422443d..0664cc73608 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -307,23 +307,23 @@ SET @@session.sql_mode=0/*!*/; SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; create table t1 (a varchar(64) character set utf8)/*!*/; SET TIMESTAMP=1000000000/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-6-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-6-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=7/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-7-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-7-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=DEFAULT/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-8-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-8-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-9-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-9-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=7/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-a-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-a-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=DEFAULT/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-b-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-b-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-c-0' INTO table t1 character set koi8r/*!*/; +load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-c-0' INTO table t1 character set koi8r/*!*/; SET TIMESTAMP=1000000000/*!*/; drop table t1/*!*/; DELIMITER ; diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index fe86d850267..f992729d2be 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -211,6 +211,7 @@ load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8 select hex(a) from t1; drop table t1; flush logs; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --exec $MYSQL_BINLOG --short-form $MYSQLTEST_VARDIR/log/master-bin.000011 # End of 5.0 tests From b3fafa40fc839141ba525466e0c98d3db01631f8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Mar 2007 08:41:13 +0100 Subject: [PATCH 38/46] Fixes of compilation warnings and errors. sql/mysql_priv.h: Removing compiler warning "NULL used in arithmetic" sql/mysqld.cc: First argument to WARN_DEPRECATED is supposed to be a pointer (to THD structure) sql/sql_insert.cc: Removing compiler warning "unused variable". Apparently query is not used when compiling libmysqld. sql/sql_yacc.yy: Removing compiler error "Macro already defined" --- sql/mysql_priv.h | 2 +- sql/mysqld.cc | 2 +- sql/sql_insert.cc | 2 ++ sql/sql_yacc.yy | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 97f56c83b4f..7ca0d567253 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -100,7 +100,7 @@ void net_set_read_timeout(NET *net, uint timeout); #define WARN_DEPRECATED(Thd,Ver,Old,New) \ do { \ DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0); \ - if (Thd != NULL) \ + if (((gptr)Thd) != NULL) \ push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ (Old), (Ver), (New)); \ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5709a12c03d..f8fbbee5644 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -6957,7 +6957,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), if (!slave_warning_issued) //only show the warning once { slave_warning_issued = true; - WARN_DEPRECATED(0, "5.2", "for replication startup options", + WARN_DEPRECATED(NULL, "5.2", "for replication startup options", "'CHANGE MASTER'"); } break; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 613b28faf63..b44cbf46a31 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -381,7 +381,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, List_item *values; Name_resolution_context *context; Name_resolution_context_state ctx_state; +#ifndef EMBEDDED_LIBRARY char *query= thd->query; +#endif bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->security_ctx->master_access & SUPER_ACL)); thr_lock_type lock_type = table_list->lock_type; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index efe8d85d8f5..933fa839e71 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -46,6 +46,7 @@ const LEX_STRING null_lex_str={0,0}; #define yyoverflow(A,B,C,D,E,F) {ulong val= *(F); if (my_yyoverflow((B), (D), &val)) { yyerror((char*) (A)); return 2; } else { *(F)= (YYSIZE_T)val; }} +#undef WARN_DEPRECATED /* this macro is also defined in mysql_priv.h */ #define WARN_DEPRECATED(A,B) \ push_warning_printf(((THD *)yythd), MYSQL_ERROR::WARN_LEVEL_WARN, \ ER_WARN_DEPRECATED_SYNTAX, \ From ef02cb6305cab784573a8a607889bf3420c946b3 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Mar 2007 11:52:25 +0400 Subject: [PATCH 39/46] Renaming recenly added test case, because of name conflict when merging into 5.1. mysql-test/t/rpl_loaddata_charset.test: Rename: mysql-test/t/rpl_loaddata2.test -> mysql-test/t/rpl_loaddata_charset.test mysql-test/r/rpl_loaddata_charset.result: Rename: mysql-test/r/rpl_loaddata2.result -> mysql-test/r/rpl_loaddata_charset.result --- .../r/{rpl_loaddata2.result => rpl_loaddata_charset.result} | 0 mysql-test/t/{rpl_loaddata2.test => rpl_loaddata_charset.test} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename mysql-test/r/{rpl_loaddata2.result => rpl_loaddata_charset.result} (100%) rename mysql-test/t/{rpl_loaddata2.test => rpl_loaddata_charset.test} (100%) diff --git a/mysql-test/r/rpl_loaddata2.result b/mysql-test/r/rpl_loaddata_charset.result similarity index 100% rename from mysql-test/r/rpl_loaddata2.result rename to mysql-test/r/rpl_loaddata_charset.result diff --git a/mysql-test/t/rpl_loaddata2.test b/mysql-test/t/rpl_loaddata_charset.test similarity index 100% rename from mysql-test/t/rpl_loaddata2.test rename to mysql-test/t/rpl_loaddata_charset.test From 30ece9b060bc141becd5ead9f67058ecfda21bec Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Mar 2007 12:26:07 +0400 Subject: [PATCH 40/46] Fix for wrong replace command. --- mysql-test/r/mysqlbinlog.result | 14 +++++++------- mysql-test/t/mysqlbinlog.test | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index 0664cc73608..9ee2a1e769a 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -307,23 +307,23 @@ SET @@session.sql_mode=0/*!*/; SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; create table t1 (a varchar(64) character set utf8)/*!*/; SET TIMESTAMP=1000000000/*!*/; -load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-6-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-6-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=7/*!*/; -load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-7-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-7-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=DEFAULT/*!*/; -load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-8-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-8-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; -load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-9-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-9-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=7/*!*/; -load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-a-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-a-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=DEFAULT/*!*/; -load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-b-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-b-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; -load data LOCAL INFILE 'MYSQLTEST_VARDIR/tmp/SQL_LOAD_MB-c-0' INTO table t1 character set koi8r/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-c-0' INTO table t1 character set koi8r/*!*/; SET TIMESTAMP=1000000000/*!*/; drop table t1/*!*/; DELIMITER ; diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index f992729d2be..d35bd16c1f9 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -211,7 +211,7 @@ load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8 select hex(a) from t1; drop table t1; flush logs; ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR --exec $MYSQL_BINLOG --short-form $MYSQLTEST_VARDIR/log/master-bin.000011 # End of 5.0 tests From 8fa0d1b98e701f3643093696ffe6e6ea02481a82 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Mar 2007 10:40:48 +0100 Subject: [PATCH 41/46] Removing two more compilation warnings. sql/slave.cc: Guard declaration of variable to avoid "unused variable" warning. sql/sql_repl.cc: Guard declaration of variable to avoid "unused variable" warning. --- sql/slave.cc | 2 ++ sql/sql_repl.cc | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sql/slave.cc b/sql/slave.cc index 572fafd58b5..9d466ce5dad 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -52,7 +52,9 @@ ulonglong relay_log_space_limit = 0; int disconnect_slave_event_count = 0, abort_slave_event_count = 0; int events_till_abort = -1; +#ifndef DBUG_OFF static int events_till_disconnect = -1; +#endif typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 1702783b5b7..b451c612398 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -22,7 +22,9 @@ int max_binlog_dump_events = 0; // unlimited my_bool opt_sporadic_binlog_dump_fail = 0; +#ifndef DBUG_OFF static int binlog_dump_count = 0; +#endif /* fake_rotate_event() builds a fake (=which does not exist physically in any From bf9521d24bd9fd5228d52642fd16abd3830c2d09 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Mar 2007 11:52:20 +0100 Subject: [PATCH 42/46] Fixing manual merge error. sql/slave.cc: Removing piece of code which get there when manual merging from 5.0 tree. --- sql/slave.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index bb2aa11626e..bede185f4c1 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2460,13 +2460,6 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); -#ifndef DBUG_OFF - /* - Bug #19938 Valgrind error (race) in handle_slave_sql() - Read the value of rli->event_till_abort before releasing the mutex - */ - const int eta= rli->events_till_abort; -#endif /* Note: the order of the broadcast and unlock calls below (first broadcast, then unlock) is important. Otherwise a killer_thread can execute between the calls and From b9959b748adb81b7de229fc10ef0ff307895805f Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Mar 2007 16:08:13 +0400 Subject: [PATCH 43/46] after merge fix --- .../binlog_tests}/ctype_cp932_binlog.test | 27 ++--------- mysql-test/r/binlog_stm_ctype_ucs.result | 27 +++++++++++ mysql-test/r/ctype_cp932_binlog.result | 46 ------------------- mysql-test/r/ctype_cp932_notembedded.result | 17 ------- 4 files changed, 30 insertions(+), 87 deletions(-) rename mysql-test/{t => extra/binlog_tests}/ctype_cp932_binlog.test (64%) create mode 100644 mysql-test/r/binlog_stm_ctype_ucs.result delete mode 100644 mysql-test/r/ctype_cp932_binlog.result delete mode 100644 mysql-test/r/ctype_cp932_notembedded.result diff --git a/mysql-test/t/ctype_cp932_binlog.test b/mysql-test/extra/binlog_tests/ctype_cp932_binlog.test similarity index 64% rename from mysql-test/t/ctype_cp932_binlog.test rename to mysql-test/extra/binlog_tests/ctype_cp932_binlog.test index ee0e588fdae..5e93d6e126e 100644 --- a/mysql-test/t/ctype_cp932_binlog.test +++ b/mysql-test/extra/binlog_tests/ctype_cp932_binlog.test @@ -26,32 +26,11 @@ SET @var1= x'8300'; # code (and I have used it to test the fix) until there is some way to # exercise this code from mysql-test-run. EXECUTE stmt1 USING @var1; -SHOW BINLOG EVENTS FROM 98; +--replace_column 2 # 5 # +--replace_regex /table_id: [0-9]+/table_id: #/ +SHOW BINLOG EVENTS FROM 102; SELECT HEX(f1) FROM t1; DROP table t1; # end test for bug#11338 # End of 4.1 tests - -# -# Bug#18293: Values in stored procedure written to binlog unescaped -# - -delimiter |; -CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, - s2 CHAR(50) CHARACTER SET cp932, - d DECIMAL(10,2))| -CREATE PROCEDURE bug18293 (IN ins1 CHAR(50), - IN ins2 CHAR(50) CHARACTER SET cp932, - IN ind DECIMAL(10,2)) - BEGIN - INSERT INTO t4 VALUES (ins1, ins2, ind); - END| -CALL bug18293("Foo's a Bar", _cp932 0xED40ED41ED42, 47.93)| -SELECT HEX(s1),HEX(s2),d FROM t4| -DROP PROCEDURE bug18293| -DROP TABLE t4| -SHOW BINLOG EVENTS FROM 402| -delimiter ;| - -# End of 5.0 tests diff --git a/mysql-test/r/binlog_stm_ctype_ucs.result b/mysql-test/r/binlog_stm_ctype_ucs.result new file mode 100644 index 00000000000..76f3a3b06b9 --- /dev/null +++ b/mysql-test/r/binlog_stm_ctype_ucs.result @@ -0,0 +1,27 @@ +SET TIMESTAMP=10000; +create table t2 (c char(30)) charset=ucs2; +set @v=convert('abc' using ucs2); +reset master; +insert into t2 values (@v); +show binlog events from 102; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 102 User var 1 142 @`v`=_ucs2 0x006100620063 COLLATE ucs2_general_ci +master-bin.000001 142 Query 1 231 use `test`; insert into t2 values (@v) +flush logs; +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; +DELIMITER /*!*/; +ROLLBACK/*!*/; +SET @`v`:=_ucs2 0x006100620063 COLLATE `ucs2_general_ci`/*!*/; +use test/*!*/; +SET TIMESTAMP=10000/*!*/; +SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1/*!*/; +SET @@session.sql_mode=0/*!*/; +/*!\C latin1 *//*!*/; +SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; +insert into t2 values (@v)/*!*/; +DELIMITER ; +# End of log file +ROLLBACK /* added by mysqlbinlog */; +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; +drop table t2; diff --git a/mysql-test/r/ctype_cp932_binlog.result b/mysql-test/r/ctype_cp932_binlog.result deleted file mode 100644 index ff295961008..00000000000 --- a/mysql-test/r/ctype_cp932_binlog.result +++ /dev/null @@ -1,46 +0,0 @@ -drop table if exists t1; -set names cp932; -set character_set_database = cp932; -RESET MASTER; -CREATE TABLE t1(f1 blob); -PREPARE stmt1 FROM 'INSERT INTO t1 VALUES(?)'; -SET @var1= x'8300'; -EXECUTE stmt1 USING @var1; -SHOW BINLOG EVENTS FROM 98; -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 98 Query 1 188 use `test`; CREATE TABLE t1(f1 blob) -master-bin.000001 188 User var 1 227 @`var1`=_binary 0x8300 COLLATE binary -master-bin.000001 227 Query 1 323 use `test`; INSERT INTO t1 VALUES(@'var1') -SELECT HEX(f1) FROM t1; -HEX(f1) -8300 -DROP table t1; -CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, -s2 CHAR(50) CHARACTER SET cp932, -d DECIMAL(10,2))| -CREATE PROCEDURE bug18293 (IN ins1 CHAR(50), -IN ins2 CHAR(50) CHARACTER SET cp932, -IN ind DECIMAL(10,2)) -BEGIN -INSERT INTO t4 VALUES (ins1, ins2, ind); -END| -CALL bug18293("Foo's a Bar", _cp932 0xED40ED41ED42, 47.93)| -SELECT HEX(s1),HEX(s2),d FROM t4| -HEX(s1) HEX(s2) d -466F6F2773206120426172 ED40ED41ED42 47.93 -DROP PROCEDURE bug18293| -DROP TABLE t4| -SHOW BINLOG EVENTS FROM 402| -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 402 Query 1 568 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, -s2 CHAR(50) CHARACTER SET cp932, -d DECIMAL(10,2)) -master-bin.000001 568 Query 1 816 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), -IN ins2 CHAR(50) CHARACTER SET cp932, -IN ind DECIMAL(10,2)) -BEGIN -INSERT INTO t4 VALUES (ins1, ins2, ind); -END -master-bin.000001 816 Query 1 1035 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) -master-bin.000001 1035 Query 1 1124 use `test`; DROP PROCEDURE bug18293 -master-bin.000001 1124 Query 1 1203 use `test`; DROP TABLE t4 diff --git a/mysql-test/r/ctype_cp932_notembedded.result b/mysql-test/r/ctype_cp932_notembedded.result deleted file mode 100644 index c58bfc65454..00000000000 --- a/mysql-test/r/ctype_cp932_notembedded.result +++ /dev/null @@ -1,17 +0,0 @@ -drop table if exists t1; -set names cp932; -set character_set_database = cp932; -RESET MASTER; -CREATE TABLE t1(f1 blob); -PREPARE stmt1 FROM 'INSERT INTO t1 VALUES(?)'; -SET @var1= x'8300'; -EXECUTE stmt1 USING @var1; -SHOW BINLOG EVENTS FROM 98; -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 98 Query 1 188 use `test`; CREATE TABLE t1(f1 blob) -master-bin.000001 188 User var 1 227 @`var1`=_binary 0x8300 COLLATE binary -master-bin.000001 227 Query 1 323 use `test`; INSERT INTO t1 VALUES(@'var1') -SELECT HEX(f1) FROM t1; -HEX(f1) -8300 -DROP table t1; From 683c1e6ad7ace035891c0f5fbd31c2ad0820f6ad Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Mar 2007 18:16:17 +0400 Subject: [PATCH 44/46] After merge fix mysql-test/t/rpl_loaddata_simple.test: Rename: mysql-test/t/rpl_loaddata2.test -> mysql-test/t/rpl_loaddata_simple.test mysql-test/r/rpl_loaddata_simple.result: Rename: mysql-test/r/rpl_loaddata2.result -> mysql-test/r/rpl_loaddata_simple.result --- mysql-test/r/ctype_cp932_binlog_stm.result | 12 +- mysql-test/r/mysqlbinlog.result | 15 +- mysql-test/r/rpl_loaddata_simple.result | 153 +++++++++++++++++++++ mysql-test/t/ctype_cp932_binlog_stm.test | 2 +- mysql-test/t/mysqlbinlog.test | 7 +- mysql-test/t/rpl_loaddata_simple.test | 14 ++ sql/log_event.cc | 8 +- 7 files changed, 190 insertions(+), 21 deletions(-) create mode 100644 mysql-test/r/rpl_loaddata_simple.result create mode 100644 mysql-test/t/rpl_loaddata_simple.test diff --git a/mysql-test/r/ctype_cp932_binlog_stm.result b/mysql-test/r/ctype_cp932_binlog_stm.result index ef1067e7c5d..ef024e2fa20 100644 --- a/mysql-test/r/ctype_cp932_binlog_stm.result +++ b/mysql-test/r/ctype_cp932_binlog_stm.result @@ -30,17 +30,17 @@ HEX(s1) HEX(s2) d 466F6F2773206120426172 ED40ED41ED42 47.93 DROP PROCEDURE bug18293| DROP TABLE t4| -SHOW BINLOG EVENTS FROM 397| +SHOW BINLOG EVENTS FROM 406| Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 397 Query 1 560 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, +master-bin.000001 406 Query 1 572 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, s2 CHAR(50) CHARACTER SET cp932, d DECIMAL(10,2)) -master-bin.000001 560 Query 1 805 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), +master-bin.000001 572 Query 1 820 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), IN ins2 CHAR(50) CHARACTER SET cp932, IN ind DECIMAL(10,2)) BEGIN INSERT INTO t4 VALUES (ins1, ins2, ind); END -master-bin.000001 805 Query 1 1021 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) -master-bin.000001 1021 Query 1 1107 use `test`; DROP PROCEDURE bug18293 -master-bin.000001 1107 Query 1 1183 use `test`; DROP TABLE t4 +master-bin.000001 820 Query 1 1039 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) +master-bin.000001 1039 Query 1 1128 use `test`; DROP PROCEDURE bug18293 +master-bin.000001 1128 Query 1 1207 use `test`; DROP TABLE t4 diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index db47e70e9e9..73e60f833c7 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -308,26 +308,27 @@ SET @@session.sql_mode=0/*!*/; SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; create table t1 (a varchar(64) character set utf8)/*!*/; SET TIMESTAMP=1000000000/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-6-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-6-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=7/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-7-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-7-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=DEFAULT/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-8-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-8-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-9-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-9-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=7/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-a-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-a-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; SET @@session.collation_database=DEFAULT/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-b-0' INTO table t1/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-b-0' INTO table t1/*!*/; SET TIMESTAMP=1000000000/*!*/; -load data LOCAL INFILE '/home/bar/mysql-5.0.b15126/mysql-test/var/tmp/SQL_LOAD_MB-c-0' INTO table t1 character set koi8r/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-c-0' INTO table t1 character set koi8r/*!*/; SET TIMESTAMP=1000000000/*!*/; drop table t1/*!*/; DELIMITER ; # End of log file ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; +flush logs; diff --git a/mysql-test/r/rpl_loaddata_simple.result b/mysql-test/r/rpl_loaddata_simple.result new file mode 100644 index 00000000000..196093211b6 --- /dev/null +++ b/mysql-test/r/rpl_loaddata_simple.result @@ -0,0 +1,153 @@ +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 (word CHAR(20) NOT NULL); +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t1; +SELECT * FROM t1 ORDER BY word; +word +Aarhus +Aarhus +Aaron +Aaron +Ababa +Ababa +aback +aback +abaft +abaft +abandon +abandon +abandoned +abandoned +abandoning +abandoning +abandonment +abandonment +abandons +abandons +abase +abased +abasement +abasements +abases +abash +abashed +abashes +abashing +abasing +abate +abated +abatement +abatements +abater +abates +abating +Abba +abbe +abbey +abbeys +abbot +abbots +Abbott +abbreviate +abbreviated +abbreviates +abbreviating +abbreviation +abbreviations +Abby +abdomen +abdomens +abdominal +abduct +abducted +abduction +abductions +abductor +abductors +abducts +Abe +abed +Abel +Abelian +Abelson +Aberdeen +Abernathy +aberrant +aberration +SELECT * FROM t1 ORDER BY word; +word +Aarhus +Aarhus +Aaron +Aaron +Ababa +Ababa +aback +aback +abaft +abaft +abandon +abandon +abandoned +abandoned +abandoning +abandoning +abandonment +abandonment +abandons +abandons +abase +abased +abasement +abasements +abases +abash +abashed +abashes +abashing +abasing +abate +abated +abatement +abatements +abater +abates +abating +Abba +abbe +abbey +abbeys +abbot +abbots +Abbott +abbreviate +abbreviated +abbreviates +abbreviating +abbreviation +abbreviations +Abby +abdomen +abdomens +abdominal +abduct +abducted +abduction +abductions +abductor +abductors +abducts +Abe +abed +Abel +Abelian +Abelson +Aberdeen +Abernathy +aberrant +aberration +drop table t1; diff --git a/mysql-test/t/ctype_cp932_binlog_stm.test b/mysql-test/t/ctype_cp932_binlog_stm.test index 6b591fbe5f5..9111c4ad369 100644 --- a/mysql-test/t/ctype_cp932_binlog_stm.test +++ b/mysql-test/t/ctype_cp932_binlog_stm.test @@ -22,7 +22,7 @@ CALL bug18293("Foo's a Bar", _cp932 0xED40ED41ED42, 47.93)| SELECT HEX(s1),HEX(s2),d FROM t4| DROP PROCEDURE bug18293| DROP TABLE t4| -SHOW BINLOG EVENTS FROM 397| +SHOW BINLOG EVENTS FROM 406| delimiter ;| # End of 5.0 tests diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index 0d20b1061bf..852cc6d2e36 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -214,7 +214,8 @@ load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8 select hex(a) from t1; drop table t1; flush logs; ---exec $MYSQL_BINLOG --short-form $MYSQLTEST_VARDIR/log/master-bin.000011 +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--exec $MYSQL_BINLOG --short-form $MYSQLTEST_VARDIR/log/master-bin.000012 # End of 5.0 tests @@ -223,7 +224,7 @@ flush logs; # flush logs; --error 1 ---exec $MYSQL_BINLOG $MYSQLTEST_VARDIR/log/master-bin.000012 >/dev/null 2>/dev/null ---exec $MYSQL_BINLOG --force-if-open $MYSQLTEST_VARDIR/log/master-bin.000012 >/dev/null 2>/dev/null +--exec $MYSQL_BINLOG $MYSQLTEST_VARDIR/log/master-bin.000014 >/dev/null 2>/dev/null +--exec $MYSQL_BINLOG --force-if-open $MYSQLTEST_VARDIR/log/master-bin.000014 >/dev/null 2>/dev/null # End of 5.1 tests diff --git a/mysql-test/t/rpl_loaddata_simple.test b/mysql-test/t/rpl_loaddata_simple.test new file mode 100644 index 00000000000..9e908cac677 --- /dev/null +++ b/mysql-test/t/rpl_loaddata_simple.test @@ -0,0 +1,14 @@ +--source include/master-slave.inc + +CREATE TABLE t1 (word CHAR(20) NOT NULL); +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t1; +SELECT * FROM t1 ORDER BY word; +sync_slave_with_master; + +# Check +SELECT * FROM t1 ORDER BY word; + +# Cleanup +connection master; +drop table t1; +sync_slave_with_master; diff --git a/sql/log_event.cc b/sql/log_event.cc index f161efe6492..9289cf9b12c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1865,11 +1865,11 @@ void Query_log_event::print_query_header(IO_CACHE* file, if (charset_database_number != print_event_info->charset_database_number) { if (charset_database_number) - fprintf(file, "SET @@session.collation_database=%d%s\n", - charset_database_number, print_event_info->delimiter); + my_b_printf(file, "SET @@session.collation_database=%d%s\n", + charset_database_number, print_event_info->delimiter); else - fprintf(file, "SET @@session.collation_database=DEFAULT%s\n", - print_event_info->delimiter); + my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n", + print_event_info->delimiter); print_event_info->charset_database_number= charset_database_number; } } From 17b3cbe81b1b22382198279497c90071499d5b08 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Mar 2007 16:17:48 +0100 Subject: [PATCH 45/46] Post-merge fix --- sql/sql_insert.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 5a3f0a7b261..f58d08b8dea 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -364,6 +364,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, Name_resolution_context_state ctx_state; #ifndef EMBEDDED_LIBRARY char *query= thd->query; +#endif /* log_on is about delayed inserts only. By default, both logs are enabled (this won't cause problems if the server @@ -371,7 +372,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, */ bool log_on= ((thd->options & OPTION_BIN_LOG) || (!(thd->security_ctx->master_access & SUPER_ACL))); -#endif thr_lock_type lock_type = table_list->lock_type; Item *unused_conds= 0; DBUG_ENTER("mysql_insert"); From 4268313e50e8ebe69e1baaef4d84f54c2764dfb1 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Mar 2007 10:35:04 +0100 Subject: [PATCH 46/46] Removing global events_till_disconnect variable as it is replaced by a member of master info structure. --- sql/slave.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index ea8e9e404d4..6f62f74647a 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -54,9 +54,6 @@ ulonglong relay_log_space_limit = 0; int disconnect_slave_event_count = 0, abort_slave_event_count = 0; int events_till_abort = -1; -#ifndef DBUG_OFF -static int events_till_disconnect = -1; -#endif typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE;