From 93e8fe1067a28582b158d9db3773d3ad46970b1a Mon Sep 17 00:00:00 2001 From: "aelkin@mysql.com" <> Date: Mon, 29 May 2006 18:01:58 +0300 Subject: [PATCH 01/32] Bug#19188: incorrect temporary table name of DROP query in replication merge with 5.0. specific test case in inside of #17263 section (grave quoted name). --- mysql-test/r/rpl_temporary.result | 12 +- mysql-test/t/rpl_temporary.test | 28 ++--- sql/mysql_priv.h | 10 ++ sql/sql_base.cc | 175 ++++++++++++++---------------- 4 files changed, 115 insertions(+), 110 deletions(-) diff --git a/mysql-test/r/rpl_temporary.result b/mysql-test/r/rpl_temporary.result index 7f6fa17e11c..8524fe8aa3d 100644 --- a/mysql-test/r/rpl_temporary.result +++ b/mysql-test/r/rpl_temporary.result @@ -88,15 +88,21 @@ f 1 drop temporary table t4; drop table t5; -set @session.pseudo_thread_id=100; +set @@session.pseudo_thread_id=100; create temporary table t101 (id int); create temporary table t102 (id int); -set @session.pseudo_thread_id=200; +set @@session.pseudo_thread_id=200; create temporary table t201 (id int); -create temporary table `#not_user_table_prefixed_with_hash_sign_no_harm` (id int); +create temporary table `t``201` (id int); +create temporary table `#sql_not_user_table202` (id int); +set @@session.pseudo_thread_id=300; +create temporary table t301 (id int); +create temporary table t302 (id int); +create temporary table `#sql_not_user_table303` (id int); create table t1(f int); insert into t1 values (1); select * from t1 /* must be 1 */; f 1 drop table t1; +End of 5.1 tests diff --git a/mysql-test/t/rpl_temporary.test b/mysql-test/t/rpl_temporary.test index 93e22e74597..800491d13e3 100644 --- a/mysql-test/t/rpl_temporary.test +++ b/mysql-test/t/rpl_temporary.test @@ -165,24 +165,19 @@ drop table t5; # value was set up at the moment of temp table creation # connection con1; -set @session.pseudo_thread_id=100; +set @@session.pseudo_thread_id=100; create temporary table t101 (id int); create temporary table t102 (id int); -set @session.pseudo_thread_id=200; +set @@session.pseudo_thread_id=200; create temporary table t201 (id int); -create temporary table `#not_user_table_prefixed_with_hash_sign_no_harm` (id int); - -# -# Don't kill our own connection to the server as -# the result code differs depending on platform. -# -# Select the id to kill into a variable of mysqltest -let $con1_id= `select connection_id()`; -# Switch connection to avoid killing our own connection -connection master; ---disable_query_log -eval kill $con1_id; ---enable_query_log +create temporary table `t``201` (id int); +# emulate internal temp table not to come to binlog +create temporary table `#sql_not_user_table202` (id int); +set @@session.pseudo_thread_id=300; +create temporary table t301 (id int); +create temporary table t302 (id int); +create temporary table `#sql_not_user_table303` (id int); +disconnect con1; #now do something to show that slave is ok after DROP temp tables connection master; @@ -195,4 +190,5 @@ select * from t1 /* must be 1 */; connection master; drop table t1; -# End of 5.1 tests +--echo End of 5.1 tests + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 127aa6c5acd..428723ff31f 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1942,6 +1942,16 @@ inline int hexchar_to_int(char c) return -1; } +/* + is_user_table() + return true if the table was created explicitly +*/ + +inline bool is_user_table(TABLE * table) +{ + const char *name= table->s->table_name.str; + return strncmp(name, tmp_file_prefix, tmp_file_prefix_length); +} /* Some functions that are different in the embedded library and the normal diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8ea53747b40..32d84043d5b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1183,137 +1183,130 @@ static inline uint tmpkeyval(THD *thd, TABLE *table) void close_temporary_tables(THD *thd) { - TABLE *next, - *prev_table /* prev link is not maintained in TABLE's double-linked list */, - *table; - char *query= (gptr) 0, *end; - uint query_buf_size, max_names_len; - bool found_user_tables; - + TABLE *table; if (!thd->temporary_tables) return; - - LINT_INIT(end); - query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS - /* - insertion sort of temp tables by pseudo_thread_id to build ordered list + if (!mysql_bin_log.is_open() || thd->current_stmt_binlog_row_based) + { + for (table= thd->temporary_tables; table; table= table->next) + { + close_temporary(table, 1, 1); + } + thd->temporary_tables= 0; + return; + } + + TABLE *next, + *prev_table /* prev link is not maintained in TABLE's double-linked list */; + bool was_quote_show= true; /* to assume thd->options has OPTION_QUOTE_SHOW_CREATE */ + // Better add "if exists", in case a RESET MASTER has been done + const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "; + uint stub_len= sizeof(stub) - 1; + char buf[256]; + memcpy(buf, stub, stub_len); + String s_query= String(buf, sizeof(buf), system_charset_info); + bool found_user_tables= false; + LINT_INIT(next); + + /* + insertion sort of temp tables by pseudo_thread_id to build ordered list of sublists of equal pseudo_thread_id */ - for (prev_table= thd->temporary_tables, - table= prev_table->next, - found_user_tables= (prev_table->s->table_name.str[0] != '#'); + + for (prev_table= thd->temporary_tables, table= prev_table->next; table; prev_table= table, table= table->next) { - TABLE *prev_sorted /* same as for prev_table */, - *sorted; - /* - table not created directly by the user is moved to the tail. - Fixme/todo: nothing (I checked the manual) prevents user to create temp - with `#' - */ - if (table->s->table_name.str[0] == '#') - continue; - else + TABLE *prev_sorted /* same as for prev_table */, *sorted; + if (is_user_table(table)) { - found_user_tables = 1; - } - for (prev_sorted= NULL, sorted= thd->temporary_tables; sorted != table; - prev_sorted= sorted, sorted= sorted->next) - { - if (sorted->s->table_name.str[0] == '#' || tmpkeyval(thd, sorted) > tmpkeyval(thd, table)) + if (!found_user_tables) + found_user_tables= true; + for (prev_sorted= NULL, sorted= thd->temporary_tables; sorted != table; + prev_sorted= sorted, sorted= sorted->next) { - /* move into the sorted part of the list from the unsorted */ - prev_table->next= table->next; - table->next= sorted; - if (prev_sorted) + if (!is_user_table(sorted) || + tmpkeyval(thd, sorted) > tmpkeyval(thd, table)) { - prev_sorted->next= table; + /* move into the sorted part of the list from the unsorted */ + prev_table->next= table->next; + table->next= sorted; + if (prev_sorted) + { + prev_sorted->next= table; + } + else + { + thd->temporary_tables= table; + } + table= prev_table; + break; } - else - { - thd->temporary_tables= table; - } - table= prev_table; - break; } } - } - /* - calc query_buf_size as max per sublists, one sublist per pseudo thread id. - Also stop at first occurence of `#'-named table that starts - all implicitly created temp tables - */ - for (max_names_len= 0, table=thd->temporary_tables; - table && table->s->table_name.str[0] != '#'; - table=table->next) - { - uint tmp_names_len; - for (tmp_names_len= table->s->table_cache_key.length + 1; - table->next && table->s->table_name.str[0] != '#' && - tmpkeyval(thd, table) == tmpkeyval(thd, table->next); - table=table->next) - { - /* - We are going to add 4 ` around the db/table names, so 1 might not look - enough; indeed it is enough, because table->s->table_cache_key.length is - greater (by 8, because of server_id and thread_id) than db||table. - */ - tmp_names_len += table->next->s->table_cache_key.length + 1; - } - if (tmp_names_len > max_names_len) max_names_len= tmp_names_len; } - - /* allocate */ - if (found_user_tables && mysql_bin_log.is_open() && - !thd->current_stmt_binlog_row_based && - (query = alloc_root(thd->mem_root, query_buf_size+= max_names_len))) - // Better add "if exists", in case a RESET MASTER has been done - end= strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "); + + /* We always quote db,table names though it is slight overkill */ + if (found_user_tables && + !(was_quote_show= (thd->options & OPTION_QUOTE_SHOW_CREATE))) + { + thd->options |= OPTION_QUOTE_SHOW_CREATE; + } /* scan sorted tmps to generate sequence of DROP */ - for (table=thd->temporary_tables; table; table= next) + for (table= thd->temporary_tables; table; table= next) { - if (query // we might be out of memory, but this is not fatal - && table->s->table_name.str[0] != '#') + if (is_user_table(table)) { - char *end_cur; /* Set pseudo_thread_id to be that of the processed table */ thd->variables.pseudo_thread_id= tmpkeyval(thd, table); /* Loop forward through all tables within the sublist of common pseudo_thread_id to create single DROP query */ - for (end_cur= end; - table && table->s->table_name.str[0] != '#' && + for (s_query.length(stub_len); + table && is_user_table(table) && tmpkeyval(thd, table) == thd->variables.pseudo_thread_id; table= next) { - end_cur= strxmov(end_cur, "`", table->s->db.str, "`.`", - table->s->table_name.str, "`,", NullS); + /* + We are going to add 4 ` around the db/table names and possible more + due to special characters in the names + */ + append_identifier(thd, &s_query, table->s->db.str, strlen(table->s->db.str)); + s_query.q_append('.'); + append_identifier(thd, &s_query, table->s->table_name.str, + strlen(table->s->table_name.str)); + s_query.q_append(','); next= table->next; close_temporary(table, 1, 1); } thd->clear_error(); - /* The -1 is to remove last ',' */ - Query_log_event qinfo(thd, query, (ulong)(end_cur - query) - 1, 0, FALSE); + CHARSET_INFO *cs_save= thd->variables.character_set_client; + thd->variables.character_set_client= system_charset_info; + Query_log_event qinfo(thd, s_query.ptr(), + s_query.length() - 1 /* to remove trailing ',' */, + 0, FALSE); + thd->variables.character_set_client= cs_save; /* - Imagine the thread had created a temp table, then was doing a SELECT, - and the SELECT was killed. Then it's not clever to mark the statement - above as "killed", because it's not really a statement updating data, - and there are 99.99% chances it will succeed on slave. If a real update - (one updating a persistent table) was killed on the master, then this - real update will be logged with error_code=killed, rightfully causing - the slave to stop. + Imagine the thread had created a temp table, then was doing a SELECT, and + the SELECT was killed. Then it's not clever to mark the statement above as + "killed", because it's not really a statement updating data, and there + are 99.99% chances it will succeed on slave. + If a real update (one updating a persistent table) was killed on the + master, then this real update will be logged with error_code=killed, + rightfully causing the slave to stop. */ qinfo.error_code= 0; mysql_bin_log.write(&qinfo); } - else + else { next= table->next; close_temporary(table, 1, 1); } } + if (!was_quote_show) + thd->options &= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */ thd->temporary_tables=0; } From ff179e371f6ed583fcefe7e4b53917d2fe4f7c93 Mon Sep 17 00:00:00 2001 From: "guilhem@mysql.com" <> Date: Tue, 20 Jun 2006 10:40:36 +0200 Subject: [PATCH 02/32] Fix for BUG#20522 "RBR: CREATE TEMPORARY TABLE SELECT writes to binlog though unneeded". It's indeed unneeded, as slave is only interested in permanent tables, and permanent tables don't depend on temporary tables when in row-based binlogging mode. And other CREATE TEMPORARY TABLE (referring no table or with LIKE) already don't write the CREATE to binlog in row-based mode. --- mysql-test/r/rpl_row_create_table.result | 1 + mysql-test/t/rpl_row_create_table.test | 1 + sql/sql_class.h | 3 ++- sql/sql_insert.cc | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/rpl_row_create_table.result b/mysql-test/r/rpl_row_create_table.result index b3a8f5e1a0f..75807c8bfc9 100644 --- a/mysql-test/r/rpl_row_create_table.result +++ b/mysql-test/r/rpl_row_create_table.result @@ -178,6 +178,7 @@ CREATE TABLE t8 LIKE t4; CREATE TABLE t9 LIKE tt4; CREATE TEMPORARY TABLE tt5 LIKE t4; CREATE TEMPORARY TABLE tt6 LIKE tt4; +CREATE TEMPORARY TABLE tt7 SELECT 1; **** On Master **** SHOW CREATE TABLE t8; Table t8 diff --git a/mysql-test/t/rpl_row_create_table.test b/mysql-test/t/rpl_row_create_table.test index 0cb0fd766a3..4f01d7e5459 100644 --- a/mysql-test/t/rpl_row_create_table.test +++ b/mysql-test/t/rpl_row_create_table.test @@ -97,6 +97,7 @@ CREATE TABLE t8 LIKE t4; CREATE TABLE t9 LIKE tt4; CREATE TEMPORARY TABLE tt5 LIKE t4; CREATE TEMPORARY TABLE tt6 LIKE tt4; +CREATE TEMPORARY TABLE tt7 SELECT 1; --echo **** On Master **** --query_vertical SHOW CREATE TABLE t8 --query_vertical SHOW CREATE TABLE t9 diff --git a/sql/sql_class.h b/sql/sql_class.h index 723dad715bd..b5328d4c56b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1610,7 +1610,8 @@ public: virtual bool can_rollback_data() { return 1; } // Needed for access from local class MY_HOOKS in prepare(), since thd is proteted. - THD *get_thd(void) { return thd; } + const THD *get_thd(void) { return thd; } + const HA_CREATE_INFO *get_create_info() { return create_info; }; }; #include diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index f1f97400283..dc27adb853a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2714,7 +2714,8 @@ public: MY_HOOKS(select_create *x) : ptr(x) { } virtual void do_prelock(TABLE **tables, uint count) { - if (ptr->get_thd()->current_stmt_binlog_row_based) + if (ptr->get_thd()->current_stmt_binlog_row_based && + !(ptr->get_create_info()->options & HA_LEX_CREATE_TMP_TABLE)) ptr->binlog_show_create_table(tables, count); } From 78a9adb659ee458c4969ba1648a9eaf63ea9579e Mon Sep 17 00:00:00 2001 From: "lars@mysql.com" <> Date: Fri, 30 Jun 2006 11:31:43 +0200 Subject: [PATCH 03/32] Changed the number of test iterations since this makes test time go from 623 seconds to 11 seconds on AMD64. This is because we have no native atomic implementation on AMD64, so the rwlock-based implementation is used, which is a bit slow. It will be probably be optimized for AMD64 quite soon, but for now this test case will do fewer iterations. --- unittest/mysys/my_atomic-t.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unittest/mysys/my_atomic-t.c b/unittest/mysys/my_atomic-t.c index 71408ce957f..4e2e496c3b1 100644 --- a/unittest/mysys/my_atomic-t.c +++ b/unittest/mysys/my_atomic-t.c @@ -166,9 +166,9 @@ int main() pthread_cond_init(&cond, 0); my_atomic_rwlock_init(&rwl); - test_atomic("my_atomic_add32", test_atomic_add_handler, 100,1000000); - test_atomic("my_atomic_swap32", test_atomic_swap_handler, 100,1000000); - test_atomic("my_atomic_cas32", test_atomic_cas_handler, 100,1000000); + test_atomic("my_atomic_add32", test_atomic_add_handler, 100,10000); + test_atomic("my_atomic_swap32", test_atomic_swap_handler, 100,10000); + test_atomic("my_atomic_cas32", test_atomic_cas_handler, 100,10000); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); From 92fd2d1c2ea0b3a362684aa2c63c145772cec43f Mon Sep 17 00:00:00 2001 From: "aivanov@mysql.com" <> Date: Mon, 3 Jul 2006 23:28:19 +0400 Subject: [PATCH 04/32] BUG#19209 "Test 'rpl_openssl' hangs on Windows" Enabling rpl_openssl.test for Windows to check that currently it still hangs (because I can't reproduce this on my machine). --- mysql-test/t/rpl_openssl.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/rpl_openssl.test b/mysql-test/t/rpl_openssl.test index af70a1a9453..e18ddce9c92 100644 --- a/mysql-test/t/rpl_openssl.test +++ b/mysql-test/t/rpl_openssl.test @@ -1,6 +1,6 @@ # TODO: THIS TEST DOES NOT WORK ON WINDOWS # This should be fixed. ---source include/not_windows.inc +#--source include/not_windows.inc source include/have_openssl.inc; source include/master-slave.inc; From 163c4d747c337cd7bfc10b0c2d87b2041301cb01 Mon Sep 17 00:00:00 2001 From: "aivanov@mysql.com" <> Date: Tue, 4 Jul 2006 01:07:49 +0400 Subject: [PATCH 05/32] BUG#19209 "Test 'rpl_openssl' hangs on Windows" Disabling 'rpl_openssl'. --- mysql-test/t/rpl_openssl.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/rpl_openssl.test b/mysql-test/t/rpl_openssl.test index e18ddce9c92..af70a1a9453 100644 --- a/mysql-test/t/rpl_openssl.test +++ b/mysql-test/t/rpl_openssl.test @@ -1,6 +1,6 @@ # TODO: THIS TEST DOES NOT WORK ON WINDOWS # This should be fixed. -#--source include/not_windows.inc +--source include/not_windows.inc source include/have_openssl.inc; source include/master-slave.inc; From a43c4b0265ac89b7371b44a3940d37430a87b21a Mon Sep 17 00:00:00 2001 From: "guilhem@mysql.com" <> Date: Wed, 5 Jul 2006 14:41:35 +0200 Subject: [PATCH 06/32] Fix for BUG#20188 "REPLACE or ON DUPLICATE KEY UPDATE in auto_increment breaks binlog": if slave's table had a higher auto_increment counter than master's (even though all rows of the two tables were identical), then in some cases, REPLACE and INSERT ON DUPLICATE KEY UPDATE failed to replicate statement-based (it inserted different values on slave from on master). write_record() contained a "thd->next_insert_id=0" to force an adjustment of thd->next_insert_id after the update or replacement. But it is this assigment introduced indeterminism of the statement on the slave, thus the bug. For ON DUPLICATE, we replace that assignment by a call to handler::adjust_next_insert_id_after_explicit_value() which is deterministic (does not depend on slave table's autoinc counter). For REPLACE, this assignment can simply be removed (as REPLACE can't insert a number larger than thd->next_insert_id). We also move a too early restore_auto_increment() down to when we really know that we can restore the value. --- mysql-test/r/rpl_insert_id.result | 65 +++++++++++++++++++++++++++++++ mysql-test/t/rpl_insert_id.test | 63 ++++++++++++++++++++++++++++++ sql/handler.cc | 32 +++++++++------ sql/handler.h | 1 + sql/sql_insert.cc | 20 ++++------ 5 files changed, 157 insertions(+), 24 deletions(-) diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index b11f1b92020..cd66e8727c1 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -132,3 +132,68 @@ id last_id drop function bug15728; drop function bug15728_insert; drop table t1, t2; +create table t1 (n int primary key auto_increment not null, +b int, unique(b)); +set sql_log_bin=0; +insert into t1 values(null,100); +replace into t1 values(null,50),(null,100),(null,150); +select * from t1 order by n; +n b +2 50 +3 100 +4 150 +truncate table t1; +set sql_log_bin=1; +insert into t1 values(null,100); +select * from t1 order by n; +n b +1 100 +insert into t1 values(null,200),(null,300); +delete from t1 where b <> 100; +select * from t1 order by n; +n b +1 100 +replace into t1 values(null,100),(null,350); +select * from t1 order by n; +n b +2 100 +3 350 +select * from t1 order by n; +n b +2 100 +3 350 +insert into t1 values (NULL,400),(3,500),(NULL,600) on duplicate key UPDATE n=1000; +select * from t1 order by n; +n b +2 100 +4 400 +1000 350 +1001 600 +select * from t1 order by n; +n b +2 100 +4 400 +1000 350 +1001 600 +drop table t1; +create table t1 (n int primary key auto_increment not null, +b int, unique(b)); +insert into t1 values(null,100); +select * from t1 order by n; +n b +1 100 +insert into t1 values(null,200),(null,300); +delete from t1 where b <> 100; +select * from t1 order by n; +n b +1 100 +insert into t1 values(null,100),(null,350) on duplicate key update n=2; +select * from t1 order by n; +n b +2 100 +3 350 +select * from t1 order by n; +n b +2 100 +3 350 +drop table t1; diff --git a/mysql-test/t/rpl_insert_id.test b/mysql-test/t/rpl_insert_id.test index e038829760d..90a123cf5dc 100644 --- a/mysql-test/t/rpl_insert_id.test +++ b/mysql-test/t/rpl_insert_id.test @@ -147,6 +147,69 @@ drop function bug15728; drop function bug15728_insert; drop table t1, t2; +# test of BUG#20188 REPLACE or ON DUPLICATE KEY UPDATE in +# auto_increment breaks binlog + +create table t1 (n int primary key auto_increment not null, +b int, unique(b)); + +# First, test that we do not call restore_auto_increment() too early +# in write_record(): +set sql_log_bin=0; +insert into t1 values(null,100); +replace into t1 values(null,50),(null,100),(null,150); +select * from t1 order by n; +truncate table t1; +set sql_log_bin=1; + +insert into t1 values(null,100); +select * from t1 order by n; +sync_slave_with_master; +# make slave's table autoinc counter bigger +insert into t1 values(null,200),(null,300); +delete from t1 where b <> 100; +# check that slave's table content is identical to master +select * from t1 order by n; +# only the auto_inc counter differs. + +connection master; +replace into t1 values(null,100),(null,350); +select * from t1 order by n; +sync_slave_with_master; +select * from t1 order by n; + +# Same test as for REPLACE, but for ON DUPLICATE KEY UPDATE + +# We first check that if we update a row using a value larger than the +# table's counter, the counter for next row is bigger than the +# after-value of the updated row. +connection master; +insert into t1 values (NULL,400),(3,500),(NULL,600) on duplicate key UPDATE n=1000; +select * from t1 order by n; +sync_slave_with_master; +select * from t1 order by n; + +# and now test for the bug: +connection master; +drop table t1; +create table t1 (n int primary key auto_increment not null, +b int, unique(b)); +insert into t1 values(null,100); +select * from t1 order by n; +sync_slave_with_master; +insert into t1 values(null,200),(null,300); +delete from t1 where b <> 100; +select * from t1 order by n; + +connection master; +insert into t1 values(null,100),(null,350) on duplicate key update n=2; +select * from t1 order by n; +sync_slave_with_master; +select * from t1 order by n; + +connection master; +drop table t1; + # End of 5.0 tests sync_slave_with_master; diff --git a/sql/handler.cc b/sql/handler.cc index b40934ea194..67f9e25703e 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1471,6 +1471,26 @@ next_insert_id(ulonglong nr,struct system_variables *variables) } +void handler::adjust_next_insert_id_after_explicit_value(ulonglong nr) +{ + /* + If we have set THD::next_insert_id previously and plan to insert an + explicitely-specified value larger than this, we need to increase + THD::next_insert_id to be greater than the explicit value. + */ + THD *thd= table->in_use; + if (thd->clear_next_insert_id && (nr >= thd->next_insert_id)) + { + if (thd->variables.auto_increment_increment != 1) + nr= next_insert_id(nr, &thd->variables); + else + nr++; + thd->next_insert_id= nr; + DBUG_PRINT("info",("next_insert_id: %lu", (ulong) nr)); + } +} + + /* Update the auto_increment field if necessary @@ -1547,17 +1567,7 @@ bool handler::update_auto_increment() /* Clear flag for next row */ /* Mark that we didn't generate a new value **/ auto_increment_column_changed=0; - - /* Update next_insert_id if we have already generated a value */ - if (thd->clear_next_insert_id && nr >= thd->next_insert_id) - { - if (variables->auto_increment_increment != 1) - nr= next_insert_id(nr, variables); - else - nr++; - thd->next_insert_id= nr; - DBUG_PRINT("info",("next_insert_id: %lu", (ulong) nr)); - } + adjust_next_insert_id_after_explicit_value(nr); DBUG_RETURN(0); } if (!(nr= thd->next_insert_id)) diff --git a/sql/handler.h b/sql/handler.h index 31aac075a5e..ee4bd239657 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -563,6 +563,7 @@ public: {} virtual ~handler(void) { /* TODO: DBUG_ASSERT(inited == NONE); */ } int ha_open(const char *name, int mode, int test_if_locked); + void adjust_next_insert_id_after_explicit_value(ulonglong nr); bool update_auto_increment(); virtual void print_error(int error, myf errflag); virtual bool get_error_message(int error, String *buf); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 26f3b6f5faa..fc3ea264ae7 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -955,7 +955,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) uint key_nr; if (error != HA_WRITE_SKIP) goto err; - table->file->restore_auto_increment(); if ((int) (key_nr = table->file->get_dup_key(error)) < 0) { error=HA_WRITE_SKIP; /* Database can't find key */ @@ -1028,20 +1027,20 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (res == VIEW_CHECK_ERROR) goto before_trg_err; - if (thd->clear_next_insert_id) - { - /* Reset auto-increment cacheing if we do an update */ - thd->clear_next_insert_id= 0; - thd->next_insert_id= 0; - } 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; } info->updated++; + if (table->next_number_field) + table->file->adjust_next_insert_id_after_explicit_value(table->next_number_field->val_int()); + trg_error= (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE)); @@ -1067,12 +1066,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, TRUE)) goto before_trg_err; - if (thd->clear_next_insert_id) - { - /* Reset auto-increment cacheing if we do an update */ - thd->clear_next_insert_id= 0; - thd->next_insert_id= 0; - } if ((error=table->file->update_row(table->record[1], table->record[0]))) goto err; @@ -1142,6 +1135,7 @@ err: table->file->print_error(error,MYF(0)); before_trg_err: + table->file->restore_auto_increment(); if (key) my_safe_afree(key, table->s->max_unique_length, MAX_KEY_LENGTH); DBUG_RETURN(1); From fd92d8072aeacc94362716d43b8e449c7330091a Mon Sep 17 00:00:00 2001 From: "mats@mysql.com" <> Date: Wed, 5 Jul 2006 16:58:56 +0200 Subject: [PATCH 07/32] BUG#20821 (INSERT DELAYED fails to write some rows to binlog): Reverting to old behaviour of writing the query before all rows have been written. --- mysql-test/r/rpl_insert.result | 16 ++++ mysql-test/r/rpl_row_delayed_ins.result | 6 +- mysql-test/t/rpl_insert.test | 27 +++++++ sql/sql_class.cc | 1 + sql/sql_insert.cc | 98 +++++++++++++++---------- 5 files changed, 106 insertions(+), 42 deletions(-) create mode 100644 mysql-test/r/rpl_insert.result create mode 100644 mysql-test/t/rpl_insert.test diff --git a/mysql-test/r/rpl_insert.result b/mysql-test/r/rpl_insert.result new file mode 100644 index 00000000000..b0c44be017f --- /dev/null +++ b/mysql-test/r/rpl_insert.result @@ -0,0 +1,16 @@ +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 SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +CREATE TABLE t1 (id INT, name VARCHAR(64)); +SELECT COUNT(*) FROM mysqlslap.t1; +COUNT(*) +20000 +SELECT COUNT(*) FROM mysqlslap.t1; +COUNT(*) +20000 +DROP SCHEMA IF EXISTS mysqlslap; diff --git a/mysql-test/r/rpl_row_delayed_ins.result b/mysql-test/r/rpl_row_delayed_ins.result index 16001b96ac2..31fffeb59cc 100644 --- a/mysql-test/r/rpl_row_delayed_ins.result +++ b/mysql-test/r/rpl_row_delayed_ins.result @@ -17,8 +17,10 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 4 Format_desc 1 102 Server ver: VERSION, Binlog ver: 4 master-bin.000001 102 Query 1 222 use `test`; create table t1(a int not null primary key) engine=myisam master-bin.000001 222 Table_map 1 261 table_id: # (test.t1) -master-bin.000001 261 Write_rows 1 305 table_id: # flags: STMT_END_F -master-bin.000001 305 Query 1 380 use `test`; flush tables +master-bin.000001 261 Write_rows 1 295 table_id: # flags: STMT_END_F +master-bin.000001 295 Table_map 1 334 table_id: # (test.t1) +master-bin.000001 334 Write_rows 1 373 table_id: # flags: STMT_END_F +master-bin.000001 373 Query 1 448 use `test`; flush tables SELECT * FROM t1 ORDER BY a; a 1 diff --git a/mysql-test/t/rpl_insert.test b/mysql-test/t/rpl_insert.test new file mode 100644 index 00000000000..2271fc9ad8a --- /dev/null +++ b/mysql-test/t/rpl_insert.test @@ -0,0 +1,27 @@ + +# +# Bug#20821: INSERT DELAYED fails to write some rows to binlog +# + +--source include/master-slave.inc +--source include/not_embedded.inc +--source include/not_windows.inc + +--disable_warnings +CREATE SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +--enable_warnings + +CREATE TABLE t1 (id INT, name VARCHAR(64)); + +let $query = "INSERT INTO t1 VALUES (1, 'Dr. No'), (2, 'From Russia With Love'), (3, 'Goldfinger'), (4, 'Thunderball'), (5, 'You Only Live Twice')"; +--exec $MYSQL_SLAP --silent --concurrency=20 --iterations=200 --query=$query --delimiter=";" + +SELECT COUNT(*) FROM mysqlslap.t1; +sync_slave_with_master; +SELECT COUNT(*) FROM mysqlslap.t1; + +connection master; +DROP SCHEMA IF EXISTS mysqlslap; +sync_slave_with_master; + diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7fa0173ddea..3fe549f882a 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2717,6 +2717,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, bool is_trans, bool suppress_use) { DBUG_ENTER("THD::binlog_query"); + DBUG_PRINT("enter", ("qtype=%d, query='%s'", qtype, query)); DBUG_ASSERT(query && mysql_bin_log.is_open()); switch (qtype) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 7b017ad7317..41eb178c299 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -26,8 +26,8 @@ static int check_null_fields(THD *thd,TABLE *entry); #ifndef EMBEDDED_LIBRARY static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list); -static int write_delayed(THD *thd,TABLE *table, enum_duplicates dup, bool ignore, - char *query, uint query_length, bool log_on); +static int write_delayed(THD *thd, TABLE *table, enum_duplicates dup, + LEX_STRING query, bool ignore, bool log_on); static void end_delayed_insert(THD *thd); pthread_handler_t handle_delayed_insert(void *arg); static void unlink_blobs(register TABLE *table); @@ -511,7 +511,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, #ifndef EMBEDDED_LIBRARY if (lock_type == TL_WRITE_DELAYED) { - error=write_delayed(thd, table, duplic, ignore, query, thd->query_length, log_on); + LEX_STRING const st_query = { query, thd->query_length }; + error=write_delayed(thd, table, duplic, st_query, ignore, log_on); query=0; } else @@ -1237,11 +1238,16 @@ public: bool query_start_used,last_insert_id_used,insert_id_used, ignore, log_query; ulonglong last_insert_id; timestamp_auto_set_type timestamp_field_type; + LEX_STRING query; - delayed_row(enum_duplicates dup_arg, bool ignore_arg, bool log_query_arg) - :record(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg) {} + delayed_row(LEX_STRING const query_arg, enum_duplicates dup_arg, + bool ignore_arg, bool log_query_arg) + : record(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg), + query(query_arg) + {} ~delayed_row() { + x_free(query.str); x_free(record); } }; @@ -1249,9 +1255,6 @@ public: class delayed_insert :public ilink { uint locks_in_memory; - char *query; - ulong query_length; - ulong query_allocated; public: THD thd; TABLE *table; @@ -1265,7 +1268,7 @@ public: TABLE_LIST table_list; // Argument delayed_insert() - :locks_in_memory(0), query(0), query_length(0), query_allocated(0), + :locks_in_memory(0), table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0), group_count(0) { @@ -1291,7 +1294,6 @@ public: } ~delayed_insert() { - my_free(query, MYF(MY_WME|MY_ALLOW_ZERO_PTR)); /* The following is not really needed, but just for safety */ delayed_row *row; while ((row=rows.get())) @@ -1311,25 +1313,6 @@ public: VOID(pthread_cond_broadcast(&COND_thread_count)); /* Tell main we are ready */ } - int set_query(char const *q, ulong qlen) { - if (q && qlen > 0) - { - if (query_allocated < qlen + 1) - { - ulong const flags(MY_WME|MY_FREE_ON_ERROR|MY_ALLOW_ZERO_PTR); - query= my_realloc(query, qlen + 1, MYF(flags)); - if (query == 0) - return HA_ERR_OUT_OF_MEM; - query_allocated= qlen; - } - query_length= qlen; - memcpy(query, q, qlen + 1); - } - else - query_length= 0; - return 0; - } - /* The following is for checking when we can delete ourselves */ inline void lock() { @@ -1616,13 +1599,14 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) /* Put a question in queue */ -static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, - bool ignore, char *query, uint query_length, - bool log_on) +static int +write_delayed(THD *thd,TABLE *table, enum_duplicates duplic, + LEX_STRING query, bool ignore, bool log_on) { - delayed_row *row=0; + delayed_row *row; delayed_insert *di=thd->di; DBUG_ENTER("write_delayed"); + DBUG_PRINT("enter", ("query = '%s' length %u", query.str, query.length)); thd->proc_info="waiting for handler insert"; pthread_mutex_lock(&di->mutex); @@ -1630,13 +1614,28 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, pthread_cond_wait(&di->cond_client,&di->mutex); thd->proc_info="storing row into queue"; - if (thd->killed || !(row= new delayed_row(duplic, ignore, log_on))) + if (thd->killed) goto err; + /* + Take a copy of the query string, if there is any. The string will + be free'ed when the row is destroyed. If there is no query string, + we don't do anything special. + */ + + if (query.str) + if (!(query.str= my_strndup(query.str, MYF(MY_WME), query.length))) + goto err; + row= new delayed_row(query, duplic, ignore, log_on); + if (row == NULL) + { + my_free(query.str, MYF(MY_WME)); + goto err; + } + if (!(row->record= (char*) my_malloc(table->s->reclength, MYF(MY_WME)))) goto err; memcpy(row->record, table->record[0], table->s->reclength); - di->set_query(query, query_length); row->start_time= thd->start_time; row->query_start_used= thd->query_start_used; row->last_insert_id_used= thd->last_insert_id_used; @@ -1995,7 +1994,7 @@ bool delayed_insert::handle_inserts(void) if (thd.killed || table->s->version != refresh_version) { thd.killed= THD::KILL_CONNECTION; - max_rows= ~(ulong)0; // Do as much as possible + max_rows= ULONG_MAX; // Do as much as possible } /* @@ -2042,11 +2041,18 @@ bool delayed_insert::handle_inserts(void) thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); row->log_query = 0; } + if (using_ignore) { using_ignore=0; table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); } + + if (row->log_query && row->query.str != NULL && mysql_bin_log.is_open()) + thd.binlog_query(THD::ROW_QUERY_TYPE, + row->query.str, row->query.length, + FALSE, FALSE); + if (table->s->blob_fields) free_delayed_insert_blobs(table); thread_safe_sub(delayed_rows_in_use,1,&LOCK_delayed_status); @@ -2093,13 +2099,25 @@ bool delayed_insert::handle_inserts(void) pthread_cond_broadcast(&cond_client); // If waiting clients } } - thd.proc_info=0; pthread_mutex_unlock(&mutex); - /* After releasing the mutex, to prevent deadlocks. */ - if (mysql_bin_log.is_open()) - thd.binlog_query(THD::ROW_QUERY_TYPE, query, query_length, FALSE, FALSE); +#ifdef HAVE_ROW_BASED_REPLICATION + /* + We need to flush the pending event when using row-based + replication since the flushing normally done in binlog_query() is + not done last in the statement: for delayed inserts, the insert + statement is logged *before* all rows are inserted. + + We can flush the pending event without checking the thd->lock + since the delayed insert *thread* is not inside a stored function + or trigger. + + TODO: Move the logging to last in the sequence of rows. + */ + if (thd.current_stmt_binlog_row_based) + thd.binlog_flush_pending_rows_event(TRUE); +#endif /* HAVE_ROW_BASED_REPLICATION */ if ((error=table->file->extra(HA_EXTRA_NO_CACHE))) { // This shouldn't happen From 8f5689fd4fd0f8e36ad4b2cd3b93c881bf072275 Mon Sep 17 00:00:00 2001 From: "mats@mysql.com" <> Date: Wed, 5 Jul 2006 21:17:00 +0200 Subject: [PATCH 08/32] Switching to use INSERT DELAYED, which it should be, for rpl_insert. --- mysql-test/t/rpl_insert.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/rpl_insert.test b/mysql-test/t/rpl_insert.test index 2271fc9ad8a..c53cd60fa74 100644 --- a/mysql-test/t/rpl_insert.test +++ b/mysql-test/t/rpl_insert.test @@ -14,7 +14,7 @@ USE mysqlslap; CREATE TABLE t1 (id INT, name VARCHAR(64)); -let $query = "INSERT INTO t1 VALUES (1, 'Dr. No'), (2, 'From Russia With Love'), (3, 'Goldfinger'), (4, 'Thunderball'), (5, 'You Only Live Twice')"; +let $query = "INSERT DELAYED INTO t1 VALUES (1, 'Dr. No'), (2, 'From Russia With Love'), (3, 'Goldfinger'), (4, 'Thunderball'), (5, 'You Only Live Twice')"; --exec $MYSQL_SLAP --silent --concurrency=20 --iterations=200 --query=$query --delimiter=";" SELECT COUNT(*) FROM mysqlslap.t1; From 140b488c9605b5ccfdd6864df8f021ec7bb30773 Mon Sep 17 00:00:00 2001 From: "guilhem@mysql.com" <> Date: Thu, 6 Jul 2006 14:37:09 +0200 Subject: [PATCH 09/32] Fix for BUG#20524 "auto_increment_* not observed when inserting a too large value": the bug was that if MySQL generated a value for an auto_increment column, based on auto_increment_* variables, and this value was bigger than the column's max possible value, then that max possible value was inserted (after issuing a warning). But this didn't honour auto_increment_* variables (and so could cause conflicts in a master-master replication where one master is supposed to generated only even numbers, and the other only odd numbers), so now we "round down" this max possible value to honour auto_increment_* variables, before inserting it. --- mysql-test/r/rpl_auto_increment.result | 44 +++++++++++++++++++++ mysql-test/t/rpl_auto_increment.test | 40 ++++++++++++++++++- sql/handler.cc | 53 +++++++++++++++++++++++++- 3 files changed, 134 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/rpl_auto_increment.result b/mysql-test/r/rpl_auto_increment.result index 9eca51ad2d9..ea4815e5e64 100644 --- a/mysql-test/r/rpl_auto_increment.result +++ b/mysql-test/r/rpl_auto_increment.result @@ -183,3 +183,47 @@ a 32 42 drop table t1; +create table t1 (a tinyint not null auto_increment primary key) engine=myisam; +insert into t1 values(103); +set auto_increment_increment=11; +set auto_increment_offset=4; +insert into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +ERROR 23000: Duplicate entry '125' for key 1 +select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t1 order by a; +a mod(a-@@auto_increment_offset,@@auto_increment_increment) +103 0 +114 0 +125 0 +create table t2 (a tinyint unsigned not null auto_increment primary key) engine=myisam; +set auto_increment_increment=10; +set auto_increment_offset=1; +set insert_id=1000; +insert into t2 values(null); +Warnings: +Warning 1264 Out of range value adjusted for column 'a' at row 1 +select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t2 order by a; +a mod(a-@@auto_increment_offset,@@auto_increment_increment) +251 0 +create table t3 like t1; +set auto_increment_increment=1000; +set auto_increment_offset=700; +insert into t3 values(null); +Warnings: +Warning 1264 Out of range value adjusted for column 'a' at row 1 +select * from t3 order by a; +a +127 +select * from t1 order by a; +a +103 +114 +125 +select * from t2 order by a; +a +251 +select * from t3 order by a; +a +127 +drop table t1,t2,t3; diff --git a/mysql-test/t/rpl_auto_increment.test b/mysql-test/t/rpl_auto_increment.test index 71032404307..caa2b79feb5 100644 --- a/mysql-test/t/rpl_auto_increment.test +++ b/mysql-test/t/rpl_auto_increment.test @@ -96,9 +96,47 @@ select * from t1; sync_slave_with_master; select * from t1; + +# Test for BUG#20524 "auto_increment_* not observed when inserting +# a too large value". When an autogenerated value was bigger than the +# maximum possible value of the field, it was truncated to that max +# possible value, without being "rounded down" to still honour +# auto_increment_* variables. + +connection master; +drop table t1; +create table t1 (a tinyint not null auto_increment primary key) engine=myisam; +insert into t1 values(103); +set auto_increment_increment=11; +set auto_increment_offset=4; +insert into t1 values(null); +insert into t1 values(null); +--error 1062 +insert into t1 values(null); +select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t1 order by a; + +# same but with a larger value +create table t2 (a tinyint unsigned not null auto_increment primary key) engine=myisam; +set auto_increment_increment=10; +set auto_increment_offset=1; +set insert_id=1000; +insert into t2 values(null); +select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t2 order by a; + +# An offset so big that even first value does not fit +create table t3 like t1; +set auto_increment_increment=1000; +set auto_increment_offset=700; +insert into t3 values(null); +select * from t3 order by a; +sync_slave_with_master; +select * from t1 order by a; +select * from t2 order by a; +select * from t3 order by a; + connection master; -drop table t1; +drop table t1,t2,t3; # End cleanup sync_slave_with_master; diff --git a/sql/handler.cc b/sql/handler.cc index b40934ea194..9259bc85aa0 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1471,6 +1471,46 @@ next_insert_id(ulonglong nr,struct system_variables *variables) } +/* + Computes the largest number X: + - smaller than or equal to "nr" + - of the form: auto_increment_offset + N * auto_increment_increment + where N>=0. + + SYNOPSIS + prev_insert_id + nr Number to "round down" + variables variables struct containing auto_increment_increment and + auto_increment_offset + + RETURN + The number X if it exists, "nr" otherwise. +*/ + +inline ulonglong +prev_insert_id(ulonglong nr, struct system_variables *variables) +{ + if (unlikely(nr < variables->auto_increment_offset)) + { + /* + There's nothing good we can do here. That is a pathological case, where + the offset is larger than the column's max possible value, i.e. not even + the first sequence value may be inserted. User will receive warning. + */ + DBUG_PRINT("info",("auto_increment: nr: %lu cannot honour " + "auto_increment_offset: %lu", + nr, variables->auto_increment_offset)); + return nr; + } + if (variables->auto_increment_increment == 1) + return nr; // optimization of the formula below + nr= (((nr - variables->auto_increment_offset)) / + (ulonglong) variables->auto_increment_increment); + return (nr * (ulonglong) variables->auto_increment_increment + + variables->auto_increment_offset); +} + + /* Update the auto_increment field if necessary @@ -1580,10 +1620,19 @@ bool handler::update_auto_increment() /* Mark that we should clear next_insert_id before next stmt */ thd->clear_next_insert_id= 1; - if (!table->next_number_field->store((longlong) nr, TRUE)) + if (likely(!table->next_number_field->store((longlong) nr, TRUE))) thd->insert_id((ulonglong) nr); else - thd->insert_id(table->next_number_field->val_int()); + { + /* + overflow of the field; we'll use the max value, however we try to + decrease it to honour auto_increment_* variables: + */ + nr= prev_insert_id(table->next_number_field->val_int(), variables); + thd->insert_id(nr); + if (unlikely(table->next_number_field->store((longlong) nr, TRUE))) + thd->insert_id(nr= table->next_number_field->val_int()); + } /* We can't set next_insert_id if the auto-increment key is not the From fd520be81762e57e2c146227743c78e065a3df7e Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 6 Jul 2006 17:40:36 +0200 Subject: [PATCH 10/32] Behaviour change of mysql-test-run.pl: by default we never run disabled tests (even if they're explicitely listed on the command-line). We add an option --enable-disabled which will run tests even though they are disabled, and will print, for each such test, the comment explaining why it was disabled. The reason for the change is when you want to run "all tests which are about NDB" for example: mysql-test-run.pl t/*ndb*.test used to run some disabled NDB tests, causing failures, causing investigations. Code amended and approved by Kent. --- mysql-test/lib/mtr_cases.pl | 61 ++++++++++++++++++++++-------------- mysql-test/lib/mtr_report.pl | 18 +++++++++++ mysql-test/mysql-test-run.pl | 6 ++-- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index 448ca90d48d..6f326562649 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -37,6 +37,23 @@ sub collect_test_cases ($) { opendir(TESTDIR, $testdir) or mtr_error("Can't open dir \"$testdir\": $!"); + # ---------------------------------------------------------------------- + # Disable some tests listed in disabled.def + # ---------------------------------------------------------------------- + my %disabled; + if ( open(DISABLED, "$testdir/disabled.def" ) ) + { + while ( ) + { + chomp; + if ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) + { + $disabled{$1}= $2; + } + } + close DISABLED; + } + if ( @::opt_cases ) { foreach my $tname ( @::opt_cases ) { # Run in specified order, no sort @@ -100,30 +117,13 @@ sub collect_test_cases ($) { } } - collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,{}, + collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled, $component_id); } closedir TESTDIR; } else { - # ---------------------------------------------------------------------- - # Disable some tests listed in disabled.def - # ---------------------------------------------------------------------- - my %disabled; - if ( ! $::opt_ignore_disabled_def and open(DISABLED, "$testdir/disabled.def" ) ) - { - while ( ) - { - chomp; - if ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) - { - $disabled{$1}= $2; - } - } - close DISABLED; - } - foreach my $elem ( sort readdir(TESTDIR) ) { my $component_id= undef; my $tname= undef; @@ -414,20 +414,35 @@ sub collect_one_test_case($$$$$$$) { } # FIXME why this late? + my $marked_as_disabled= 0; if ( $disabled->{$tname} ) { - $tinfo->{'skip'}= 1; - $tinfo->{'disable'}= 1; # Sub type of 'skip' - $tinfo->{'comment'}= $disabled->{$tname} if $disabled->{$tname}; + $marked_as_disabled= 1; + $tinfo->{'comment'}= $disabled->{$tname}; } if ( -f $disabled_file ) { - $tinfo->{'skip'}= 1; - $tinfo->{'disable'}= 1; # Sub type of 'skip' + $marked_as_disabled= 1; $tinfo->{'comment'}= mtr_fromfile($disabled_file); } + # If test was marked as disabled, either opt_enable_disabled is off and then + # we skip this test, or it is on and then we run this test but warn + + if ( $marked_as_disabled ) + { + if ( $::opt_enable_disabled ) + { + $tinfo->{'dont_skip_though_disabled'}= 1; + } + else + { + $tinfo->{'skip'}= 1; + $tinfo->{'disable'}= 1; # Sub type of 'skip' + } + } + if ( $component_id eq 'im' ) { if ( $::glob_use_embedded_server ) diff --git a/mysql-test/lib/mtr_report.pl b/mysql-test/lib/mtr_report.pl index f2da89355f7..6a68e3a68d0 100644 --- a/mysql-test/lib/mtr_report.pl +++ b/mysql-test/lib/mtr_report.pl @@ -10,6 +10,7 @@ sub mtr_report_test_name($); sub mtr_report_test_passed($); sub mtr_report_test_failed($); sub mtr_report_test_skipped($); +sub mtr_report_test_not_skipped_though_disabled($); sub mtr_show_failed_diff ($); sub mtr_report_stats ($); @@ -100,6 +101,23 @@ sub mtr_report_test_skipped ($) { } } +sub mtr_report_tests_not_skipped_though_disabled ($) { + my $tests= shift; + + if ( $::opt_enable_disabled ) + { + my @disabled_tests= grep {$_->{'dont_skip_though_disabled'}} @$tests; + if ( @disabled_tests ) + { + print "\nTest(s) which will be run though they are marked as disabled:\n"; + foreach my $tinfo ( sort {$a->{'name'} cmp $b->{'name'}} @disabled_tests ) + { + printf " %-20s : %s\n", $tinfo->{'name'}, $tinfo->{'comment'}; + } + } + } +} + sub mtr_report_test_passed ($) { my $tinfo= shift; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index ec105b29e3f..fcaf43d1b56 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -216,6 +216,7 @@ our $opt_extern; our $opt_fast; our $opt_force; our $opt_reorder; +our $opt_enable_disabled; our $opt_gcov; our $opt_gcov_err; @@ -661,6 +662,7 @@ sub command_line_setup () { 'netware' => \$opt_netware, 'old-master' => \$opt_old_master, 'reorder' => \$opt_reorder, + 'enable-disabled' => \$opt_enable_disabled, 'script-debug' => \$opt_script_debug, 'sleep=i' => \$opt_sleep, 'socket=s' => \$opt_socket, @@ -1781,12 +1783,12 @@ sub run_suite () { mtr_print_thick_line(); - mtr_report("Finding Tests in the '$suite' suite"); - mtr_timer_start($glob_timers,"suite", 60 * $opt_suite_timeout); mtr_report("Starting Tests in the '$suite' suite"); + mtr_report_tests_not_skipped_though_disabled($tests); + mtr_print_header(); foreach my $tinfo ( @$tests ) From a5f184d5c7bf7da7f1fbcab7f6b8d41a5a676825 Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 6 Jul 2006 17:54:04 +0200 Subject: [PATCH 11/32] Fix for BUG#20649 "mixed replication mode does not work with INSERT DELAYED". The bug was that if the server was running in mixed binlogging mode, and an INSERT DELAYED used some needing-row-based components like UUID(), the server didn't binlog this row-based but statement-based, which thus failed to insert correct data on the slave. This changeset implements that when a delayed_insert thread is created, if the server's global binlog mode is "mixed", that thread will use row-based. This also fixes BUG#20633 "INSERT DELAYED RAND() or @user_var does not replicate statement-based": we don't fix it in statement-based mode (would require bookeeping of rand seeds and user variables used by each row), but at least it will now work in mixed mode (as row-based will be used). We re-enable rpl_switch_stm_row_mixed.test (so BUG#18590 which was about re-enabling this test, will be closed) to test the fixes. Between when it was disabled and now, some good changes to row-based binlogging (no generation of table map events for non-changed tables) induce changes in the test's result file. --- mysql-test/r/rpl_switch_stm_row_mixed.result | 24 ++++++++++++++------ mysql-test/t/disabled.def | 1 - mysql-test/t/rpl_switch_stm_row_mixed.test | 7 ++++++ sql/sql_insert.cc | 3 +++ 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/rpl_switch_stm_row_mixed.result b/mysql-test/r/rpl_switch_stm_row_mixed.result index 313037bb9dc..7c6a984bed8 100644 --- a/mysql-test/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/r/rpl_switch_stm_row_mixed.result @@ -142,12 +142,20 @@ select foo3(); ERROR HY000: Cannot change the binary logging format inside a stored function or trigger select * from t1 where a="alarm"; a +insert delayed into t2 values("delay_1_"); +insert delayed into t2 values(concat("delay_2_",UUID())); +Warnings: +Warning 1265 Data truncated for column 'UUID()' at row 1 +insert delayed into t2 values("delay_3_"),(concat("delay_4_",UUID())),("delay_5_"); +Warnings: +Warning 1265 Data truncated for column 'UUID()' at row 2 +insert delayed into t2 values("delay_6_"); select count(*) from t1; count(*) 36 select count(*) from t2; count(*) -1 +7 select count(*) from t3; count(*) 2 @@ -160,8 +168,6 @@ count(*) show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # drop database if exists mysqltest1 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # create database mysqltest1 master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TABLE t1 (a varchar(100)) master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) @@ -178,10 +184,6 @@ master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work") master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E6379 COLLATE latin1_swedish_ci master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' @@ -269,4 +271,12 @@ master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F drop database mysqltest1; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 6f26847f8d7..a0b61f21047 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -32,7 +32,6 @@ rpl_ndb_ddl : BUG#18946 result file needs update + test needs to ch rpl_ndb_innodb2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement #rpl_ndb_log : BUG#18947 2006-03-21 tomas CRBR: order in binlog of create table and insert (on different table) not determ rpl_ndb_myisam2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement -rpl_switch_stm_row_mixed : BUG#18590 2006-03-28 brian rpl_row_blob_innodb : BUG#18980 2006-04-10 kent Test fails randomly rpl_row_func003 : BUG#19074 2006-13-04 andrei test failed rpl_sp : BUG#16456 2006-02-16 jmiller diff --git a/mysql-test/t/rpl_switch_stm_row_mixed.test b/mysql-test/t/rpl_switch_stm_row_mixed.test index 4a79b3995c4..f7d942c319b 100644 --- a/mysql-test/t/rpl_switch_stm_row_mixed.test +++ b/mysql-test/t/rpl_switch_stm_row_mixed.test @@ -154,6 +154,13 @@ call foo2(); select foo3(); select * from t1 where a="alarm"; +# Test that INSERT DELAYED works in mixed mode +insert delayed into t2 values("delay_1_"); +insert delayed into t2 values(concat("delay_2_",UUID())); +insert delayed into t2 values("delay_3_"),(concat("delay_4_",UUID())),("delay_5_"); +insert delayed into t2 values("delay_6_"); +sleep 4; # time for the delayed insert to reach disk + # If you want to do manual testing of the mixed mode regarding UDFs (not # testable automatically as quite platform- and compiler-dependent), # you just need to set the variable below to 1, and to diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 088bd3e59e5..8e08c79515e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1290,6 +1290,9 @@ public: thd.command=COM_DELAYED_INSERT; thd.lex->current_select= 0; // for my_message_sql thd.lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock() +#ifdef HAVE_ROW_BASED_REPLICATION + thd.set_current_stmt_binlog_row_based_if_mixed(); +#endif bzero((char*) &thd.net, sizeof(thd.net)); // Safety bzero((char*) &table_list, sizeof(table_list)); // Safety From a4768e115a09b39921017e08092ae63dc4fddb8b Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 6 Jul 2006 18:01:32 +0200 Subject: [PATCH 12/32] Fix for BUG#20349 "mysql-test-run.pl needs to add --debug to mysql_client_test like mysql-test-run". Nothing to document. --- mysql-test/mysql-test-run.pl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index d03f82f3866..90bc059b40b 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3354,6 +3354,12 @@ sub run_mysqltest ($) { "--port=$master->[0]->{'path_myport'} " . "--socket=$master->[0]->{'path_mysock'}"; + if ( $opt_debug ) + { + $cmdline_mysql_client_test .= + " --debug=d:t:A,$opt_vardir_trace/log/mysql_client_test.trace"; + } + if ( $glob_use_embedded_server ) { $cmdline_mysql_client_test.= From 6ba4d22ca2b425f9639cab9a6716a50d1110dc8f Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Thu, 6 Jul 2006 22:03:35 +0200 Subject: [PATCH 13/32] Testcase for BUG#20633 "INSERT DELAYED RAND() or @user_var does not replicate statement-based" (bugfix was committed today): we verify that now it works in mixed mode. And a comment. --- mysql-test/r/rpl_switch_stm_row_mixed.result | 12 ++++++++++++ mysql-test/t/rpl_switch_stm_row_mixed.test | 14 ++++++++++++-- sql/sql_insert.cc | 6 ++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/rpl_switch_stm_row_mixed.result b/mysql-test/r/rpl_switch_stm_row_mixed.result index 7c6a984bed8..93fc7f450d0 100644 --- a/mysql-test/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/r/rpl_switch_stm_row_mixed.result @@ -150,6 +150,10 @@ insert delayed into t2 values("delay_3_"),(concat("delay_4_",UUID())),("delay_5_ Warnings: Warning 1265 Data truncated for column 'UUID()' at row 2 insert delayed into t2 values("delay_6_"); +create table t12 (a int, b float); +insert delayed into t12 values(1,rand()); +set @a=2.345; +insert delayed into t12 values(2,@a); select count(*) from t1; count(*) 36 @@ -165,6 +169,9 @@ count(*) select count(*) from t5; count(*) 58 +select count(*) from t12; +count(*) +2 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # drop database if exists mysqltest1 @@ -279,4 +286,9 @@ master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; create table t12 (a int, b float) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t12) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t12) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F drop database mysqltest1; diff --git a/mysql-test/t/rpl_switch_stm_row_mixed.test b/mysql-test/t/rpl_switch_stm_row_mixed.test index f7d942c319b..e29aea8f0b7 100644 --- a/mysql-test/t/rpl_switch_stm_row_mixed.test +++ b/mysql-test/t/rpl_switch_stm_row_mixed.test @@ -154,12 +154,21 @@ call foo2(); select foo3(); select * from t1 where a="alarm"; -# Test that INSERT DELAYED works in mixed mode +# Test that INSERT DELAYED works in mixed mode (BUG#20649) insert delayed into t2 values("delay_1_"); insert delayed into t2 values(concat("delay_2_",UUID())); insert delayed into t2 values("delay_3_"),(concat("delay_4_",UUID())),("delay_5_"); insert delayed into t2 values("delay_6_"); -sleep 4; # time for the delayed insert to reach disk + +# Test for BUG#20633 (INSERT DELAYED RAND()/user_variable does not +# replicate fine in statement-based ; we test that in mixed mode it +# works). +create table t12 (a int, b float); +insert delayed into t12 values(1,rand()); +set @a=2.345; +insert delayed into t12 values(2,@a); + +sleep 4; # time for the delayed inserts to reach disk # If you want to do manual testing of the mixed mode regarding UDFs (not # testable automatically as quite platform- and compiler-dependent), @@ -195,6 +204,7 @@ select count(*) from t2; select count(*) from t3; select count(*) from t4; select count(*) from t5; +select count(*) from t12; if ($you_want_to_test_UDF) { select count(*) from t6; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index aa81dc75867..30dbf3b7226 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1290,9 +1290,11 @@ public: thd.command=COM_DELAYED_INSERT; thd.lex->current_select= 0; // for my_message_sql thd.lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock() -#ifdef HAVE_ROW_BASED_REPLICATION + /* + Statement-based replication of INSERT DELAYED has problems with RAND() + and user vars, so in mixed mode we go to row-based. + */ thd.set_current_stmt_binlog_row_based_if_mixed(); -#endif bzero((char*) &thd.net, sizeof(thd.net)); // Safety bzero((char*) &table_list, sizeof(table_list)); // Safety From f7d0091dfa29b8485b6bea7afe4c148d98fe07fc Mon Sep 17 00:00:00 2001 From: "mats@romeo.(none)" <> Date: Fri, 7 Jul 2006 09:25:12 +0200 Subject: [PATCH 14/32] Adding sleep to allow mysqlslap to finish it's job on systems under high load. --- mysql-test/t/rpl_insert.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysql-test/t/rpl_insert.test b/mysql-test/t/rpl_insert.test index c53cd60fa74..1b67736dd72 100644 --- a/mysql-test/t/rpl_insert.test +++ b/mysql-test/t/rpl_insert.test @@ -17,6 +17,8 @@ CREATE TABLE t1 (id INT, name VARCHAR(64)); let $query = "INSERT DELAYED INTO t1 VALUES (1, 'Dr. No'), (2, 'From Russia With Love'), (3, 'Goldfinger'), (4, 'Thunderball'), (5, 'You Only Live Twice')"; --exec $MYSQL_SLAP --silent --concurrency=20 --iterations=200 --query=$query --delimiter=";" +--sleep 10 + SELECT COUNT(*) FROM mysqlslap.t1; sync_slave_with_master; SELECT COUNT(*) FROM mysqlslap.t1; From 1afe62011604c72c02245a21daa60383650f68d0 Mon Sep 17 00:00:00 2001 From: "grog@eucla.lemis.com" <> Date: Fri, 7 Jul 2006 16:57:55 +0930 Subject: [PATCH 15/32] slave.cc: Tidy up white space. --- sql/slave.cc | 1067 +++++++++++++++++++++++++------------------------- 1 file changed, 533 insertions(+), 534 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index d9895323b92..0ac4cda7b3b 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -63,14 +63,14 @@ static int count_relay_log_space(RELAY_LOG_INFO* rli); static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type); static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, - bool suppress_warnings); + bool suppress_warnings); static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, - bool reconnect, bool suppress_warnings); + bool reconnect, bool suppress_warnings); static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, - void* thread_killed_arg); + void* thread_killed_arg); static int request_table_dump(MYSQL* mysql, const char* db, const char* table); static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, - const char* table_name, bool overwrite); + const char* table_name, bool overwrite); static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi); /* @@ -78,17 +78,17 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi); SYNOPSIS init_thread_mask() - mask Return value here - mi master_info for slave - inverse If set, returns which threads are not running + mask Return value here + mi master_info for slave + inverse If set, returns which threads are not running IMPLEMENTATION Get a bit mask for which threads are running so that we can later restart these threads. RETURN - mask If inverse == 0, running threads - If inverse == 1, stopped threads + mask If inverse == 0, running threads + If inverse == 1, stopped threads */ void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse) @@ -168,7 +168,7 @@ int init_slave() } if (init_master_info(active_mi,master_info_file,relay_log_info_file, - !master_host, (SLAVE_IO | SLAVE_SQL))) + !master_host, (SLAVE_IO | SLAVE_SQL))) { sql_print_error("Failed to initialize the master info structure"); goto err; @@ -182,11 +182,11 @@ int init_slave() if (master_host && !opt_skip_slave_start) { if (start_slave_threads(1 /* need mutex */, - 0 /* no wait for start*/, - active_mi, - master_info_file, - relay_log_info_file, - SLAVE_IO | SLAVE_SQL)) + 0 /* no wait for start*/, + active_mi, + master_info_file, + relay_log_info_file, + SLAVE_IO | SLAVE_SQL)) { sql_print_error("Failed to create slave threads"); goto err; @@ -206,12 +206,12 @@ err: SYNOPSIS init_relay_log_pos() - rli Relay information (will be initialized) - log Name of relay log file to read from. NULL = First log - pos Position in relay log file - need_data_lock Set to 1 if this functions should do mutex locks - errmsg Store pointer to error message here - look_for_description_event + rli Relay information (will be initialized) + log Name of relay log file to read from. NULL = First log + pos Position in relay log file + need_data_lock Set to 1 if this functions should do mutex locks + errmsg Store pointer to error message here + look_for_description_event 1 if we should look for such an event. We only need this when the SQL thread starts and opens an existing relay log and has to execute it (possibly from an @@ -229,13 +229,13 @@ err: - check proper initialization of group_master_log_name/group_master_log_pos RETURN VALUES - 0 ok - 1 error. errmsg is set to point to the error message + 0 ok + 1 error. errmsg is set to point to the error message */ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, - ulonglong pos, bool need_data_lock, - const char** errmsg, + ulonglong pos, bool need_data_lock, + const char** errmsg, bool look_for_description_event) { DBUG_ENTER("init_relay_log_pos"); @@ -243,7 +243,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, *errmsg=0; pthread_mutex_t *log_lock=rli->relay_log.get_log_lock(); - + if (need_data_lock) pthread_mutex_lock(&rli->data_lock); @@ -260,13 +260,13 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, By default the relay log is in binlog format 3 (4.0). Even if format is 4, this will work enough to read the first event (Format_desc) (remember that format 4 is just lenghtened compared to format - 3; format 3 is a prefix of format 4). + 3; format 3 is a prefix of format 4). */ rli->relay_log.description_event_for_exec= new Format_description_log_event(3); - + pthread_mutex_lock(log_lock); - + /* Close log file and free buffers if it's already open */ if (rli->cur_log_fd >= 0) { @@ -274,7 +274,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, my_close(rli->cur_log_fd, MYF(MY_WME)); rli->cur_log_fd = -1; } - + rli->group_relay_log_pos = rli->event_relay_log_pos = pos; /* @@ -293,9 +293,9 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, goto err; } strmake(rli->group_relay_log_name,rli->linfo.log_file_name, - sizeof(rli->group_relay_log_name)-1); + sizeof(rli->group_relay_log_name)-1); strmake(rli->event_relay_log_name,rli->linfo.log_file_name, - sizeof(rli->event_relay_log_name)-1); + sizeof(rli->event_relay_log_name)-1); if (rli->relay_log.is_active(rli->linfo.log_file_name)) { /* @@ -314,7 +314,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, Open the relay log and set rli->cur_log to point at this one */ if ((rli->cur_log_fd=open_binlog(&rli->cache_buf, - rli->linfo.log_file_name,errmsg)) < 0) + rli->linfo.log_file_name,errmsg)) < 0) goto err; rli->cur_log = &rli->cache_buf; } @@ -325,7 +325,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, if (pos > BIN_LOG_HEADER_SIZE) /* If pos<=4, we stay at 4 */ { Log_event* ev; - while (look_for_description_event) + while (look_for_description_event) { /* Read the possible Format_description_log_event; if position @@ -378,7 +378,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, or Format_desc. */ } - else + else { DBUG_PRINT("info",("found event of another type=%d", ev->get_type_code())); @@ -391,7 +391,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, { char llbuf1[22], llbuf2[22]; DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s", - llstr(my_b_tell(rli->cur_log),llbuf1), + llstr(my_b_tell(rli->cur_log),llbuf1), llstr(rli->event_relay_log_pos,llbuf2))); } #endif @@ -406,7 +406,7 @@ err: if (!relay_log_purge) rli->log_space_limit= 0; pthread_cond_broadcast(&rli->data_cond); - + pthread_mutex_unlock(log_lock); if (need_data_lock) @@ -423,7 +423,7 @@ err: SYNOPSIS init_slave_skip_errors() - arg List of errors numbers to skip, separated with ',' + arg List of errors numbers to skip, separated with ',' NOTES Called from get_options() in mysqld.cc on start-up @@ -462,7 +462,7 @@ void init_slave_skip_errors(const char* arg) void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, - bool skip_lock) + bool skip_lock) { DBUG_ENTER("st_relay_log_info::inc_group_relay_log_pos"); @@ -471,10 +471,10 @@ void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, inc_event_relay_log_pos(); group_relay_log_pos= event_relay_log_pos; strmake(group_relay_log_name,event_relay_log_name, - sizeof(group_relay_log_name)-1); + sizeof(group_relay_log_name)-1); notify_group_relay_log_name_update(); - + /* If the slave does not support transactions and replicates a transaction, users should not trust group_master_log_pos (which they can display with @@ -506,7 +506,7 @@ void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, With the end_log_pos solution, we avoid computations involving lengthes. */ DBUG_PRINT("info", ("log_pos: %lu group_master_log_pos: %lu", - (long) log_pos, (long) group_master_log_pos)); + (long) log_pos, (long) group_master_log_pos)); if (log_pos) // 3.23 binlogs don't have log_posx { group_master_log_pos= log_pos; @@ -546,7 +546,7 @@ void st_relay_log_info::close_temporary_tables() */ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, - const char** errmsg) + const char** errmsg) { int error=0; DBUG_ENTER("purge_relay_logs"); @@ -584,10 +584,10 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, rli->slave_skip_counter=0; pthread_mutex_lock(&rli->data_lock); - /* - we close the relay log fd possibly left open by the slave SQL thread, + /* + we close the relay log fd possibly left open by the slave SQL thread, to be able to delete it; the relay log fd possibly left open by the slave - I/O thread will be closed naturally in reset_logs() by the + I/O thread will be closed naturally in reset_logs() by the close(LOG_CLOSE_TO_BE_OPENED) call */ if (rli->cur_log_fd >= 0) @@ -605,9 +605,9 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, } /* Save name of used relay log file */ strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(), - sizeof(rli->group_relay_log_name)-1); + sizeof(rli->group_relay_log_name)-1); strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(), - sizeof(rli->event_relay_log_name)-1); + sizeof(rli->event_relay_log_name)-1); rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE; if (count_relay_log_space(rli)) { @@ -617,12 +617,12 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, if (!just_reset) error= init_relay_log_pos(rli, rli->group_relay_log_name, rli->group_relay_log_pos, - 0 /* do not need data lock */, errmsg, 0); - + 0 /* do not need data lock */, errmsg, 0); + err: #ifndef DBUG_OFF char buf[22]; -#endif +#endif DBUG_PRINT("info",("log_space_total: %s",llstr(rli->log_space_total,buf))); pthread_mutex_unlock(&rli->data_lock); DBUG_RETURN(error); @@ -641,7 +641,7 @@ int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) sql_cond_lock=sql_lock; io_cond_lock=io_lock; - + if (skip_lock) { sql_lock = io_lock = 0; @@ -651,10 +651,10 @@ int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) DBUG_PRINT("info",("Terminating IO thread")); mi->abort_slave=1; if ((error=terminate_slave_thread(mi->io_thd,io_lock, - io_cond_lock, - &mi->stop_cond, - &mi->slave_running)) && - !force_all) + io_cond_lock, + &mi->stop_cond, + &mi->slave_running)) && + !force_all) DBUG_RETURN(error); } if ((thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL)) && mi->rli.slave_running) @@ -663,10 +663,10 @@ int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) DBUG_ASSERT(mi->rli.sql_thd != 0) ; mi->rli.abort_slave=1; if ((error=terminate_slave_thread(mi->rli.sql_thd,sql_lock, - sql_cond_lock, - &mi->rli.stop_cond, - &mi->rli.slave_running)) && - !force_all) + sql_cond_lock, + &mi->rli.stop_cond, + &mi->rli.slave_running)) && + !force_all) DBUG_RETURN(error); } DBUG_RETURN(0); @@ -674,9 +674,9 @@ int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, - pthread_mutex_t *cond_lock, - pthread_cond_t* term_cond, - volatile uint *slave_running) + pthread_mutex_t *cond_lock, + pthread_cond_t* term_cond, + volatile uint *slave_running) { DBUG_ENTER("terminate_slave_thread"); if (term_lock) @@ -695,7 +695,7 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, be referening freed memory trying to kick it */ - while (*slave_running) // Should always be true + while (*slave_running) // Should always be true { DBUG_PRINT("loop", ("killing slave thread")); KICK_SLAVE(thd); @@ -714,11 +714,11 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock, - pthread_mutex_t *cond_lock, - pthread_cond_t *start_cond, - volatile uint *slave_running, - volatile ulong *slave_run_id, - MASTER_INFO* mi, + pthread_mutex_t *cond_lock, + pthread_cond_t *start_cond, + volatile uint *slave_running, + volatile ulong *slave_run_id, + MASTER_INFO* mi, bool high_priority) { pthread_t th; @@ -738,7 +738,7 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock, sql_print_error("Server id not set, will not start slave"); DBUG_RETURN(ER_BAD_SLAVE); } - + if (*slave_running) { if (start_cond) @@ -764,12 +764,12 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock, { DBUG_PRINT("sleep",("Waiting for slave thread to start")); const char* old_msg = thd->enter_cond(start_cond,cond_lock, - "Waiting for slave thread to start"); + "Waiting for slave thread to start"); pthread_cond_wait(start_cond,cond_lock); thd->exit_cond(old_msg); pthread_mutex_lock(cond_lock); // re-acquire it as exit_cond() released if (thd->killed) - DBUG_RETURN(thd->killed_errno()); + DBUG_RETURN(thd->killed_errno()); } } if (start_lock) @@ -788,14 +788,14 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock, */ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, - MASTER_INFO* mi, const char* master_info_fname, - const char* slave_info_fname, int thread_mask) + MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname, int thread_mask) { pthread_mutex_t *lock_io=0,*lock_sql=0,*lock_cond_io=0,*lock_cond_sql=0; pthread_cond_t* cond_io=0,*cond_sql=0; int error=0; DBUG_ENTER("start_slave_threads"); - + if (need_slave_mutex) { lock_io = &mi->run_lock; @@ -811,15 +811,15 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, if (thread_mask & SLAVE_IO) error=start_slave_thread(handle_slave_io,lock_io,lock_cond_io, - cond_io, - &mi->slave_running, &mi->slave_run_id, - mi, 1); //high priority, to read the most possible + cond_io, + &mi->slave_running, &mi->slave_run_id, + mi, 1); //high priority, to read the most possible if (!error && (thread_mask & SLAVE_SQL)) { error=start_slave_thread(handle_slave_sql,lock_sql,lock_cond_sql, - cond_sql, - &mi->rli.slave_running, &mi->rli.slave_run_id, - mi, 0); + cond_sql, + &mi->rli.slave_running, &mi->rli.slave_run_id, + mi, 0); if (error) terminate_slave_threads(mi, thread_mask & SLAVE_IO, 0); } @@ -997,8 +997,8 @@ void skip_load_data_infile(NET *net) DBUG_ENTER("skip_load_data_infile"); (void)net_request_file(net, "/dev/null"); - (void)my_net_read(net); // discard response - (void)net_write_command(net, 0, "", 0, "", 0); // Send ok + (void)my_net_read(net); // discard response + (void)net_write_command(net, 0, "", 0, "", 0); // Send ok DBUG_VOID_RETURN; } @@ -1024,7 +1024,7 @@ const char *print_slave_db_safe(const char* db) } static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, - const char *default_val) + const char *default_val) { uint length; DBUG_ENTER("init_strvar_from_file"); @@ -1037,8 +1037,8 @@ static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, else { /* - If we truncated a line or stopped on last char, remove all chars - up to and including newline. + If we truncated a line or stopped on last char, remove all chars + up to and including newline. */ int c; while (((c=my_b_get(f)) != '\n' && c != my_b_EOF)); @@ -1059,8 +1059,8 @@ static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) char buf[32]; DBUG_ENTER("init_intvar_from_file"); - - if (my_b_gets(f, buf, sizeof(buf))) + + if (my_b_gets(f, buf, sizeof(buf))) { *var = atoi(buf); DBUG_RETURN(0); @@ -1081,7 +1081,7 @@ static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) when people upgrade a 3.23 master to 4.0 without doing RESET MASTER: 4.0 slaves are fooled. So we do this only to distinguish between 3.23 and more recent masters (it's too late to change things for 3.23). - + RETURNS 0 ok 1 error @@ -1098,7 +1098,7 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) */ delete mi->rli.relay_log.description_event_for_queue; mi->rli.relay_log.description_event_for_queue= 0; - + if (!my_isdigit(&my_charset_bin,*mysql->server_version)) errmsg = "Master reported unrecognized MySQL version"; else @@ -1106,7 +1106,7 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) /* Note the following switch will bug when we have MySQL branch 30 ;) */ - switch (*mysql->server_version) + switch (*mysql->server_version) { case '0': case '1': @@ -1115,13 +1115,13 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) break; case '3': mi->rli.relay_log.description_event_for_queue= new - Format_description_log_event(1, mysql->server_version); + Format_description_log_event(1, mysql->server_version); break; case '4': mi->rli.relay_log.description_event_for_queue= new - Format_description_log_event(3, mysql->server_version); + Format_description_log_event(3, mysql->server_version); break; - default: + default: /* Master is MySQL >=5.0. Give a default Format_desc event, so that we can take the early steps (like tests for "is this a 3.23 master") which we @@ -1131,18 +1131,18 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) master is 3.23, 4.0, etc. */ mi->rli.relay_log.description_event_for_queue= new - Format_description_log_event(4, mysql->server_version); + Format_description_log_event(4, mysql->server_version); break; } } - - /* + + /* This does not mean that a 5.0 slave will be able to read a 6.0 master; but as we don't know yet, we don't want to forbid this for now. If a 5.0 slave can't read a 6.0 master, this will show up when the slave can't read some events sent by the master, and there will be error messages. */ - + if (errmsg) { sql_print_error(errmsg); @@ -1162,12 +1162,12 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) */ MYSQL_RES *master_res= 0; MYSQL_ROW master_row; - + if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) && (master_res= mysql_store_result(mysql)) && (master_row= mysql_fetch_row(master_res))) { - mi->clock_diff_with_master= + mi->clock_diff_with_master= (long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10)); } else @@ -1177,8 +1177,8 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) do not trust column Seconds_Behind_Master of SHOW SLAVE STATUS"); } if (master_res) - mysql_free_result(master_res); - + mysql_free_result(master_res); + /* Check that the master's server id and ours are different. Because if they are equal (which can result from a simple copy of master's datadir to slave, @@ -1245,9 +1245,9 @@ be equal for replication to work"; time and so could differ for slave and master even if they are really in the same system time zone. So we are omiting this check and just relying on documentation. Also according to Monty there are many users - who are using replication between servers in various time zones. Hence - such check will broke everything for them. (And now everything will - work for them because by default both their master and slave will have + who are using replication between servers in various time zones. Hence + such check will broke everything for them. (And now everything will + work for them because by default both their master and slave will have 'SYSTEM' time zone). This check is only necessary for 4.x masters (and < 5.0.4 masters but those were alpha). @@ -1257,7 +1257,7 @@ be equal for replication to work"; (master_res= mysql_store_result(mysql))) { if ((master_row= mysql_fetch_row(master_res)) && - strcmp(master_row[0], + strcmp(master_row[0], global_system_variables.time_zone->get_name()->ptr())) errmsg= "The slave I/O thread stops because master and slave have \ different values for the TIME_ZONE global variable. The values must \ @@ -1287,7 +1287,7 @@ err: */ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, - const char* table_name, bool overwrite) + const char* table_name, bool overwrite) { ulong packet_len; char *query, *save_db; @@ -1309,10 +1309,10 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, } if (net->read_pos[0] == 255) // error from master { - char *err_msg; + char *err_msg; err_msg= (char*) net->read_pos + ((mysql->server_capabilities & - CLIENT_PROTOCOL_41) ? - 3+SQLSTATE_LENGTH+1 : 3); + CLIENT_PROTOCOL_41) ? + 3+SQLSTATE_LENGTH+1 : 3); my_error(ER_MASTER, MYF(0), err_msg); DBUG_RETURN(1); } @@ -1351,12 +1351,12 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, DBUG_ASSERT(thd->db != 0); thd->db_length= strlen(thd->db); mysql_parse(thd, thd->query, packet_len); // run create table - thd->db = save_db; // leave things the way the were before + thd->db = save_db; // leave things the way the were before thd->db_length= save_db_length; thd->options = save_options; - + if (thd->query_error) - goto err; // mysql_parse took care of the error send + goto err; // mysql_parse took care of the error send thd->proc_info = "Opening master dump table"; tables.lock_type = TL_WRITE; @@ -1365,7 +1365,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, sql_print_error("create_table_from_dump: could not open created table"); goto err; } - + file = tables.table->file; thd->proc_info = "Reading master dump table data"; /* Copy the data file */ @@ -1396,22 +1396,22 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, err: close_thread_tables(thd); thd->net.no_send_ok = 0; - DBUG_RETURN(error); + DBUG_RETURN(error); } int fetch_master_table(THD *thd, const char *db_name, const char *table_name, - MASTER_INFO *mi, MYSQL *mysql, bool overwrite) + MASTER_INFO *mi, MYSQL *mysql, bool overwrite) { int error= 1; const char *errmsg=0; bool called_connected= (mysql != NULL); DBUG_ENTER("fetch_master_table"); DBUG_PRINT("enter", ("db_name: '%s' table_name: '%s'", - db_name,table_name)); + db_name,table_name)); if (!called_connected) - { + { if (!(mysql = mysql_init(NULL))) { DBUG_RETURN(1); @@ -1442,7 +1442,7 @@ int fetch_master_table(THD *thd, const char *db_name, const char *table_name, goto err; } if (create_table_from_dump(thd, mysql, db_name, - table_name, overwrite)) + table_name, overwrite)) goto err; // create_table_from_dump have sent the error already error = 0; @@ -1452,7 +1452,7 @@ int fetch_master_table(THD *thd, const char *db_name, const char *table_name, mysql_close(mysql); if (errmsg && thd->vio_ok()) my_message(error, errmsg, MYF(0)); - DBUG_RETURN(test(error)); // Return 1 on error + DBUG_RETURN(test(error)); // Return 1 on error } @@ -1568,44 +1568,44 @@ file '%s', errno %d)", fname, my_errno); goto err; } if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, - MYF(MY_WME))) + MYF(MY_WME))) { sql_print_error("Failed to create a cache on relay log info file '%s'", - fname); + fname); msg= current_thd->net.last_error; goto err; } /* Init relay log with first entry in the relay index file */ if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */, - &msg, 0)) + &msg, 0)) { sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4)"); goto err; } rli->group_master_log_name[0]= 0; - rli->group_master_log_pos= 0; + rli->group_master_log_pos= 0; rli->info_fd= info_fd; } else // file exists { if (info_fd >= 0) reinit_io_cache(&rli->info_file, READ_CACHE, 0L,0,0); - else + else { int error=0; if ((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0) { sql_print_error("\ Failed to open the existing relay log info file '%s' (errno %d)", - fname, my_errno); + fname, my_errno); error= 1; } else if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME))) { sql_print_error("Failed to create a cache on relay log info file '%s'", - fname); + fname); error= 1; } if (error) @@ -1618,16 +1618,16 @@ Failed to open the existing relay log info file '%s' (errno %d)", DBUG_RETURN(1); } } - + rli->info_fd = info_fd; int relay_log_pos, master_log_pos; if (init_strvar_from_file(rli->group_relay_log_name, - sizeof(rli->group_relay_log_name), + sizeof(rli->group_relay_log_name), &rli->info_file, "") || init_intvar_from_file(&relay_log_pos, - &rli->info_file, BIN_LOG_HEADER_SIZE) || + &rli->info_file, BIN_LOG_HEADER_SIZE) || init_strvar_from_file(rli->group_master_log_name, - sizeof(rli->group_master_log_name), + sizeof(rli->group_master_log_name), &rli->info_file, "") || init_intvar_from_file(&master_log_pos, &rli->info_file, 0)) { @@ -1640,15 +1640,15 @@ Failed to open the existing relay log info file '%s' (errno %d)", rli->group_master_log_pos= master_log_pos; if (init_relay_log_pos(rli, - rli->group_relay_log_name, - rli->group_relay_log_pos, - 0 /* no data lock*/, - &msg, 0)) + rli->group_relay_log_name, + rli->group_relay_log_pos, + 0 /* no data lock*/, + &msg, 0)) { char llbuf[22]; sql_print_error("Failed to open the relay log '%s' (relay_log_pos %s)", - rli->group_relay_log_name, - llstr(rli->group_relay_log_pos, llbuf)); + rli->group_relay_log_name, + llstr(rli->group_relay_log_pos, llbuf)); goto err; } } @@ -1657,7 +1657,7 @@ Failed to open the existing relay log info file '%s' (errno %d)", { char llbuf1[22], llbuf2[22]; DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s", - llstr(my_b_tell(rli->cur_log),llbuf1), + llstr(my_b_tell(rli->cur_log),llbuf1), llstr(rli->event_relay_log_pos,llbuf2))); DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos); @@ -1699,14 +1699,14 @@ static inline int add_relay_log(RELAY_LOG_INFO* rli,LOG_INFO* linfo) if (!my_stat(linfo->log_file_name,&s,MYF(0))) { sql_print_error("log %s listed in the index, but failed to stat", - linfo->log_file_name); + linfo->log_file_name); DBUG_RETURN(1); } rli->log_space_total += s.st_size; #ifndef DBUG_OFF char buf[22]; DBUG_PRINT("info",("log_space_total: %s", llstr(rli->log_space_total,buf))); -#endif +#endif DBUG_RETURN(0); } @@ -1721,11 +1721,11 @@ static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) pthread_mutex_lock(&rli->log_space_lock); save_proc_info= thd->enter_cond(&rli->log_space_cond, - &rli->log_space_lock, - "\ + &rli->log_space_lock, + "\ Waiting for the slave SQL thread to free enough relay log space"); while (rli->log_space_limit < rli->log_space_total && - !(slave_killed=io_slave_killed(thd,mi)) && + !(slave_killed=io_slave_killed(thd,mi)) && !rli->ignore_log_space_limit) pthread_cond_wait(&rli->log_space_cond, &rli->log_space_lock); thd->exit_cond(save_proc_info); @@ -1748,9 +1748,9 @@ static int count_relay_log_space(RELAY_LOG_INFO* rli) if (add_relay_log(rli,&linfo)) DBUG_RETURN(1); } while (!rli->relay_log.find_next_log(&linfo, 1)); - /* + /* As we have counted everything, including what may have written in a - preceding write, we must reset bytes_written, or we may count some space + preceding write, we must reset bytes_written, or we may count some space twice. */ rli->relay_log.reset_bytes_written(); @@ -1816,8 +1816,8 @@ void init_master_info_with_options(MASTER_INFO* mi) DBUG_ENTER("init_master_info_with_options"); mi->master_log_name[0] = 0; - mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number - + mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number + if (master_host) strmake(mi->host, master_host, sizeof(mi->host) - 1); if (master_user) @@ -1826,7 +1826,7 @@ void init_master_info_with_options(MASTER_INFO* mi) strmake(mi->password, master_password, MAX_PASSWORD_LENGTH); mi->port = master_port; mi->connect_retry = master_connect_retry; - + mi->ssl= master_ssl; if (master_ssl_ca) strmake(mi->ssl_ca, master_ssl_ca, sizeof(mi->ssl_ca)-1); @@ -1935,7 +1935,7 @@ file '%s', errno %d)", fname, my_errno); goto err; } if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0, - MYF(MY_WME))) + MYF(MY_WME))) { sql_print_error("Failed to create a cache on master info file (\ file '%s')", fname); @@ -1995,8 +1995,8 @@ file '%s')", fname); overwritten by the second row later. */ if (init_strvar_from_file(mi->master_log_name, - sizeof(mi->master_log_name), &mi->file, - "")) + sizeof(mi->master_log_name), &mi->file, + "")) goto errwithmsg; lines= strtoul(mi->master_log_name, &first_non_digit, 10); @@ -2012,15 +2012,15 @@ file '%s')", fname); lines= 7; if (init_intvar_from_file(&master_log_pos, &mi->file, 4) || - init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, - master_host) || - init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, - master_user) || + init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, + master_host) || + init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, + master_user) || init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1, &mi->file, master_password) || - init_intvar_from_file(&port, &mi->file, master_port) || - init_intvar_from_file(&connect_retry, &mi->file, - master_connect_retry)) + init_intvar_from_file(&port, &mi->file, master_port) || + init_intvar_from_file(&connect_retry, &mi->file, + master_connect_retry)) goto errwithmsg; /* @@ -2059,8 +2059,8 @@ file '%s')", fname); mi->ssl= (my_bool) ssl; } DBUG_PRINT("master_info",("log_file_name: %s position: %ld", - mi->master_log_name, - (ulong) mi->master_log_pos)); + mi->master_log_name, + (ulong) mi->master_log_pos)); mi->rli.mi = mi; if (init_relay_log_info(&mi->rli, slave_info_fname)) @@ -2105,23 +2105,23 @@ int register_slave_on_master(MYSQL* mysql) /* 30 is a good safety margin */ if (report_host_len + report_user_len + report_password_len + 30 > sizeof(buf)) - DBUG_RETURN(0); // safety + DBUG_RETURN(0); // safety int4store(pos, server_id); pos+= 4; - pos= net_store_data(pos, report_host, report_host_len); + pos= net_store_data(pos, report_host, report_host_len); pos= net_store_data(pos, report_user, report_user_len); pos= net_store_data(pos, report_password, report_password_len); int2store(pos, (uint16) report_port); pos+= 2; - int4store(pos, rpl_recovery_rank); pos+= 4; + int4store(pos, rpl_recovery_rank); pos+= 4; /* The master will fill in master_id */ - int4store(pos, 0); pos+= 4; + int4store(pos, 0); pos+= 4; if (simple_command(mysql, COM_REGISTER_SLAVE, (char*) buf, - (uint) (pos- buf), 0)) + (uint) (pos- buf), 0)) { sql_print_error("Error on COM_REGISTER_SLAVE: %d '%s'", - mysql_errno(mysql), - mysql_error(mysql)); + mysql_errno(mysql), + mysql_error(mysql)); DBUG_RETURN(1); } DBUG_RETURN(0); @@ -2136,25 +2136,25 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) DBUG_ENTER("show_master_info"); field_list.push_back(new Item_empty_string("Slave_IO_State", - 14)); + 14)); field_list.push_back(new Item_empty_string("Master_Host", - sizeof(mi->host))); + sizeof(mi->host))); field_list.push_back(new Item_empty_string("Master_User", - sizeof(mi->user))); + sizeof(mi->user))); field_list.push_back(new Item_return_int("Master_Port", 7, - MYSQL_TYPE_LONG)); + MYSQL_TYPE_LONG)); field_list.push_back(new Item_return_int("Connect_Retry", 10, - MYSQL_TYPE_LONG)); + MYSQL_TYPE_LONG)); field_list.push_back(new Item_empty_string("Master_Log_File", - FN_REFLEN)); + FN_REFLEN)); field_list.push_back(new Item_return_int("Read_Master_Log_Pos", 10, - MYSQL_TYPE_LONGLONG)); + MYSQL_TYPE_LONGLONG)); field_list.push_back(new Item_empty_string("Relay_Log_File", - FN_REFLEN)); + FN_REFLEN)); field_list.push_back(new Item_return_int("Relay_Log_Pos", 10, - MYSQL_TYPE_LONGLONG)); + MYSQL_TYPE_LONGLONG)); field_list.push_back(new Item_empty_string("Relay_Master_Log_File", - FN_REFLEN)); + FN_REFLEN)); field_list.push_back(new Item_empty_string("Slave_IO_Running", 3)); field_list.push_back(new Item_empty_string("Slave_SQL_Running", 3)); field_list.push_back(new Item_empty_string("Replicate_Do_DB", 20)); @@ -2163,33 +2163,33 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) field_list.push_back(new Item_empty_string("Replicate_Ignore_Table", 23)); field_list.push_back(new Item_empty_string("Replicate_Wild_Do_Table", 24)); field_list.push_back(new Item_empty_string("Replicate_Wild_Ignore_Table", - 28)); + 28)); field_list.push_back(new Item_return_int("Last_Errno", 4, MYSQL_TYPE_LONG)); field_list.push_back(new Item_empty_string("Last_Error", 20)); field_list.push_back(new Item_return_int("Skip_Counter", 10, - MYSQL_TYPE_LONG)); + MYSQL_TYPE_LONG)); field_list.push_back(new Item_return_int("Exec_Master_Log_Pos", 10, - MYSQL_TYPE_LONGLONG)); + MYSQL_TYPE_LONGLONG)); field_list.push_back(new Item_return_int("Relay_Log_Space", 10, - MYSQL_TYPE_LONGLONG)); + MYSQL_TYPE_LONGLONG)); field_list.push_back(new Item_empty_string("Until_Condition", 6)); field_list.push_back(new Item_empty_string("Until_Log_File", FN_REFLEN)); - field_list.push_back(new Item_return_int("Until_Log_Pos", 10, + field_list.push_back(new Item_return_int("Until_Log_Pos", 10, MYSQL_TYPE_LONGLONG)); field_list.push_back(new Item_empty_string("Master_SSL_Allowed", 7)); field_list.push_back(new Item_empty_string("Master_SSL_CA_File", sizeof(mi->ssl_ca))); - field_list.push_back(new Item_empty_string("Master_SSL_CA_Path", + field_list.push_back(new Item_empty_string("Master_SSL_CA_Path", sizeof(mi->ssl_capath))); - field_list.push_back(new Item_empty_string("Master_SSL_Cert", + field_list.push_back(new Item_empty_string("Master_SSL_Cert", sizeof(mi->ssl_cert))); - field_list.push_back(new Item_empty_string("Master_SSL_Cipher", + field_list.push_back(new Item_empty_string("Master_SSL_Cipher", sizeof(mi->ssl_cipher))); - field_list.push_back(new Item_empty_string("Master_SSL_Key", + field_list.push_back(new Item_empty_string("Master_SSL_Key", sizeof(mi->ssl_key))); field_list.push_back(new Item_return_int("Seconds_Behind_Master", 10, MYSQL_TYPE_LONGLONG)); - + if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -2199,7 +2199,7 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) DBUG_PRINT("info",("host is set: '%s'", mi->host)); String *packet= &thd->packet; protocol->prepare_for_resend(); - + /* TODO: we read slave_running without run_lock, whereas these variables are updated under run_lock and not data_lock. In 5.0 we should lock @@ -2216,8 +2216,8 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) protocol->store(mi->master_log_name, &my_charset_bin); protocol->store((ulonglong) mi->master_log_pos); protocol->store(mi->rli.group_relay_log_name + - dirname_length(mi->rli.group_relay_log_name), - &my_charset_bin); + dirname_length(mi->rli.group_relay_log_name), + &my_charset_bin); protocol->store((ulonglong) mi->rli.group_relay_log_pos); protocol->store(mi->rli.group_master_log_name, &my_charset_bin); protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT ? @@ -2244,13 +2244,13 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) protocol->store((ulonglong) mi->rli.log_space_total); protocol->store( - mi->rli.until_condition==RELAY_LOG_INFO::UNTIL_NONE ? "None": + mi->rli.until_condition==RELAY_LOG_INFO::UNTIL_NONE ? "None": ( mi->rli.until_condition==RELAY_LOG_INFO::UNTIL_MASTER_POS? "Master": "Relay"), &my_charset_bin); protocol->store(mi->rli.until_log_name, &my_charset_bin); protocol->store((ulonglong) mi->rli.until_log_pos); - -#ifdef HAVE_OPENSSL + +#ifdef HAVE_OPENSSL protocol->store(mi->ssl? "Yes":"No", &my_charset_bin); #else protocol->store(mi->ssl? "Ignored":"No", &my_charset_bin); @@ -2355,10 +2355,10 @@ int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache) my_b_seek(file, 0L); my_b_printf(file, "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n", - LINES_IN_MASTER_INFO_WITH_SSL, + LINES_IN_MASTER_INFO_WITH_SSL, mi->master_log_name, llstr(mi->master_log_pos, lbuf), - mi->host, mi->user, - mi->password, mi->port, mi->connect_retry, + mi->host, mi->user, + mi->password, mi->port, mi->connect_retry, (int)(mi->ssl), mi->ssl_ca, mi->ssl_capath, mi->ssl_cert, mi->ssl_cipher, mi->ssl_key); DBUG_RETURN(-flush_io_cache(file)); @@ -2419,7 +2419,7 @@ st_relay_log_info::~st_relay_log_info() wait_for_pos() thd client thread that sent SELECT MASTER_POS_WAIT log_name log name to wait for - log_pos position to wait for + log_pos position to wait for timeout timeout in seconds before giving up waiting NOTES @@ -2458,7 +2458,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, msg= thd->enter_cond(&data_cond, &data_lock, "Waiting for the slave SQL thread to " "advance position"); - /* + /* This function will abort when it notices that some CHANGE MASTER or RESET MASTER has changed the master info. To catch this, these commands modify abort_pos_wait ; We just monitor @@ -2504,7 +2504,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, { error= -2; goto err; - } + } /* The "compare and wait" main loop */ while (!thd->killed && @@ -2564,7 +2564,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, } //wait for master update, with optional timeout. - + DBUG_PRINT("info",("Waiting for master update")); /* We are going to pthread_cond_(timed)wait(); if the SQL thread stops it @@ -2574,7 +2574,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, { /* Note that pthread_cond_timedwait checks for the timeout - before for the condition ; i.e. it returns ETIMEDOUT + before for the condition ; i.e. it returns ETIMEDOUT if the system time equals or exceeds the time specified by abstime before the condition variable is signaled or broadcast, _or_ if the absolute time specified by abstime has already passed at the time @@ -2608,7 +2608,7 @@ improper_arguments: %d timed_out: %d", (int) (error == -2), (int) (error == -1))); if (thd->killed || init_abort_pos_wait != abort_pos_wait || - !slave_running) + !slave_running) { error= -2; } @@ -2648,13 +2648,13 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) { DBUG_ENTER("init_slave_thread"); thd->system_thread = (thd_type == SLAVE_THD_SQL) ? - SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO; + SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO; thd->security_ctx->skip_grants(); my_net_init(&thd->net, 0); thd->net.read_timeout = slave_net_timeout; thd->slave_thread = 1; set_slave_thread_options(thd); - /* + /* It's nonsense to constrain the slave threads with max_join_size; if a query succeeded on master, we HAVE to execute it. So set OPTION_BIG_SELECTS. Setting max_join_size to HA_POS_ERROR is not enough @@ -2680,7 +2680,7 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) #if !defined(__WIN__) && !defined(__NETWARE__) sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use + VOID(sigemptyset(&set)); // Get mask in use VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); #endif @@ -2695,7 +2695,7 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, - void* thread_killed_arg) + void* thread_killed_arg) { int nap_time; thr_alarm_t alarmed; @@ -2716,7 +2716,7 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, thr_alarm(&alarmed, 2 * nap_time, &alarm_buff); sleep(nap_time); thr_end_alarm(&alarmed); - + if ((*thread_killed)(thd,thread_killed_arg)) DBUG_RETURN(1); start_time=time((time_t*) 0); @@ -2726,7 +2726,7 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, static int request_dump(MYSQL* mysql, MASTER_INFO* mi, - bool *suppress_warnings) + bool *suppress_warnings) { char buf[FN_REFLEN + 10]; int len; @@ -2748,11 +2748,11 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi, now we just fill up the error log :-) */ if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) - *suppress_warnings= 1; // Suppress reconnect warning + *suppress_warnings= 1; // Suppress reconnect warning else sql_print_error("Error on COM_BINLOG_DUMP: %d %s, will retry in %d secs", - mysql_errno(mysql), mysql_error(mysql), - master_connect_retry); + mysql_errno(mysql), mysql_error(mysql), + master_connect_retry); DBUG_RETURN(1); } @@ -2772,14 +2772,14 @@ static int request_table_dump(MYSQL* mysql, const char* db, const char* table) { sql_print_error("request_table_dump: Buffer overrun"); DBUG_RETURN(1); - } - + } + *p++ = db_len; memcpy(p, db, db_len); p += db_len; *p++ = table_len; memcpy(p, table, table_len); - + if (simple_command(mysql, COM_TABLE_DUMP, buf, p - buf + table_len, 1)) { sql_print_error("request_table_dump: Error sending the table dump \ @@ -2793,19 +2793,19 @@ command"); /* Read one event from the master - + SYNOPSIS read_event() - mysql MySQL connection - mi Master connection information - suppress_warnings TRUE when a normal net read timeout has caused us to - try a reconnect. We do not want to print anything to - the error log in this case because this a anormal - event in an idle server. + mysql MySQL connection + mi Master connection information + suppress_warnings TRUE when a normal net read timeout has caused us to + try a reconnect. We do not want to print anything to + the error log in this case because this a anormal + event in an idle server. RETURN VALUES - 'packet_error' Error - number Length of packet + 'packet_error' Error + number Length of packet */ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) @@ -2820,24 +2820,24 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) */ #ifndef DBUG_OFF if (disconnect_slave_event_count && !(mi->events_till_disconnect--)) - DBUG_RETURN(packet_error); + DBUG_RETURN(packet_error); #endif - + len = net_safe_read(mysql); if (len == packet_error || (long) len < 1) { if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) { /* - We are trying a normal reconnect after a read timeout; - we suppress prints to .err file as long as the reconnect - happens without problems + We are trying a normal reconnect after a read timeout; + we suppress prints to .err file as long as the reconnect + happens without problems */ *suppress_warnings= TRUE; } else sql_print_error("Error reading packet from server: %s ( server_errno=%d)", - mysql_error(mysql), mysql_errno(mysql)); + mysql_error(mysql), mysql_errno(mysql)); DBUG_RETURN(packet_error); } @@ -2846,13 +2846,13 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) { sql_print_information("Slave: received end packet from server, apparent " "master shutdown: %s", - mysql_error(mysql)); + mysql_error(mysql)); DBUG_RETURN(packet_error); } - + DBUG_PRINT("info",( "len=%u, net->read_pos[4] = %d\n", - len, mysql->net.read_pos[4])); - DBUG_RETURN(len - 1); + len, mysql->net.read_pos[4])); + DBUG_RETURN(len - 1); } @@ -2862,8 +2862,8 @@ int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) switch (expected_error) { case ER_NET_READ_ERROR: - case ER_NET_ERROR_ON_WRITE: - case ER_SERVER_SHUTDOWN: + case ER_NET_ERROR_ON_WRITE: + case ER_SERVER_SHUTDOWN: case ER_NEW_ABORTING_CONNECTION: DBUG_RETURN(1); default: @@ -2876,25 +2876,25 @@ int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) SYNOPSYS st_relay_log_info::is_until_satisfied() DESCRIPTION - Checks if UNTIL condition is reached. Uses caching result of last - comparison of current log file name and target log file name. So cached - value should be invalidated if current log file name changes + Checks if UNTIL condition is reached. Uses caching result of last + comparison of current log file name and target log file name. So cached + value should be invalidated if current log file name changes (see st_relay_log_info::notify_... functions). - - This caching is needed to avoid of expensive string comparisons and + + This caching is needed to avoid of expensive string comparisons and strtol() conversions needed for log names comparison. We don't need to - compare them each time this function is called, we only need to do this - when current log name changes. If we have UNTIL_MASTER_POS condition we - need to do this only after Rotate_log_event::exec_event() (which is - rare, so caching gives real benifit), and if we have UNTIL_RELAY_POS - condition then we should invalidate cached comarison value after + compare them each time this function is called, we only need to do this + when current log name changes. If we have UNTIL_MASTER_POS condition we + need to do this only after Rotate_log_event::exec_event() (which is + rare, so caching gives real benifit), and if we have UNTIL_RELAY_POS + condition then we should invalidate cached comarison value after inc_group_relay_log_pos() which called for each group of events (so we - have some benefit if we have something like queries that use + have some benefit if we have something like queries that use autoincrement or if we have transactions). - + Should be called ONLY if until_condition != UNTIL_NONE ! RETURN VALUE - true - condition met or error happened (condition seems to have + true - condition met or error happened (condition seems to have bad log file name) false - condition not met */ @@ -2906,7 +2906,7 @@ bool st_relay_log_info::is_until_satisfied() DBUG_ENTER("st_relay_log_info::is_until_satisfied"); DBUG_ASSERT(until_condition != UNTIL_NONE); - + if (until_condition == UNTIL_MASTER_POS) { log_name= group_master_log_name; @@ -2917,7 +2917,7 @@ bool st_relay_log_info::is_until_satisfied() log_name= group_relay_log_name; log_pos= group_relay_log_pos; } - + if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN) { /* @@ -2931,7 +2931,7 @@ bool st_relay_log_info::is_until_satisfied() if (*log_name) { const char *basename= log_name + dirname_length(log_name); - + const char *q= (const char*)(fn_ext(basename)+1); if (strncmp(basename, until_log_name, (int)(q-basename)) == 0) { @@ -2941,11 +2941,11 @@ bool st_relay_log_info::is_until_satisfied() if (log_name_extension < until_log_name_extension) until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_LESS; else - until_log_names_cmp_result= - (log_name_extension > until_log_name_extension) ? + until_log_names_cmp_result= + (log_name_extension > until_log_name_extension) ? UNTIL_LOG_NAMES_CMP_GREATER : UNTIL_LOG_NAMES_CMP_EQUAL ; } - else + else { /* Probably error so we aborting */ sql_print_error("Slave SQL thread is stopped because UNTIL " @@ -2956,8 +2956,8 @@ bool st_relay_log_info::is_until_satisfied() else DBUG_RETURN(until_log_pos == 0); } - - DBUG_RETURN(((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL && + + DBUG_RETURN(((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL && log_pos >= until_log_pos) || until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER)); } @@ -3143,7 +3143,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) pthread_mutex_unlock(&rli->data_lock); thd->server_id = ev->server_id; // use the original server id for logging - thd->set_time(); // time the query + thd->set_time(); // time the query thd->lex->current_select= 0; if (!ev->when) ev->when = time(NULL); @@ -3192,16 +3192,16 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) { exec_res= 0; end_trans(thd, ROLLBACK); - /* chance for concurrent connection to get more locks */ + /* chance for concurrent connection to get more locks */ safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE), - (CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli); + (CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli); pthread_mutex_lock(&rli->data_lock); // because of SHOW STATUS - rli->trans_retries++; + rli->trans_retries++; rli->retried_trans++; pthread_mutex_unlock(&rli->data_lock); DBUG_PRINT("info", ("Slave retries transaction " "rli->trans_retries: %lu", rli->trans_retries)); - } + } } else sql_print_error("Slave SQL thread retried transaction %lu time(s) " @@ -3285,8 +3285,8 @@ pthread_handler_t handle_slave_io(void *arg) pthread_cond_broadcast(&mi->start_cond); DBUG_PRINT("master_info",("log_file_name: '%s' position: %s", - mi->master_log_name, - llstr(mi->master_log_pos,llbuff))); + mi->master_log_name, + llstr(mi->master_log_pos,llbuff))); if (!(mi->mysql = mysql = mysql_init(NULL))) { @@ -3299,9 +3299,9 @@ pthread_handler_t handle_slave_io(void *arg) if (!safe_connect(thd, mysql, mi)) sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',\ replication started in log '%s' at position %s", mi->user, - mi->host, mi->port, - IO_RPL_LOG_NAME, - llstr(mi->master_log_pos,llbuff)); + mi->host, mi->port, + IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); else { sql_print_information("Slave I/O thread killed while connecting to master"); @@ -3339,9 +3339,9 @@ connected: sql_print_error("Failed on request_dump()"); if (io_slave_killed(thd,mi)) { - sql_print_information("Slave I/O thread killed while requesting master \ + sql_print_information("Slave I/O thread killed while requesting master \ dump"); - goto err; + goto err; } mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT; @@ -3351,35 +3351,35 @@ dump"); #endif end_server(mysql); /* - First time retry immediately, assuming that we can recover - right away - if first time fails, sleep between re-tries - hopefuly the admin can fix the problem sometime + First time retry immediately, assuming that we can recover + right away - if first time fails, sleep between re-tries + hopefuly the admin can fix the problem sometime */ if (retry_count++) { - if (retry_count > master_retry_count) - goto err; // Don't retry forever - safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, - (void*)mi); + if (retry_count > master_retry_count) + goto err; // Don't retry forever + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*)mi); } if (io_slave_killed(thd,mi)) { - sql_print_information("Slave I/O thread killed while retrying master \ + sql_print_information("Slave I/O thread killed while retrying master \ dump"); - goto err; + goto err; } thd->proc_info = "Reconnecting after a failed binlog dump request"; if (!suppress_warnings) - sql_print_error("Slave I/O thread: failed dump request, \ + sql_print_error("Slave I/O thread: failed dump request, \ reconnecting to try again, log '%s' at postion %s", IO_RPL_LOG_NAME, - llstr(mi->master_log_pos,llbuff)); + llstr(mi->master_log_pos,llbuff)); if (safe_reconnect(thd, mysql, mi, suppress_warnings) || - io_slave_killed(thd,mi)) + io_slave_killed(thd,mi)) { - sql_print_information("Slave I/O thread killed during or \ + sql_print_information("Slave I/O thread killed during or \ after reconnect"); - goto err; + goto err; } goto connected; @@ -3398,72 +3398,72 @@ after reconnect"); ulong event_len = read_event(mysql, mi, &suppress_warnings); if (io_slave_killed(thd,mi)) { - if (global_system_variables.log_warnings) - sql_print_information("Slave I/O thread killed while reading event"); - goto err; + if (global_system_variables.log_warnings) + sql_print_information("Slave I/O thread killed while reading event"); + goto err; } if (event_len == packet_error) { - uint mysql_error_number= mysql_errno(mysql); - if (mysql_error_number == ER_NET_PACKET_TOO_LARGE) - { - sql_print_error("\ + uint mysql_error_number= mysql_errno(mysql); + if (mysql_error_number == ER_NET_PACKET_TOO_LARGE) + { + sql_print_error("\ Log entry on master is longer than max_allowed_packet (%ld) on \ slave. If the entry is correct, restart the server with a higher value of \ max_allowed_packet", - thd->variables.max_allowed_packet); - goto err; - } - if (mysql_error_number == ER_MASTER_FATAL_ERROR_READING_BINLOG) - { - sql_print_error(ER(mysql_error_number), mysql_error_number, - mysql_error(mysql)); - goto err; - } + thd->variables.max_allowed_packet); + goto err; + } + if (mysql_error_number == ER_MASTER_FATAL_ERROR_READING_BINLOG) + { + sql_print_error(ER(mysql_error_number), mysql_error_number, + mysql_error(mysql)); + goto err; + } mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT; - thd->proc_info = "Waiting to reconnect after a failed master event read"; + thd->proc_info = "Waiting to reconnect after a failed master event read"; #ifdef SIGNAL_WITH_VIO_CLOSE thd->clear_active_vio(); #endif - end_server(mysql); - if (retry_count++) - { - if (retry_count > master_retry_count) - goto err; // Don't retry forever - safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, - (void*) mi); - } - if (io_slave_killed(thd,mi)) - { - if (global_system_variables.log_warnings) - sql_print_information("Slave I/O thread killed while waiting to \ + end_server(mysql); + if (retry_count++) + { + if (retry_count > master_retry_count) + goto err; // Don't retry forever + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*) mi); + } + if (io_slave_killed(thd,mi)) + { + if (global_system_variables.log_warnings) + sql_print_information("Slave I/O thread killed while waiting to \ reconnect after a failed read"); - goto err; - } - thd->proc_info = "Reconnecting after a failed master event read"; - if (!suppress_warnings) - sql_print_information("Slave I/O thread: Failed reading log event, \ + goto err; + } + thd->proc_info = "Reconnecting after a failed master event read"; + if (!suppress_warnings) + sql_print_information("Slave I/O thread: Failed reading log event, \ reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME, - llstr(mi->master_log_pos, llbuff)); - if (safe_reconnect(thd, mysql, mi, suppress_warnings) || - io_slave_killed(thd,mi)) - { - if (global_system_variables.log_warnings) - sql_print_information("Slave I/O thread killed during or after a \ + llstr(mi->master_log_pos, llbuff)); + if (safe_reconnect(thd, mysql, mi, suppress_warnings) || + io_slave_killed(thd,mi)) + { + if (global_system_variables.log_warnings) + sql_print_information("Slave I/O thread killed during or after a \ reconnect done to recover from failed read"); - goto err; - } - goto connected; + goto err; + } + goto connected; } // if (event_len == packet_error) - retry_count=0; // ok event, reset retry counter + retry_count=0; // ok event, reset retry counter thd->proc_info = "Queueing master event to the relay log"; if (queue_event(mi,(const char*)mysql->net.read_pos + 1, - event_len)) + event_len)) { - sql_print_error("Slave I/O thread could not queue event from master"); - goto err; + sql_print_error("Slave I/O thread could not queue event from master"); + goto err; } if (flush_master_info(mi, 1)) { @@ -3489,27 +3489,27 @@ reconnect done to recover from failed read"); ignore_log_space_limit=%d", llstr(rli->log_space_limit,llbuf1), llstr(rli->log_space_total,llbuf2), - (int) rli->ignore_log_space_limit)); + (int) rli->ignore_log_space_limit)); } #endif if (rli->log_space_limit && rli->log_space_limit < - rli->log_space_total && + rli->log_space_total && !rli->ignore_log_space_limit) - if (wait_for_relay_log_space(rli)) - { - sql_print_error("Slave I/O thread aborted while waiting for relay \ + if (wait_for_relay_log_space(rli)) + { + sql_print_error("Slave I/O thread aborted while waiting for relay \ log space"); - goto err; - } - } + goto err; + } + } } // error = 0; err: // print the current replication position sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s", - IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); + IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query = thd->db = 0; // extra safety thd->query_length= thd->db_length= 0; @@ -3549,11 +3549,11 @@ err: THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); - pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done + pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done pthread_mutex_unlock(&mi->run_lock); my_thread_end(); pthread_exit(0); - DBUG_RETURN(0); // Can't return anything here + DBUG_RETURN(0); // Can't return anything here } @@ -3561,7 +3561,7 @@ err: pthread_handler_t handle_slave_sql(void *arg) { - THD *thd; /* needs to be first for thread_stack */ + THD *thd; /* needs to be first for thread_stack */ char llbuff[22],llbuff1[22]; RELAY_LOG_INFO* rli = &((MASTER_INFO*)arg)->rli; @@ -3575,13 +3575,13 @@ pthread_handler_t handle_slave_sql(void *arg) pthread_mutex_lock(&rli->run_lock); DBUG_ASSERT(!rli->slave_running); errmsg= 0; -#ifndef DBUG_OFF +#ifndef DBUG_OFF rli->events_till_abort = abort_slave_event_count; -#endif +#endif thd = new THD; // note that contructor of THD uses DBUG_ ! thd->thread_stack = (char*)&thd; // remember where our stack is - + /* Inform waiting threads that slave has started */ rli->slave_run_id++; @@ -3635,13 +3635,13 @@ pthread_handler_t handle_slave_sql(void *arg) rli->trans_retries= 0; // start from "no error" if (init_relay_log_pos(rli, - rli->group_relay_log_name, - rli->group_relay_log_pos, - 1 /*need data lock*/, &errmsg, + rli->group_relay_log_name, + rli->group_relay_log_pos, + 1 /*need data lock*/, &errmsg, 1 /*look for a description_event*/)) { sql_print_error("Error initializing relay log position: %s", - errmsg); + errmsg); goto err; } THD_CHECK_SENTRY(thd); @@ -3649,7 +3649,7 @@ pthread_handler_t handle_slave_sql(void *arg) { char llbuf1[22], llbuf2[22]; DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s", - llstr(my_b_tell(rli->cur_log),llbuf1), + llstr(my_b_tell(rli->cur_log),llbuf1), llstr(rli->event_relay_log_pos,llbuf2))); DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); /* @@ -3672,13 +3672,13 @@ pthread_handler_t handle_slave_sql(void *arg) DBUG_ASSERT(rli->sql_thd == thd); DBUG_PRINT("master_info",("log_file_name: %s position: %s", - rli->group_master_log_name, - llstr(rli->group_master_log_pos,llbuff))); + rli->group_master_log_name, + llstr(rli->group_master_log_pos,llbuff))); if (global_system_variables.log_warnings) sql_print_information("Slave SQL thread initialized, starting replication in \ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, - llstr(rli->group_master_log_pos,llbuff),rli->group_relay_log_name, - llstr(rli->group_relay_log_pos,llbuff1)); + llstr(rli->group_master_log_pos,llbuff),rli->group_relay_log_name, + llstr(rli->group_relay_log_pos,llbuff1)); /* execute init_slave variable */ if (sys_init_slave.value_length) @@ -3742,8 +3742,8 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ /* Thread stopped. Print the current replication position to the log */ sql_print_information("Slave SQL thread exiting, replication stopped in log " - "'%s' at position %s", - RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff)); + "'%s' at position %s", + RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff)); err: @@ -3760,7 +3760,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ should already have done these assignments (each event which sets these variables is supposed to set them to 0 before terminating)). */ - thd->query= thd->db= thd->catalog= 0; + thd->query= thd->db= thd->catalog= 0; thd->query_length= thd->db_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->proc_info = "Waiting for slave mutex on exit"; @@ -3769,7 +3769,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ pthread_mutex_lock(&rli->data_lock); DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun /* When master_pos_wait() wakes up it will check this and terminate */ - rli->slave_running= 0; + rli->slave_running= 0; /* Forget the relay log's format */ delete rli->relay_log.description_event_for_exec; rli->relay_log.description_event_for_exec= 0; @@ -3801,7 +3801,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ pthread_mutex_unlock(&rli->run_lock); my_thread_end(); pthread_exit(0); - DBUG_RETURN(0); // Can't return anything here + DBUG_RETURN(0); // Can't return anything here } @@ -3830,11 +3830,11 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) thd->file_id = cev->file_id = mi->file_id++; thd->server_id = cev->server_id; cev_not_written = 1; - + if (unlikely(net_request_file(net,cev->fname))) { sql_print_error("Slave I/O: failed requesting download of '%s'", - cev->fname); + cev->fname); goto err; } @@ -3845,18 +3845,18 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) */ { Append_block_log_event aev(thd,0,0,0,0); - + for (;;) { if (unlikely((num_bytes=my_net_read(net)) == packet_error)) { - sql_print_error("Network read error downloading '%s' from master", - cev->fname); - goto err; + sql_print_error("Network read error downloading '%s' from master", + cev->fname); + goto err; } if (unlikely(!num_bytes)) /* eof */ { - net_write_command(net, 0, "", 0, "", 0);/* 3.23 master wants it */ + net_write_command(net, 0, "", 0, "", 0);/* 3.23 master wants it */ /* If we wrote Create_file_log_event, then we need to write Execute_load_log_event. If we did not write Create_file_log_event, @@ -3864,43 +3864,43 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) INFILE had not existed, i.e. write nothing. */ if (unlikely(cev_not_written)) - break; - Execute_load_log_event xev(thd,0,0); - xev.log_pos = cev->log_pos; - if (unlikely(mi->rli.relay_log.append(&xev))) - { - sql_print_error("Slave I/O: error writing Exec_load event to \ + break; + Execute_load_log_event xev(thd,0,0); + xev.log_pos = cev->log_pos; + if (unlikely(mi->rli.relay_log.append(&xev))) + { + sql_print_error("Slave I/O: error writing Exec_load event to \ relay log"); - goto err; - } - mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); - break; + goto err; + } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + break; } if (unlikely(cev_not_written)) { - cev->block = (char*)net->read_pos; - cev->block_len = num_bytes; - if (unlikely(mi->rli.relay_log.append(cev))) - { - sql_print_error("Slave I/O: error writing Create_file event to \ + cev->block = (char*)net->read_pos; + cev->block_len = num_bytes; + if (unlikely(mi->rli.relay_log.append(cev))) + { + sql_print_error("Slave I/O: error writing Create_file event to \ relay log"); - goto err; - } - cev_not_written=0; - mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + goto err; + } + cev_not_written=0; + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); } else { - aev.block = (char*)net->read_pos; - aev.block_len = num_bytes; - aev.log_pos = cev->log_pos; - if (unlikely(mi->rli.relay_log.append(&aev))) - { - sql_print_error("Slave I/O: error writing Append_block event to \ + aev.block = (char*)net->read_pos; + aev.block_len = num_bytes; + aev.log_pos = cev->log_pos; + if (unlikely(mi->rli.relay_log.append(&aev))) + { + sql_print_error("Slave I/O: error writing Append_block event to \ relay log"); - goto err; - } - mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total) ; + goto err; + } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total) ; } } } @@ -3915,8 +3915,8 @@ err: SYNOPSIS process_io_rotate() - mi master_info for the slave - rev The rotate log event read from the binary log + mi master_info for the slave + rev The rotate log event read from the binary log DESCRIPTION Updates the master info with the place in the next binary @@ -3927,8 +3927,8 @@ err: We assume we already locked mi->data_lock RETURN VALUES - 0 ok - 1 Log event is illegal + 0 ok + 1 Log event is illegal */ @@ -3944,7 +3944,7 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev) memcpy(mi->master_log_name, rev->new_log_ident, rev->ident_len+1); mi->master_log_pos= rev->pos; DBUG_PRINT("info", ("master_log_pos: '%s' %d", - mi->master_log_name, (ulong) mi->master_log_pos)); + mi->master_log_name, (ulong) mi->master_log_pos)); #ifndef DBUG_OFF /* If we do not do this, we will be getting the first @@ -3981,7 +3981,7 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev) copied from MySQL 4.0. */ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf, - ulong event_len) + ulong event_len) { const char *errmsg = 0; ulong inc_pos; @@ -4025,7 +4025,7 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf, { sql_print_error("Read invalid event from master: '%s',\ master could be corrupt but a more likely cause of this is a bug", - errmsg); + errmsg); my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR)); DBUG_RETURN(1); } @@ -4071,8 +4071,8 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf, } if (likely(!ignore_event)) { - if (ev->log_pos) - /* + if (ev->log_pos) + /* Don't do it for fake Rotate events (see comment in Log_event::Log_event(const char* buf...) in log_event.cc). */ @@ -4097,7 +4097,7 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf, from queue_binlog_ver_1_event(), with some affordable simplifications. */ static int queue_binlog_ver_3_event(MASTER_INFO *mi, const char *buf, - ulong event_len) + ulong event_len) { const char *errmsg = 0; ulong inc_pos; @@ -4112,7 +4112,7 @@ static int queue_binlog_ver_3_event(MASTER_INFO *mi, const char *buf, { sql_print_error("Read invalid event from master: '%s',\ master could be corrupt but a more likely cause of this is a bug", - errmsg); + errmsg); my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR)); DBUG_RETURN(1); } @@ -4155,13 +4155,13 @@ err: (exactly, slave's) format. To do the conversion, we create a 5.0 event from the 3.23/4.0 bytes, then write this event to the relay log. - TODO: + TODO: Test this code before release - it has to be tested on a separate setup with 3.23 master or 4.0 master */ static int queue_old_event(MASTER_INFO *mi, const char *buf, - ulong event_len) + ulong event_len) { DBUG_ENTER("queue_old_event"); @@ -4173,7 +4173,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf, DBUG_RETURN(queue_binlog_ver_3_event(mi,buf,event_len)); default: /* unsupported format; eg version 2 */ DBUG_PRINT("info",("unsupported binlog format %d in queue_old_event()", - mi->rli.relay_log.description_event_for_queue->binlog_version)); + mi->rli.relay_log.description_event_for_queue->binlog_version)); DBUG_RETURN(1); } } @@ -4211,7 +4211,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) cleaning is already done on a per-master-thread basis (as the master server is shutting down cleanly, it has written all DROP TEMPORARY TABLE prepared statements' deletion are TODO only when we binlog prep stmts). - + We don't even increment mi->master_log_pos, because we may be just after a Rotate event. Btw, in a few milliseconds we are going to have a Start event from the next binlog (unless the master is presently running @@ -4220,7 +4220,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) goto err; case ROTATE_EVENT: { - Rotate_log_event rev(buf,event_len,mi->rli.relay_log.description_event_for_queue); + Rotate_log_event rev(buf,event_len,mi->rli.relay_log.description_event_for_queue); if (unlikely(process_io_rotate(mi,&rev))) { error= 1; @@ -4255,17 +4255,17 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) } delete mi->rli.relay_log.description_event_for_queue; mi->rli.relay_log.description_event_for_queue= tmp; - /* + /* Though this does some conversion to the slave's format, this will - preserve the master's binlog format version, and number of event types. + preserve the master's binlog format version, and number of event types. */ - /* + /* If the event was not requested by the slave (the slave did not ask for - it), i.e. has end_log_pos=0, we do not increment mi->master_log_pos + it), i.e. has end_log_pos=0, we do not increment mi->master_log_pos */ inc_pos= uint4korr(buf+LOG_POS_OFFSET) ? event_len : 0; DBUG_PRINT("info",("binlog format is now %d", - mi->rli.relay_log.description_event_for_queue->binlog_version)); + mi->rli.relay_log.description_event_for_queue->binlog_version)); } break; @@ -4274,8 +4274,8 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) break; } - /* - If this event is originating from this server, don't queue it. + /* + If this event is originating from this server, don't queue it. We don't check this for 3.23 events because it's simpler like this; 3.23 will be filtered anyway by the SQL slave thread which also tests the server id (we must also keep this test in the SQL thread, in case somebody @@ -4317,7 +4317,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) } rli->relay_log.signal_update(); // the slave SQL thread needs to re-check DBUG_PRINT("info", ("master_log_pos: %d, event originating from the same server, ignored", (ulong) mi->master_log_pos)); - } + } else { /* write the event to the relay log */ @@ -4376,13 +4376,13 @@ void end_relay_log_info(RELAY_LOG_INFO* rli) SYNPOSIS safe_connect() - thd Thread handler for slave - mysql MySQL connection handle - mi Replication handle + thd Thread handler for slave + mysql MySQL connection handle + mi Replication handle RETURN - 0 ok - # Error + 0 ok + # Error */ static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) @@ -4403,10 +4403,10 @@ static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) */ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, - bool reconnect, bool suppress_warnings) + bool reconnect, bool suppress_warnings) { int slave_was_killed; - int last_errno= -2; // impossible error + int last_errno= -2; // impossible error ulong err_count=0; char llbuff[22]; DBUG_ENTER("connect_to_master"); @@ -4416,16 +4416,16 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, #endif ulong client_flag= CLIENT_REMEMBER_OPTIONS; if (opt_slave_compressed_protocol) - client_flag=CLIENT_COMPRESS; /* We will use compression */ + client_flag=CLIENT_COMPRESS; /* We will use compression */ mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout); mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (char *) &slave_net_timeout); - + #ifdef HAVE_OPENSSL if (mi->ssl) - mysql_ssl_set(mysql, + mysql_ssl_set(mysql, mi->ssl_key[0]?mi->ssl_key:0, - mi->ssl_cert[0]?mi->ssl_cert:0, + mi->ssl_cert[0]?mi->ssl_cert:0, mi->ssl_ca[0]?mi->ssl_ca:0, mi->ssl_capath[0]?mi->ssl_capath:0, mi->ssl_cipher[0]?mi->ssl_cipher:0); @@ -4436,9 +4436,9 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir); while (!(slave_was_killed = io_slave_killed(thd,mi)) && - (reconnect ? mysql_reconnect(mysql) != 0 : - mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0, - mi->port, 0, client_flag) == 0)) + (reconnect ? mysql_reconnect(mysql) != 0 : + mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0, + mi->port, 0, client_flag) == 0)) { /* Don't repeat last error */ if ((int)mysql_errno(mysql) != last_errno) @@ -4448,11 +4448,11 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, sql_print_error("Slave I/O thread: error %s to master \ '%s@%s:%d': \ Error: '%s' errno: %d retry-time: %d retries: %d", - (reconnect ? "reconnecting" : "connecting"), - mi->user,mi->host,mi->port, - mysql_error(mysql), last_errno, - mi->connect_retry, - master_retry_count); + (reconnect ? "reconnecting" : "connecting"), + mi->user,mi->host,mi->port, + mysql_error(mysql), last_errno, + mi->connect_retry, + master_retry_count); } /* By default we try forever. The reason is that failure will trigger @@ -4468,19 +4468,19 @@ Error: '%s' errno: %d retry-time: %d retries: %d", break; } safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, - (void*)mi); + (void*)mi); } if (!slave_was_killed) { if (reconnect) - { + { if (!suppress_warnings && global_system_variables.log_warnings) - sql_print_information("Slave: connected to master '%s@%s:%d',\ + sql_print_information("Slave: connected to master '%s@%s:%d',\ replication resumed in log '%s' at position %s", mi->user, - mi->host, mi->port, - IO_RPL_LOG_NAME, - llstr(mi->master_log_pos,llbuff)); + mi->host, mi->port, + IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); } else { @@ -4490,7 +4490,7 @@ replication resumed in log '%s' at position %s", mi->user, } #ifdef SIGNAL_WITH_VIO_CLOSE thd->set_active_vio(mysql->net.vio); -#endif +#endif } mysql->reconnect= 1; DBUG_PRINT("exit",("slave_was_killed: %d", slave_was_killed)); @@ -4507,7 +4507,7 @@ replication resumed in log '%s' at position %s", mi->user, */ static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, - bool suppress_warnings) + bool suppress_warnings) { DBUG_ENTER("safe_reconnect"); DBUG_RETURN(connect_to_master(thd, mysql, mi, 1, suppress_warnings)); @@ -4520,7 +4520,7 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, SYNOPSIS flush_relay_log_info() - rli Relay log information + rli Relay log information NOTES - As this is only called by the slave thread, we don't need to @@ -4539,8 +4539,8 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, longlong2str. RETURN VALUES - 0 ok - 1 write error + 0 ok + 1 write error */ bool flush_relay_log_info(RELAY_LOG_INFO* rli) @@ -4585,12 +4585,12 @@ static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg) IO_CACHE *cur_log = rli->cur_log=&rli->cache_buf; if ((rli->cur_log_fd=open_binlog(cur_log,rli->event_relay_log_name, - errmsg)) <0) + errmsg)) <0) DBUG_RETURN(0); /* We want to start exactly where we was before: - relay_log_pos Current log pos - pending Number of bytes already processed from the event + relay_log_pos Current log pos + pending Number of bytes already processed from the event */ rli->event_relay_log_pos= max(rli->event_relay_log_pos, BIN_LOG_HEADER_SIZE); my_b_seek(cur_log,rli->event_relay_log_pos); @@ -4622,7 +4622,7 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) pthread_cond_wait() with the non-data_lock mutex */ safe_mutex_assert_owner(&rli->data_lock); - + while (!sql_slave_killed(thd,rli)) { /* @@ -4643,17 +4643,17 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) pthread_mutex_lock(log_lock); /* - Reading xxx_file_id is safe because the log will only - be rotated when we hold relay_log.LOCK_log + Reading xxx_file_id is safe because the log will only + be rotated when we hold relay_log.LOCK_log */ if (rli->relay_log.get_open_count() != rli->cur_log_old_open_count) { - // The master has switched to a new log file; Reopen the old log file - cur_log=reopen_relay_log(rli, &errmsg); - pthread_mutex_unlock(log_lock); - if (!cur_log) // No more log files - goto err; - hot_log=0; // Using old binary log + // The master has switched to a new log file; Reopen the old log file + cur_log=reopen_relay_log(rli, &errmsg); + pthread_mutex_unlock(log_lock); + if (!cur_log) // No more log files + goto err; + hot_log=0; // Using old binary log } } @@ -4691,25 +4691,25 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) */ rli->future_event_relay_log_pos= my_b_tell(cur_log); if (hot_log) - pthread_mutex_unlock(log_lock); + pthread_mutex_unlock(log_lock); DBUG_RETURN(ev); } DBUG_ASSERT(thd==rli->sql_thd); - if (opt_reckless_slave) // For mysql-test + if (opt_reckless_slave) // For mysql-test cur_log->error = 0; if (cur_log->error < 0) { errmsg = "slave SQL thread aborted because of I/O error"; if (hot_log) - pthread_mutex_unlock(log_lock); + pthread_mutex_unlock(log_lock); goto err; } if (!cur_log->error) /* EOF */ { /* - On a hot log, EOF means that there are no more updates to - process and we must block until I/O thread adds some and - signals us to continue + On a hot log, EOF means that there are no more updates to + process and we must block until I/O thread adds some and + signals us to continue */ if (hot_log) { @@ -4728,7 +4728,7 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) time_t save_timestamp= rli->last_master_timestamp; rli->last_master_timestamp= 0; - DBUG_ASSERT(rli->relay_log.get_open_count() == + DBUG_ASSERT(rli->relay_log.get_open_count() == rli->cur_log_old_open_count); if (rli->ign_master_log_name_end[0]) @@ -4750,14 +4750,14 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) DBUG_RETURN(ev); } - /* - We can, and should release data_lock while we are waiting for - update. If we do not, show slave status will block - */ - pthread_mutex_unlock(&rli->data_lock); + /* + We can, and should release data_lock while we are waiting for + update. If we do not, show slave status will block + */ + pthread_mutex_unlock(&rli->data_lock); /* - Possible deadlock : + Possible deadlock : - the I/O thread has reached log_space_limit - the SQL thread has read all relay logs, but cannot purge for some reason: @@ -4769,10 +4769,10 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) the I/O thread to temporarily ignore the log_space_limit constraint, because we do not want the I/O thread to block because of space (it's ok if it blocks for any other reason (e.g. because the - master does not send anything). Then the I/O thread stops waiting + master does not send anything). Then the I/O thread stops waiting and reads more events. The SQL thread decides when the I/O thread should take log_space_limit - into account again : ignore_log_space_limit is reset to 0 + into account again : ignore_log_space_limit is reset to 0 in purge_first_log (when the SQL thread purges the just-read relay log), and also when the SQL thread starts. We should also reset ignore_log_space_limit to 0 when the user does RESET SLAVE, but in @@ -4782,7 +4782,7 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) */ pthread_mutex_lock(&rli->log_space_lock); // prevent the I/O thread from blocking next times - rli->ignore_log_space_limit= 1; + rli->ignore_log_space_limit= 1; /* If the I/O thread is blocked, unblock it. Ok to broadcast after unlock, because the mutex is only destroyed in @@ -4796,21 +4796,21 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) // re-acquire data lock since we released it earlier pthread_mutex_lock(&rli->data_lock); rli->last_master_timestamp= save_timestamp; - continue; + continue; } /* - If the log was not hot, we need to move to the next log in - sequence. The next log could be hot or cold, we deal with both - cases separately after doing some common initialization + If the log was not hot, we need to move to the next log in + sequence. The next log could be hot or cold, we deal with both + cases separately after doing some common initialization */ end_io_cache(cur_log); DBUG_ASSERT(rli->cur_log_fd >= 0); my_close(rli->cur_log_fd, MYF(MY_WME)); rli->cur_log_fd = -1; - + if (relay_log_purge) { - /* + /* purge_first_log will properly set up relay log coordinates in rli. If the group's coordinates are equal to the event's coordinates (i.e. the relay log was not rotated in the middle of a group), @@ -4821,33 +4821,33 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) - I see no better detection method - purge_first_log is not called that often */ - if (rli->relay_log.purge_first_log + if (rli->relay_log.purge_first_log (rli, rli->group_relay_log_pos == rli->event_relay_log_pos && !strcmp(rli->group_relay_log_name,rli->event_relay_log_name))) - { - errmsg = "Error purging processed logs"; - goto err; - } + { + errmsg = "Error purging processed logs"; + goto err; + } } else { - /* - If hot_log is set, then we already have a lock on - LOCK_log. If not, we have to get the lock. + /* + If hot_log is set, then we already have a lock on + LOCK_log. If not, we have to get the lock. - According to Sasha, the only time this code will ever be executed - is if we are recovering from a bug. - */ - if (rli->relay_log.find_next_log(&rli->linfo, !hot_log)) - { - errmsg = "error switching to the next log"; - goto err; - } - rli->event_relay_log_pos = BIN_LOG_HEADER_SIZE; - strmake(rli->event_relay_log_name,rli->linfo.log_file_name, - sizeof(rli->event_relay_log_name)-1); - flush_relay_log_info(rli); + According to Sasha, the only time this code will ever be executed + is if we are recovering from a bug. + */ + if (rli->relay_log.find_next_log(&rli->linfo, !hot_log)) + { + errmsg = "error switching to the next log"; + goto err; + } + rli->event_relay_log_pos = BIN_LOG_HEADER_SIZE; + strmake(rli->event_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->event_relay_log_name)-1); + flush_relay_log_info(rli); } /* @@ -4866,66 +4866,66 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) if (rli->relay_log.is_active(rli->linfo.log_file_name)) { #ifdef EXTRA_DEBUG - if (global_system_variables.log_warnings) - sql_print_information("next log '%s' is currently active", + if (global_system_variables.log_warnings) + sql_print_information("next log '%s' is currently active", rli->linfo.log_file_name); -#endif - rli->cur_log= cur_log= rli->relay_log.get_log_file(); - rli->cur_log_old_open_count= rli->relay_log.get_open_count(); - DBUG_ASSERT(rli->cur_log_fd == -1); - - /* - Read pointer has to be at the start since we are the only - reader. +#endif + rli->cur_log= cur_log= rli->relay_log.get_log_file(); + rli->cur_log_old_open_count= rli->relay_log.get_open_count(); + DBUG_ASSERT(rli->cur_log_fd == -1); + + /* + Read pointer has to be at the start since we are the only + reader. We must keep the LOCK_log to read the 4 first bytes, as this is a hot log (same as when we call read_log_event() above: for a hot log we take the mutex). - */ - if (check_binlog_magic(cur_log,&errmsg)) + */ + if (check_binlog_magic(cur_log,&errmsg)) { if (!hot_log) pthread_mutex_unlock(log_lock); - goto err; + goto err; } if (!hot_log) pthread_mutex_unlock(log_lock); - continue; + continue; } if (!hot_log) pthread_mutex_unlock(log_lock); /* - if we get here, the log was not hot, so we will have to open it - ourselves. We are sure that the log is still not hot now (a log can get - from hot to cold, but not from cold to hot). No need for LOCK_log. + if we get here, the log was not hot, so we will have to open it + ourselves. We are sure that the log is still not hot now (a log can get + from hot to cold, but not from cold to hot). No need for LOCK_log. */ #ifdef EXTRA_DEBUG if (global_system_variables.log_warnings) - sql_print_information("next log '%s' is not active", + sql_print_information("next log '%s' is not active", rli->linfo.log_file_name); -#endif +#endif // open_binlog() will check the magic header if ((rli->cur_log_fd=open_binlog(cur_log,rli->linfo.log_file_name, - &errmsg)) <0) - goto err; + &errmsg)) <0) + goto err; } else { /* - Read failed with a non-EOF error. - TODO: come up with something better to handle this error + Read failed with a non-EOF error. + TODO: come up with something better to handle this error */ if (hot_log) - pthread_mutex_unlock(log_lock); + pthread_mutex_unlock(log_lock); sql_print_error("Slave SQL thread: I/O error reading \ event(errno: %d cur_log->error: %d)", - my_errno,cur_log->error); + my_errno,cur_log->error); // set read position to the beginning of the event my_b_seek(cur_log,rli->event_relay_log_pos); /* otherwise, we have had a partial read */ errmsg = "Aborting slave SQL thread because of partial event read"; - break; // To end of function + break; // To end of function } } if (!errmsg && global_system_variables.log_warnings) { - sql_print_information("Error reading relay log event: %s", + sql_print_information("Error reading relay log event: %s", "slave SQL thread was killed"); DBUG_RETURN(0); } @@ -4939,7 +4939,7 @@ err: /* Rotate a relay log (this is used only by FLUSH LOGS; the automatic rotation because of size is simpler because when we do it we already have all relevant - locks; here we don't, so this function is mainly taking locks). + locks; here we don't, so this function is mainly taking locks). Returns nothing as we cannot catch any error (MYSQL_BIN_LOG::new_file() is void). */ @@ -4952,7 +4952,7 @@ void rotate_relay_log(MASTER_INFO* mi) /* We don't lock rli->run_lock. This would lead to deadlocks. */ pthread_mutex_lock(&mi->run_lock); - /* + /* We need to test inited because otherwise, new_file() will attempt to lock LOCK_log, which may not be inited (if we're not a slave). */ @@ -5001,7 +5001,7 @@ struct st_reload_entry Sorted array of table names, please keep it sorted since we are using bsearch() on it below. */ -static st_reload_entry s_mysql_tables[] = +static st_reload_entry s_mysql_tables[] = { { "columns_priv", st_relay_log_info::RELOAD_GRANT_F }, { "db", st_relay_log_info::RELOAD_ACCESS_F }, @@ -5024,7 +5024,7 @@ static int reload_entry_compare(const void *lhs, const void *rhs) } void st_relay_log_info::touching_table(char const* db, char const* table, - ulong table_id) + ulong table_id) { DBUG_ENTER("st_relay_log_info::touching_table"); @@ -5051,7 +5051,7 @@ void st_relay_log_info::touching_table(char const* db, char const* table, DBUG_VOID_RETURN; } -void st_relay_log_info::transaction_end(THD* thd) +void st_relay_log_info::transaction_end(THD* thd) { DBUG_ENTER("st_relay_log_info::transaction_end"); @@ -5106,4 +5106,3 @@ template class I_List_iterator; #endif #endif /* HAVE_REPLICATION */ - From 8710951295dd45fe94f8167a7291e51c80bdecc8 Mon Sep 17 00:00:00 2001 From: "grog@eucla.lemis.com" <> Date: Fri, 7 Jul 2006 17:18:52 +0930 Subject: [PATCH 16/32] slave.cc: BUG#20850: Assert during slave shutdown in many rpl_* tests. This was caused by a race condition at the end of handle_slave_io which under some circumstances allowed the cleanup to proceed before the thread had completed. --- sql/slave.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index 0ac4cda7b3b..8f6ce6d580d 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3533,15 +3533,12 @@ err: write_ignored_events_info_to_relay_log(thd, mi); thd->proc_info = "Waiting for slave mutex on exit"; pthread_mutex_lock(&mi->run_lock); - mi->slave_running = 0; - mi->io_thd = 0; /* Forget the relay log's format */ delete mi->rli.relay_log.description_event_for_queue; mi->rli.relay_log.description_event_for_queue= 0; // TODO: make rpl_status part of MASTER_INFO change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); - mi->abort_slave = 0; // TODO: check if this is needed DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because net.vio is 0 close_thread_tables(thd, 0); @@ -3552,6 +3549,9 @@ err: pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done pthread_mutex_unlock(&mi->run_lock); my_thread_end(); + mi->abort_slave = 0; + mi->slave_running = 0; + mi->io_thd = 0; pthread_exit(0); DBUG_RETURN(0); // Can't return anything here } From fe4a22f36bc9d781cdcbbc16c453cc6ca1e75354 Mon Sep 17 00:00:00 2001 From: "mats@romeo.(none)" <> Date: Fri, 7 Jul 2006 15:59:17 +0200 Subject: [PATCH 17/32] Correcting signature for my_strndup to be able to build on Windows in pushbuild on Replication/Backup team tree. --- include/my_sys.h | 4 ++-- mysys/safemalloc.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index 2dc4053f70d..4b31f6bcd2b 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -163,7 +163,7 @@ extern gptr my_realloc(gptr oldpoint,uint Size,myf MyFlags); extern void my_no_flags_free(gptr ptr); extern gptr my_memdup(const byte *from,uint length,myf MyFlags); extern char *my_strdup(const char *from,myf MyFlags); -extern char *my_strndup(const byte *from, uint length, +extern char *my_strndup(const char *from, uint length, myf MyFlags); /* we do use FG (as a no-op) in below so that a typo on FG is caught */ #define my_free(PTR,FG) ((void)FG,my_no_flags_free(PTR)) @@ -587,7 +587,7 @@ extern gptr _my_memdup(const byte *from,uint length, const char *sFile, uint uLine,myf MyFlag); extern my_string _my_strdup(const char *from, const char *sFile, uint uLine, myf MyFlag); -extern char *_my_strndup(const byte *from, uint length, +extern char *_my_strndup(const char *from, uint length, const char *sFile, uint uLine, myf MyFlag); diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c index e40fd751037..518a6a5fdd0 100644 --- a/mysys/safemalloc.c +++ b/mysys/safemalloc.c @@ -525,7 +525,7 @@ char *_my_strdup(const char *from, const char *filename, uint lineno, } /* _my_strdup */ -char *_my_strndup(const byte *from, uint length, +char *_my_strndup(const char *from, uint length, const char *filename, uint lineno, myf MyFlags) { From 4044fb8cc7e1c3f2c0124d99daf656dbdae0f2c0 Mon Sep 17 00:00:00 2001 From: "mats@romeo.(none)" <> Date: Fri, 7 Jul 2006 18:31:00 +0200 Subject: [PATCH 18/32] Bug#20850 (Assert during slave shutdown in many rpl_* tests): Previous patch didn't work: moving variable settings inside critical region. --- sql/slave.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index 8f6ce6d580d..19060eba2d4 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3546,12 +3546,12 @@ err: THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); - pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done - pthread_mutex_unlock(&mi->run_lock); - my_thread_end(); mi->abort_slave = 0; mi->slave_running = 0; mi->io_thd = 0; + pthread_mutex_unlock(&mi->run_lock); + pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done + my_thread_end(); pthread_exit(0); DBUG_RETURN(0); // Can't return anything here } From c18b7d191a5d5d6c353f1b8e704288feed5bbe4a Mon Sep 17 00:00:00 2001 From: "mats@romeo.(none)" <> Date: Fri, 7 Jul 2006 21:03:19 +0200 Subject: [PATCH 19/32] Fixing usage of my_strndup() to new prototype. --- sql/ha_federated.cc | 7 +++---- sql/log_event.cc | 7 ++----- sql/set_var.cc | 4 ++-- sql/set_var.h | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index 91111a433dc..be923591cac 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -629,10 +629,9 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, DBUG_PRINT("info", ("Length: %d", table->s->connect_string.length)); DBUG_PRINT("info", ("String: '%.*s'", table->s->connect_string.length, table->s->connect_string.str)); - share->scheme= my_strndup((const byte*)table->s-> - connect_string.str, - table->s->connect_string.length, - MYF(0)); + share->scheme= my_strndup(table->s->connect_string.str, + table->s->connect_string.length, + MYF(0)); // Add a null for later termination of table name share->scheme[table->s->connect_string.length]= 0; diff --git a/sql/log_event.cc b/sql/log_event.cc index 36805e0043d..02bf22429f2 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3175,8 +3175,7 @@ Rotate_log_event::Rotate_log_event(const char* new_log_ident_arg, llstr(pos_arg, buff), flags)); #endif if (flags & DUP_NAME) - new_log_ident= my_strndup((const byte*) new_log_ident_arg, - ident_len, MYF(MY_WME)); + new_log_ident= my_strndup(new_log_ident_arg, ident_len, MYF(MY_WME)); DBUG_VOID_RETURN; } #endif @@ -3199,9 +3198,7 @@ Rotate_log_event::Rotate_log_event(const char* buf, uint event_len, (header_size+post_header_len)); ident_offset = post_header_len; set_if_smaller(ident_len,FN_REFLEN-1); - new_log_ident= my_strndup((byte*) buf + ident_offset, - (uint) ident_len, - MYF(MY_WME)); + new_log_ident= my_strndup(buf + ident_offset, (uint) ident_len, MYF(MY_WME)); DBUG_VOID_RETURN; } diff --git a/sql/set_var.cc b/sql/set_var.cc index b0ecc7eccef..bb9ef4d453f 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1097,7 +1097,7 @@ bool update_sys_var_str(sys_var_str *var_str, rw_lock_t *var_mutex, uint new_length= (var ? var->value->str_value.length() : 0); if (!old_value) old_value= (char*) ""; - if (!(res= my_strndup((byte*)old_value, new_length, MYF(0)))) + if (!(res= my_strndup(old_value, new_length, MYF(0)))) return 1; /* Replace the old value in such a way that the any thread using @@ -2632,7 +2632,7 @@ bool update_sys_var_str_path(THD *thd, sys_var_str *var_str, old_value= make_default_log_name(buff, log_ext); str_length= strlen(old_value); } - if (!(res= my_strndup((byte*)old_value, str_length, MYF(MY_FAE+MY_WME)))) + if (!(res= my_strndup(old_value, str_length, MYF(MY_FAE+MY_WME)))) { result= 1; goto err; diff --git a/sql/set_var.h b/sql/set_var.h index d01ce833d14..b968b4c7840 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -1055,7 +1055,7 @@ public: uint name_length_arg, gptr data_arg) :name_length(name_length_arg), data(data_arg) { - name= my_strndup((byte*) name_arg, name_length, MYF(MY_WME)); + name= my_strndup(name_arg, name_length, MYF(MY_WME)); links->push_back(this); } inline bool cmp(const char *name_cmp, uint length) From b6384f46f113b7cbbf61a44ecb09c6c3fa026980 Mon Sep 17 00:00:00 2001 From: "mats@romeo.(none)" <> Date: Fri, 7 Jul 2006 22:38:57 +0200 Subject: [PATCH 20/32] Fixing the prototype for my_strndup() to compile on Windows. --- mysys/my_malloc.c | 2 +- server-tools/instance-manager/parse.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysys/my_malloc.c b/mysys/my_malloc.c index 3fb3866f79c..9dd5530bd28 100644 --- a/mysys/my_malloc.c +++ b/mysys/my_malloc.c @@ -83,7 +83,7 @@ char *my_strdup(const char *from, myf my_flags) } -char *my_strndup(const byte *from, uint length, myf my_flags) +char *my_strndup(const char *from, uint length, myf my_flags) { gptr ptr; if ((ptr=my_malloc(length+1,my_flags)) != 0) diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h index ae29c7eb64a..fd970f54d29 100644 --- a/server-tools/instance-manager/parse.h +++ b/server-tools/instance-manager/parse.h @@ -69,7 +69,7 @@ private: inline char *Named_value::alloc_str(const LEX_STRING *str) { - return my_strndup((const byte *) str->str, str->length, MYF(0)); + return my_strndup(str->str, str->length, MYF(0)); } inline char *Named_value::alloc_str(const char *str) From 5488c269be0fb3ca0b6159d736eee7e18c6508c3 Mon Sep 17 00:00:00 2001 From: "mats@romeo.(none)" <> Date: Sat, 8 Jul 2006 06:15:24 +0200 Subject: [PATCH 21/32] BUG#20821 (INSERT DELAYED failes to write some rows to binlog): Fixing typo and potential memory problem. Reducing number of concurrent mysqlslap threads since tests fail in pushbuild due to too many threads. --- mysql-test/r/rpl_insert.result | 4 ++-- mysql-test/t/rpl_insert.test | 2 +- sql/sql_insert.cc | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/rpl_insert.result b/mysql-test/r/rpl_insert.result index b0c44be017f..bcc9b176ed3 100644 --- a/mysql-test/r/rpl_insert.result +++ b/mysql-test/r/rpl_insert.result @@ -9,8 +9,8 @@ USE mysqlslap; CREATE TABLE t1 (id INT, name VARCHAR(64)); SELECT COUNT(*) FROM mysqlslap.t1; COUNT(*) -20000 +5000 SELECT COUNT(*) FROM mysqlslap.t1; COUNT(*) -20000 +5000 DROP SCHEMA IF EXISTS mysqlslap; diff --git a/mysql-test/t/rpl_insert.test b/mysql-test/t/rpl_insert.test index 1b67736dd72..9225606c94b 100644 --- a/mysql-test/t/rpl_insert.test +++ b/mysql-test/t/rpl_insert.test @@ -15,7 +15,7 @@ USE mysqlslap; CREATE TABLE t1 (id INT, name VARCHAR(64)); let $query = "INSERT DELAYED INTO t1 VALUES (1, 'Dr. No'), (2, 'From Russia With Love'), (3, 'Goldfinger'), (4, 'Thunderball'), (5, 'You Only Live Twice')"; ---exec $MYSQL_SLAP --silent --concurrency=20 --iterations=200 --query=$query --delimiter=";" +--exec $MYSQL_SLAP --silent --concurrency=5 --iterations=200 --query=$query --delimiter=";" --sleep 10 diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 2ec2555d6fc..ceeb4f6fdf8 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1638,8 +1638,12 @@ write_delayed(THD *thd,TABLE *table, enum_duplicates duplic, */ if (query.str) - if (!(query.str= my_strndup(query.str, MYF(MY_WME), query.length))) + { + char *str; + if (!(str= my_strndup(query.str, query.length, MYF(MY_WME)))) goto err; + query.str= str; + } row= new delayed_row(query, duplic, ignore, log_on); if (row == NULL) { From 774de06d2bb9f4c95178aff7d074a671763e87c3 Mon Sep 17 00:00:00 2001 From: "aelkin/elkin@dsl-hkigw8-feb1fb00-100.dhcp.inet.fi" <> Date: Sun, 9 Jul 2006 00:29:33 +0300 Subject: [PATCH 22/32] BUG#19188 incorrect temporary table name of DROP query in replication manual merge of 5.0 patch and fixing an issue with closing temp tables when no binlog or RBR. Note, that despite temporary_tables is indeed double-linked list in 5.1 (patch for bug #19881) it is still enough to use only 'next' reference, as it was done for 5.0, when the list is sorted and destoyed after. --- mysql-test/r/rpl_temporary.result | 14 +- mysql-test/t/rpl_temporary.test | 21 +- sql/sql_base.cc | 318 ++++++++++++++++++++---------- 3 files changed, 240 insertions(+), 113 deletions(-) diff --git a/mysql-test/r/rpl_temporary.result b/mysql-test/r/rpl_temporary.result index 8524fe8aa3d..7e7d0cebe1d 100644 --- a/mysql-test/r/rpl_temporary.result +++ b/mysql-test/r/rpl_temporary.result @@ -76,16 +76,11 @@ drop table t1,t2; create temporary table t3 (f int); create temporary table t4 (f int); create table t5 (f int); -drop table if exists t999; -create temporary table t999 (f int); -LOAD DATA INFILE "./tmp/bl_dump_thread_id" into table t999; -drop table t999; -insert into t4 values (1); -kill `select id from information_schema.processlist where command='Binlog Dump'`; +select id from information_schema.processlist where command='Binlog Dump' into @id; +kill @id; insert into t5 select * from t4; select * from t5 /* must be 1 after reconnection */; f -1 drop temporary table t4; drop table t5; set @@session.pseudo_thread_id=100; @@ -105,4 +100,7 @@ select * from t1 /* must be 1 */; f 1 drop table t1; -End of 5.1 tests +select * from t1; +a +1 +drop table t1; diff --git a/mysql-test/t/rpl_temporary.test b/mysql-test/t/rpl_temporary.test index 800491d13e3..fc336db1a3a 100644 --- a/mysql-test/t/rpl_temporary.test +++ b/mysql-test/t/rpl_temporary.test @@ -142,11 +142,8 @@ create temporary table t4 (f int); create table t5 (f int); sync_with_master; # find dumper's $id -source include/get_binlog_dump_thread_id.inc; -insert into t4 values (1); -# a hint how to do that in 5.1 ---replace_result $id "`select id from information_schema.processlist where command='Binlog Dump'`" -eval kill $id; # to stimulate reconnection by slave w/o timeout +select id from information_schema.processlist where command='Binlog Dump' into @id; +kill @id; # to stimulate reconnection by slave w/o timeout insert into t5 select * from t4; save_master_pos; @@ -190,5 +187,17 @@ select * from t1 /* must be 1 */; connection master; drop table t1; ---echo End of 5.1 tests +# +#14157: utf8 encoding in binlog without set character_set_client +# +--exec $MYSQL --character-sets-dir=../sql/share/charsets/ --default-character-set=latin1 test -e 'create table t1 (a int); set names latin1; create temporary table `äöüÄÖÜ` (a int); insert into `äöüÄÖÜ` values (1); insert into t1 select * from `äöüÄÖÜ`' + +sync_slave_with_master; +#connection slave; +select * from t1; + +connection master; +drop table t1; + +# End of 5.1 tests diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 32d84043d5b..9b016ac5ecb 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -25,7 +25,7 @@ #include #include #include -#ifdef __WIN__ +#ifdef __WIN__ #include #endif @@ -879,7 +879,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, bool found=1; /* Wait until all threads has closed all the tables we had locked */ DBUG_PRINT("info", - ("Waiting for others threads to close their open tables")); + ("Waiting for other threads to close their open tables")); while (found && ! thd->killed) { found=0; @@ -890,6 +890,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, ((table->s->version) < refresh_version && table->db_stat)) { found=1; + DBUG_PRINT("signal", ("Waiting for COND_refresh")); pthread_cond_wait(&COND_refresh,&LOCK_open); break; } @@ -947,8 +948,13 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) { for (; table ; table= table->next) + { if (table->query_id == thd->query_id) + { table->query_id= 0; + table->file->ha_reset(); + } + } } @@ -1028,21 +1034,13 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) */ ha_commit_stmt(thd); + /* Ensure we are calling ha_reset() for all used tables */ + mark_used_tables_as_free_for_reuse(thd, thd->open_tables); + /* We are under simple LOCK TABLES so should not do anything else. */ - if (!prelocked_mode) + if (!prelocked_mode || !thd->lex->requires_prelocking()) DBUG_VOID_RETURN; - if (!thd->lex->requires_prelocking()) - { - /* - If we are executing one of substatements we have to mark - all tables which it used as free for reuse. - */ - mark_used_tables_as_free_for_reuse(thd, thd->open_tables); - DBUG_VOID_RETURN; - } - - DBUG_ASSERT(prelocked_mode); /* We are in prelocked mode, so we have to leave it now with doing implicit UNLOCK TABLES if need. @@ -1096,7 +1094,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) found_old_table= 0; while (thd->open_tables) - found_old_table|=close_thread_table(thd, &thd->open_tables); + found_old_table|= close_thread_table(thd, &thd->open_tables); thd->some_tables_deleted=0; /* Free tables to hold down open files */ @@ -1125,6 +1123,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) DBUG_VOID_RETURN; } + /* move one table to free list */ bool close_thread_table(THD *thd, TABLE **table_ptr) @@ -1149,11 +1148,8 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) table->s->flush_version= flush_version; table->file->extra(HA_EXTRA_FLUSH); } - else - { - // Free memory and reset for next loop - table->file->ha_reset(); - } + // Free memory and reset for next loop + table->file->ha_reset(); table->in_use=0; if (unused_tables) { @@ -1189,8 +1185,10 @@ void close_temporary_tables(THD *thd) if (!mysql_bin_log.is_open() || thd->current_stmt_binlog_row_based) { - for (table= thd->temporary_tables; table; table= table->next) + TABLE *next; + for (table= thd->temporary_tables; table; table= next) { + next=table->next; close_temporary(table, 1, 1); } thd->temporary_tables= 0; @@ -1198,7 +1196,9 @@ void close_temporary_tables(THD *thd) } TABLE *next, - *prev_table /* prev link is not maintained in TABLE's double-linked list */; + *prev_table /* TODO: 5.1 maintaines prev link in temporary_tables + double-linked list so we could fix it. But it is not necessary + at this time when the list is being destroyed */; bool was_quote_show= true; /* to assume thd->options has OPTION_QUOTE_SHOW_CREATE */ // Better add "if exists", in case a RESET MASTER has been done const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "; @@ -1310,7 +1310,6 @@ void close_temporary_tables(THD *thd) thd->temporary_tables=0; } - /* Find table in list. @@ -1526,17 +1525,37 @@ bool close_temporary_table(THD *thd, TABLE_LIST *table_list) } /* - Close temporary table and unlink from thd->temporary tables + unlink from thd->temporary tables and close temporary table */ void close_temporary_table(THD *thd, TABLE *table, bool free_share, bool delete_table) { - TABLE **prev= table->open_prev; - if ((*table->open_prev= table->next)) - table->next->open_prev= prev; + if (table->prev) + { + table->prev->next= table->next; + if (table->prev->next) + table->next->prev= table->prev; + } + else + { + /* removing the item from the list */ + DBUG_ASSERT(table == thd->temporary_tables); + /* + slave must reset its temporary list pointer to zero to exclude + passing non-zero value to end_slave via rli->save_temporary_tables + when no temp tables opened, see an invariant below. + */ + thd->temporary_tables= table->next; + if (thd->temporary_tables) + table->next->prev= 0; + } if (thd->slave_thread) + { + /* natural invariant of temporary_tables */ + DBUG_ASSERT(slave_open_temp_tables || !thd->temporary_tables); slave_open_temp_tables--; + } close_temporary(table, free_share, delete_table); } @@ -1678,6 +1697,7 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond) thd->mysys_var->current_cond= cond; proc_info=thd->proc_info; thd->proc_info="Waiting for table"; + DBUG_ENTER("wait_for_condition"); if (!thd->killed) (void) pthread_cond_wait(cond, mutex); @@ -1698,6 +1718,7 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond) thd->mysys_var->current_cond= 0; thd->proc_info= proc_info; pthread_mutex_unlock(&thd->mysys_var->mutex); + DBUG_VOID_RETURN; } @@ -1786,6 +1807,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) MYSQL_LOCK_IGNORE_FLUSH - Open table even if someone has done a flush or namelock on it. No version number checking is done. + MYSQL_OPEN_IGNORE_LOCKED_TABLES - Open table + ignoring set of locked tables and prelocked mode. IMPLEMENTATION Uses a cache of open tables to find a table not in use. @@ -1845,7 +1868,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } } - if (thd->locked_tables || thd->prelocked_mode) + if (!(flags & MYSQL_OPEN_IGNORE_LOCKED_TABLES) && + (thd->locked_tables || thd->prelocked_mode)) { // Using table locks TABLE *best_table= 0; int best_distance= INT_MIN; @@ -1973,6 +1997,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { if (table->s->version != refresh_version) { + DBUG_PRINT("note", + ("Found table '%s.%s' with different refresh version", + table_list->db, table_list->table_name)); + /* Don't close tables if we are working with a log table or were asked not to close the table explicitly @@ -2080,6 +2108,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (table->timestamp_field) table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); table_list->updatable= 1; // It is not derived table nor non-updatable VIEW + table->clear_column_bitmaps(); DBUG_ASSERT(table->key_read == 0); DBUG_RETURN(table); } @@ -2177,6 +2206,7 @@ static bool reopen_table(TABLE *table) VOID(closefrm(table, 1)); // close file, free everything *table= tmp; + table->default_column_bitmaps(); table->file->change_table_ptr(table, table->s); DBUG_ASSERT(table->alias != 0); @@ -2675,7 +2705,7 @@ retry: goto err; // Code below is for repairing a crashed file - if ((error= lock_table_name(thd, table_list))) + if ((error= lock_table_name(thd, table_list, TRUE))) { if (error < 0) goto err; @@ -3493,10 +3523,12 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, if (link_in_list) { - tmp_table->open_prev= &thd->temporary_tables; - if ((tmp_table->next= thd->temporary_tables)) - thd->temporary_tables->open_prev= &tmp_table->next; + /* growing temp list at the head */ + tmp_table->next= thd->temporary_tables; + if (tmp_table->next) + tmp_table->next->prev= tmp_table; thd->temporary_tables= tmp_table; + thd->temporary_tables->prev= 0; if (thd->slave_thread) slave_open_temp_tables++; } @@ -3544,22 +3576,50 @@ Field *view_ref_found= (Field*) 0x2; static void update_field_dependencies(THD *thd, Field *field, TABLE *table) { - if (thd->set_query_id) + DBUG_ENTER("update_field_dependencies"); + if (thd->mark_used_columns != MARK_COLUMNS_NONE) { - table->file->ha_set_bit_in_rw_set(field->fieldnr, - (bool)(thd->set_query_id-1)); - if (field->query_id != thd->query_id) + MY_BITMAP *current_bitmap, *other_bitmap; + + /* + We always want to register the used keys, as the column bitmap may have + been set for all fields (for example for view). + */ + + table->used_keys.intersect(field->part_of_key); + table->merge_keys.merge(field->part_of_key); + + if (thd->mark_used_columns == MARK_COLUMNS_READ) { - if (table->get_fields_in_item_tree) - field->flags|= GET_FIXED_FIELDS_FLAG; - field->query_id= thd->query_id; - table->used_fields++; - table->used_keys.intersect(field->part_of_key); + current_bitmap= table->read_set; + other_bitmap= table->write_set; } else - thd->dupp_field= field; - } else if (table->get_fields_in_item_tree) + { + current_bitmap= table->write_set; + other_bitmap= table->read_set; + } + + if (bitmap_fast_test_and_set(current_bitmap, field->field_index)) + { + if (thd->mark_used_columns == MARK_COLUMNS_WRITE) + { + DBUG_PRINT("warning", ("Found duplicated field")); + thd->dup_field= field; + } + else + { + DBUG_PRINT("note", ("Field found before")); + } + DBUG_VOID_RETURN; + } + if (table->get_fields_in_item_tree) + field->flags|= GET_FIXED_FIELDS_FLAG; + table->used_fields++; + } + else if (table->get_fields_in_item_tree) field->flags|= GET_FIXED_FIELDS_FLAG; + DBUG_VOID_RETURN; } @@ -3968,12 +4028,12 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, fld= WRONG_GRANT; else #endif - if (thd->set_query_id) + if (thd->mark_used_columns != MARK_COLUMNS_NONE) { /* - * get rw_set correct for this field so that the handler - * knows that this field is involved in the query and gets - * retrieved/updated + Get rw_set correct for this field so that the handler + knows that this field is involved in the query and gets + retrieved/updated */ Field *field_to_set= NULL; if (fld == view_ref_found) @@ -3981,13 +4041,22 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, Item *it= (*ref)->real_item(); if (it->type() == Item::FIELD_ITEM) field_to_set= ((Item_field*)it)->field; + else + { + if (thd->mark_used_columns == MARK_COLUMNS_READ) + it->walk(&Item::register_field_in_read_map, 1, (byte *) 0); + } } else field_to_set= fld; if (field_to_set) - field_to_set->table->file-> - ha_set_bit_in_rw_set(field_to_set->fieldnr, - (bool)(thd->set_query_id-1)); + { + TABLE *table= field_to_set->table; + if (thd->mark_used_columns == MARK_COLUMNS_READ) + bitmap_set_bit(table->read_set, field_to_set->field_index); + else + bitmap_set_bit(table->write_set, field_to_set->field_index); + } } } DBUG_RETURN(fld); @@ -4680,17 +4749,17 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, { TABLE *table_1= nj_col_1->table_ref->table; /* Mark field_1 used for table cache. */ - field_1->query_id= thd->query_id; - table_1->file->ha_set_bit_in_read_set(field_1->fieldnr); + bitmap_set_bit(table_1->read_set, field_1->field_index); table_1->used_keys.intersect(field_1->part_of_key); + table_1->merge_keys.merge(field_1->part_of_key); } if (field_2) { TABLE *table_2= nj_col_2->table_ref->table; /* Mark field_2 used for table cache. */ - field_2->query_id= thd->query_id; - table_2->file->ha_set_bit_in_read_set(field_2->fieldnr); + bitmap_set_bit(table_2->read_set, field_2->field_index); table_2->used_keys.intersect(field_2->part_of_key); + table_2->merge_keys.merge(field_2->part_of_key); } if (using_fields != NULL) @@ -5036,10 +5105,6 @@ static bool setup_natural_join_row_types(THD *thd, if (from_clause->elements == 0) return FALSE; /* We come here in the case of UNIONs. */ - /* For stored procedures do not redo work if already done. */ - if (!context->select_lex->first_execution) - return FALSE; - List_iterator_fast table_ref_it(*from_clause); TABLE_LIST *table_ref; /* Current table reference. */ /* Table reference to the left of the current. */ @@ -5052,14 +5117,18 @@ static bool setup_natural_join_row_types(THD *thd, { table_ref= left_neighbor; left_neighbor= table_ref_it++; - if (store_top_level_join_columns(thd, table_ref, - left_neighbor, right_neighbor)) - return TRUE; - if (left_neighbor) + /* For stored procedures do not redo work if already done. */ + if (context->select_lex->first_execution) { - TABLE_LIST *first_leaf_on_the_right; - first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); - left_neighbor->next_name_resolution_table= first_leaf_on_the_right; + if (store_top_level_join_columns(thd, table_ref, + left_neighbor, right_neighbor)) + return TRUE; + if (left_neighbor) + { + TABLE_LIST *first_leaf_on_the_right; + first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); + left_neighbor->next_name_resolution_table= first_leaf_on_the_right; + } } right_neighbor= table_ref; } @@ -5158,17 +5227,17 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, ****************************************************************************/ bool setup_fields(THD *thd, Item **ref_pointer_array, - List &fields, ulong set_query_id, + List &fields, enum_mark_columns mark_used_columns, List *sum_func_list, bool allow_sum_func) { reg2 Item *item; - ulong save_set_query_id= thd->set_query_id; + enum_mark_columns save_mark_used_columns= thd->mark_used_columns; nesting_map save_allow_sum_func= thd->lex->allow_sum_func; List_iterator it(fields); DBUG_ENTER("setup_fields"); - thd->set_query_id=set_query_id; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= mark_used_columns; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); if (allow_sum_func) thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level; thd->where= THD::DEFAULT_WHERE; @@ -5194,8 +5263,8 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, (item= *(it.ref()))->check_cols(1)) { thd->lex->allow_sum_func= save_allow_sum_func; - thd->set_query_id= save_set_query_id; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= save_mark_used_columns; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); DBUG_RETURN(TRUE); /* purecov: inspected */ } if (ref) @@ -5206,8 +5275,8 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, thd->used_tables|= item->used_tables(); } thd->lex->allow_sum_func= save_allow_sum_func; - thd->set_query_id= save_set_query_id; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= save_mark_used_columns; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); DBUG_RETURN(test(thd->net.report_error)); } @@ -5251,7 +5320,6 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) context name resolution contest to setup table list there from_clause Top-level list of table references in the FROM clause tables Table list (select_lex->table_list) - conds Condition of current SELECT (can be changed by VIEW) leaves List of join table leaves list (select_lex->leaf_tables) refresh It is onle refresh for subquery select_insert It is SELECT ... INSERT command @@ -5273,7 +5341,7 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) bool setup_tables(THD *thd, Name_resolution_context *context, List *from_clause, TABLE_LIST *tables, - Item **conds, TABLE_LIST **leaves, bool select_insert) + TABLE_LIST **leaves, bool select_insert) { uint tablenr= 0; DBUG_ENTER("setup_tables"); @@ -5296,6 +5364,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, table_list= table_list->next_leaf, tablenr++) { TABLE *table= table_list->table; + table->pos_in_table_list= table_list; if (first_select_table && table_list->top_table() == first_select_table) { @@ -5305,6 +5374,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } setup_table_map(table, table_list, tablenr); table->used_keys= table->s->keys_for_keyread; + table->merge_keys.clear_all(); if (table_list->use_index) { key_map map; @@ -5358,6 +5428,58 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } +/* + prepare tables and check access for the view tables + + SYNOPSIS + setup_tables_and_check_view_access() + thd Thread handler + context name resolution contest to setup table list there + from_clause Top-level list of table references in the FROM clause + tables Table list (select_lex->table_list) + conds Condition of current SELECT (can be changed by VIEW) + leaves List of join table leaves list (select_lex->leaf_tables) + refresh It is onle refresh for subquery + select_insert It is SELECT ... INSERT command + want_access what access is needed + + NOTE + a wrapper for check_tables that will also check the resulting + table leaves list for access to all the tables that belong to a view + + RETURN + FALSE ok; In this case *map will include the chosen index + TRUE error +*/ +bool setup_tables_and_check_access(THD *thd, + Name_resolution_context *context, + List *from_clause, + TABLE_LIST *tables, + TABLE_LIST **leaves, + bool select_insert, + ulong want_access) +{ + TABLE_LIST *leaves_tmp= NULL; + + if (setup_tables(thd, context, from_clause, tables, + &leaves_tmp, select_insert)) + return TRUE; + + *leaves= leaves_tmp; + + for (; leaves_tmp; leaves_tmp= leaves_tmp->next_leaf) + { + if (leaves_tmp->belong_to_view && + check_single_table_access(thd, want_access, leaves_tmp)) + { + tables->hide_view_error(thd); + return TRUE; + } + } + return FALSE; +} + + /* Create a key_map from a list of index names @@ -5387,8 +5509,8 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, name->length(), 1)) <= 0) { - my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), name->c_ptr(), - table->s->table_name.str); + my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), name->c_ptr(), + table->pos_in_table_list->alias); map->set_all(); return 1; } @@ -5478,7 +5600,6 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, } #endif - /* Update the tables used in the query based on the referenced fields. For views and natural joins this update is performed inside the loop below. @@ -5544,18 +5665,13 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if ((field= field_iterator.field())) { - /* - Mark if field used before in this select. - Used by 'insert' to verify if a field name is used twice. - */ - if (field->query_id == thd->query_id) - thd->dupp_field= field; - field->query_id= thd->query_id; - field->table->file->ha_set_bit_in_read_set(field->fieldnr); - + /* Mark fields as used to allow storage engine to optimze access */ + bitmap_set_bit(field->table->read_set, field->field_index); if (table) + { table->used_keys.intersect(field->part_of_key); - + table->merge_keys.merge(field->part_of_key); + } if (tables->is_natural_join) { TABLE *field_table; @@ -5572,16 +5688,13 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, { thd->used_tables|= field_table->map; field_table->used_keys.intersect(field->part_of_key); + field_table->merge_keys.merge(field->part_of_key); field_table->used_fields++; } } } else - { thd->used_tables|= item->used_tables(); - item->walk(&Item::reset_query_id_processor, - (byte *)(&thd->query_id)); - } } /* In case of stored tables, all fields are considered as used, @@ -5590,10 +5703,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, For NATURAL joins, used_tables is updated in the IF above. */ if (table) - { table->used_fields= table->s->fields; - table->file->ha_set_all_bits_in_read_set(); - } } if (found) DBUG_RETURN(FALSE); @@ -5652,8 +5762,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, arena->is_conventional()) arena= 0; // For easier test - thd->set_query_id=1; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= MARK_COLUMNS_READ; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); select_lex->cond_count= 0; for (table= tables; table; table= table->next_local) @@ -5908,7 +6018,7 @@ static void mysql_rm_tmp_tables(void) if (!bcmp(file->name,tmp_file_prefix,tmp_file_prefix_length)) { - sprintf(filePath,"%s%s",tmpdir,file->name); + sprintf(filePath,"%s%c%s",tmpdir,FN_LIBCHAR,file->name); VOID(my_delete(filePath,MYF(MY_WME))); } } @@ -5994,6 +6104,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, TABLE_SHARE *share; bool result= 0, signalled= 0; DBUG_ENTER("remove_table_from_cache"); + DBUG_PRINT("enter", ("Table: '%s.%s' flags: %u", db, table_name, flags)); key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; for (;;) @@ -6020,7 +6131,10 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, DBUG_PRINT("info", ("Table was in use by other thread")); in_use->some_tables_deleted=1; if (table->db_stat) + { + DBUG_PRINT("info", ("Found another active instance of the table")); result=1; + } /* Kill delayed insert threads */ if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && ! in_use->killed) @@ -6075,6 +6189,12 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG)) { + /* + Signal any thread waiting for tables to be freed to + reopen their tables + */ + (void) pthread_cond_broadcast(&COND_refresh); + DBUG_PRINT("info", ("Waiting for refresh signal")); if (!(flags & RTFC_CHECK_KILLED_FLAG) || !thd->killed) { dropping_tables++; @@ -6154,7 +6274,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) alias alias for table db database table_name name of table - db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..) + db_stat open flags (for example ->OPEN_KEYFILE|HA_OPEN_RNDFILE..) can be 0 (example in ha_example_table) prgflag READ_ALL etc.. ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc.. From fdb0f85a0ce608023a9ec7c2aedf85af6ffeb64a Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Sun, 9 Jul 2006 17:00:47 +0200 Subject: [PATCH 23/32] * Mixed replication mode * : 1) Fix for BUG#19630 "stored function inserting into two auto_increment breaks statement-based binlog": a stored function inserting into two such tables may fail to replicate (inserting wrong data in the slave's copy of the second table) if the slave's second table had an internal auto_increment counter different from master's. Because the auto_increment value autogenerated by master for the 2nd table does not go into binlog, only the first does, so the slave lacks information. To fix this, if running in mixed binlogging mode, if the stored function or trigger plans to update two different tables both having auto_increment columns, we switch to row-based for the whole function. We don't have a simple solution for statement-based binlogging mode, there the bug remains and will be documented as a known problem. Re-enabling rpl_switch_stm_row_mixed. 2) Fix for BUG#20630 "Mixed binlogging mode does not work with stored functions, triggers, views", which was a documented limitation (in mixed mode, we didn't detect that a stored function's execution needed row-based binlogging (due to some UUID() call for example); same for triggers, same for views (a view created from a SELECT UUID(), and doing INSERT INTO sometable SELECT theview; would not replicate row-based). This is implemented by, after parsing a routine's body, remembering in sp_head that this routine needs row-based binlogging. Then when this routine is used, the caller is marked to require row-based binlogging too. Same for views: when we parse a view and detect that its SELECT needs row-based binary logging, we mark the calling LEX as such. 3) Fix for BUG#20499 "mixed mode with temporary table breaks binlog": a temporary table containing e.g. UUID has its changes not binlogged, so any query updating a permanent table with data from the temporary table will run wrongly on slave. Solution: in mixed mode we don't switch back from row-based to statement-based when there exists temporary tables. 4) Attempt to test mysqlbinlog on a binlog generated by mysqlbinlog; impossible due to BUG#11312 and BUG#20329, but test is in place for when they are fixed. --- mysql-test/r/rpl_switch_stm_row_mixed.result | 518 ++++++++++++++++--- mysql-test/t/disabled.def | 1 - mysql-test/t/rpl_switch_stm_row_mixed.test | 379 ++++++++++++-- sql/item_create.cc | 4 +- sql/set_var.cc | 4 +- sql/sp.cc | 13 +- sql/sp_head.cc | 10 + sql/sp_head.h | 22 +- sql/sql_base.cc | 70 +++ sql/sql_class.h | 34 +- sql/sql_lex.cc | 4 +- sql/sql_lex.h | 16 +- sql/sql_parse.cc | 13 +- sql/sql_view.cc | 9 + sql/sql_yacc.yy | 2 +- 15 files changed, 947 insertions(+), 152 deletions(-) diff --git a/mysql-test/r/rpl_switch_stm_row_mixed.result b/mysql-test/r/rpl_switch_stm_row_mixed.result index 313037bb9dc..5d320913bfb 100644 --- a/mysql-test/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/r/rpl_switch_stm_row_mixed.result @@ -18,18 +18,18 @@ select @@global.binlog_format, @@session.binlog_format; ROW ROW CREATE TABLE t1 (a varchar(100)); prepare stmt1 from 'insert into t1 select concat(UUID(),?)'; -set @string="emergency"; -insert into t1 values("work"); +set @string="emergency_1_"; +insert into t1 values("work_2_"); execute stmt1 using @string; deallocate prepare stmt1; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values(concat(UUID(),"work")); +insert into t1 values(concat(UUID(),"work_3_")); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values(concat("for",UUID())); -insert into t1 select "yesterday"; -create temporary table tmp(a char(3)); -insert into tmp values("see"); +insert into t1 values(concat("for_4_",UUID())); +insert into t1 select "yesterday_5_"; +create temporary table tmp(a char(100)); +insert into tmp values("see_6_"); set binlog_format=statement; ERROR HY000: Cannot switch out of the row-based binary log format when the session has open temporary tables insert into t1 select * from tmp; @@ -55,16 +55,16 @@ select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format STATEMENT STATEMENT prepare stmt1 from 'insert into t1 select ?'; -set @string="emergency"; -insert into t1 values("work"); +set @string="emergency_7_"; +insert into t1 values("work_8_"); execute stmt1 using @string; deallocate prepare stmt1; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values("work"); +insert into t1 values("work_9_"); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values("for"); -insert into t1 select "yesterday"; +insert into t1 values("for_10_"); +insert into t1 select "yesterday_11_"; set binlog_format=default; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format @@ -75,16 +75,16 @@ select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format STATEMENT STATEMENT prepare stmt1 from 'insert into t1 select ?'; -set @string="emergency"; -insert into t1 values("work"); +set @string="emergency_12_"; +insert into t1 values("work_13_"); execute stmt1 using @string; deallocate prepare stmt1; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values("work"); +insert into t1 values("work_14_"); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values("for"); -insert into t1 select "yesterday"; +insert into t1 values("for_15_"); +insert into t1 select "yesterday_16_"; set binlog_format=mixed; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format @@ -94,40 +94,40 @@ select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format MIXED MIXED prepare stmt1 from 'insert into t1 select concat(UUID(),?)'; -set @string="emergency"; -insert into t1 values("work"); +set @string="emergency_17_"; +insert into t1 values("work_18_"); execute stmt1 using @string; deallocate prepare stmt1; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values(concat(UUID(),"work")); +insert into t1 values(concat(UUID(),"work_19_")); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values(concat("for",UUID())); -insert into t1 select "yesterday"; +insert into t1 values(concat("for_20_",UUID())); +insert into t1 select "yesterday_21_"; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values(concat(UUID(),"work")); +insert into t1 values(concat(UUID(),"work_22_")); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values(concat("for",UUID())); -insert into t1 select "yesterday"; -create table t2 select UUID(); +insert into t1 values(concat("for_23_",UUID())); +insert into t1 select "yesterday_24_"; +create table t2 select rpad(UUID(),100,' '); create table t3 select 1 union select UUID(); create table t4 select * from t1 where 3 in (select 1 union select 2 union select UUID() union select 3); create table t5 select * from t1 where 3 in (select 1 union select 2 union select curdate() union select 3); insert into t5 select UUID() from t1 where 3 in (select 1 union select 2 union select 3 union select * from t4); create procedure foo() begin -insert into t1 values("work"); -insert into t1 values(concat("for",UUID())); -insert into t1 select "yesterday"; +insert into t1 values("work_25_"); +insert into t1 values(concat("for_26_",UUID())); +insert into t1 select "yesterday_27_"; end| create procedure foo2() begin -insert into t1 values(concat("emergency",UUID())); -insert into t1 values("work"); -insert into t1 values(concat("for",UUID())); +insert into t1 values(concat("emergency_28_",UUID())); +insert into t1 values("work_29_"); +insert into t1 values(concat("for_30_",UUID())); set session binlog_format=row; # accepted for stored procs -insert into t1 values("more work"); +insert into t1 values("more work_31_"); set session binlog_format=mixed; end| create function foo3() returns bigint unsigned @@ -136,32 +136,221 @@ set session binlog_format=row; # rejected for stored funcs insert into t1 values("alarm"); return 100; end| +create procedure foo4(x varchar(100)) +begin +insert into t1 values(concat("work_250_",x)); +insert into t1 select "yesterday_270_"; +end| call foo(); call foo2(); +call foo4("hello"); +call foo4(UUID()); +call foo4("world"); select foo3(); ERROR HY000: Cannot change the binary logging format inside a stored function or trigger select * from t1 where a="alarm"; a +drop function foo3; +create function foo3() returns bigint unsigned +begin +insert into t1 values("foo3_32_"); +call foo(); +return 100; +end| +insert into t2 select foo3(); +prepare stmt1 from 'insert into t2 select foo3()'; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; +create function foo4() returns bigint unsigned +begin +insert into t2 select foo3(); +return 100; +end| +select foo4(); +foo4() +100 +prepare stmt1 from 'select foo4()'; +execute stmt1; +foo4() +100 +execute stmt1; +foo4() +100 +deallocate prepare stmt1; +create function foo5() returns bigint unsigned +begin +insert into t2 select UUID(); +return 100; +end| +select foo5(); +foo5() +100 +prepare stmt1 from 'select foo5()'; +execute stmt1; +foo5() +100 +execute stmt1; +foo5() +100 +deallocate prepare stmt1; +create function foo6(x varchar(100)) returns bigint unsigned +begin +insert into t2 select x; +return 100; +end| +select foo6("foo6_1_"); +foo6("foo6_1_") +100 +select foo6(concat("foo6_2_",UUID())); +foo6(concat("foo6_2_",UUID())) +100 +prepare stmt1 from 'select foo6(concat("foo6_3_",UUID()))'; +execute stmt1; +foo6(concat("foo6_3_",UUID())) +100 +execute stmt1; +foo6(concat("foo6_3_",UUID())) +100 +deallocate prepare stmt1; +create view v1 as select uuid(); +create table t11 (data varchar(255)); +insert into t11 select * from v1; +insert into t11 select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='mysqltest1' and TABLE_NAME IN ('v1','t11'); +prepare stmt1 from "insert into t11 select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='mysqltest1' and TABLE_NAME IN ('v1','t11')"; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; +create trigger t11_bi before insert on t11 for each row +begin +set NEW.data = concat(NEW.data,UUID()); +end| +insert into t11 values("try_560_"); +create table t20 select * from t1; +create table t21 select * from t2; +create table t22 select * from t3; +drop table t1,t2,t3; +create table t1 (a int primary key auto_increment, b varchar(100)); +create table t2 (a int primary key auto_increment, b varchar(100)); +create table t3 (b varchar(100)); +create function f (x varchar(100)) returns int deterministic +begin +insert into t1 values(null,x); +insert into t2 values(null,x); +return 1; +end| +select f("try_41_"); +f("try_41_") +1 +use mysqltest1; +insert into t2 values(2,null),(3,null),(4,null); +delete from t2 where a>=2; +select f("try_42_"); +f("try_42_") +1 +insert into t2 values(3,null),(4,null); +delete from t2 where a>=3; +prepare stmt1 from 'select f(?)'; +set @string="try_43_"; +insert into t1 values(null,"try_44_"); +execute stmt1 using @string; +f(?) +1 +deallocate prepare stmt1; +create table t12 select * from t1; +drop table t1; +create table t1 (a int, b varchar(100), key(a)); +select f("try_45_"); +f("try_45_") +1 +create table t13 select * from t1; +drop table t1; +create table t1 (a int primary key auto_increment, b varchar(100)); +drop function f; +truncate table t2; +create function f1 (x varchar(100)) returns int deterministic +begin +insert into t1 values(null,x); +return 1; +end| +create function f2 (x varchar(100)) returns int deterministic +begin +insert into t2 values(null,x); +return 1; +end| +select f1("try_46_"),f2("try_47_"); +f1("try_46_") f2("try_47_") +1 1 +insert into t2 values(2,null),(3,null),(4,null); +delete from t2 where a>=2; +select f1("try_48_"),f2("try_49_"); +f1("try_48_") f2("try_49_") +1 1 +insert into t3 values(concat("try_50_",f1("try_51_"),f2("try_52_"))); +drop function f2; +create function f2 (x varchar(100)) returns int deterministic +begin +declare y int; +insert into t1 values(null,x); +set y = (select count(*) from t2); +return y; +end| +select f1("try_53_"),f2("try_54_"); +f1("try_53_") f2("try_54_") +1 3 +drop function f2; +create trigger t1_bi before insert on t1 for each row +begin +insert into t2 values(null,"try_55_"); +end| +insert into t1 values(null,"try_56_"); +alter table t1 modify a int, drop primary key; +insert into t1 values(null,"try_57_"); +CREATE TEMPORARY TABLE t15 SELECT UUID(); +create table t16 like t15; +INSERT INTO t16 SELECT * FROM t15; +insert into t16 values("try_65_"); +drop table t15; +insert into t16 values("try_66_"); select count(*) from t1; count(*) -36 +7 select count(*) from t2; count(*) -1 +5 select count(*) from t3; count(*) -2 +1 select count(*) from t4; count(*) 29 select count(*) from t5; count(*) 58 +select count(*) from t11; +count(*) +8 +select count(*) from t20; +count(*) +66 +select count(*) from t21; +count(*) +14 +select count(*) from t22; +count(*) +2 +select count(*) from t12; +count(*) +4 +select count(*) from t13; +count(*) +1 +select count(*) from t16; +count(*) +3 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # drop database if exists mysqltest1 -master-bin.000001 # Table_map 1 # table_id: # (mysql.proc) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # create database mysqltest1 master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TABLE t1 (a varchar(100)) master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) @@ -178,45 +367,41 @@ master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work_8_") +master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E63795F375F COLLATE latin1_swedish_ci +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work_9_") +master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E63795F375F COLLATE latin1_swedish_ci +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("for_10_") +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_11_" +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work_13_") +master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E63795F31325F COLLATE latin1_swedish_ci +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work_14_") +master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E63795F31325F COLLATE latin1_swedish_ci +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("for_15_") +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_16_" +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work_18_") master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work") -master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E6379 COLLATE latin1_swedish_ci -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work") -master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E6379 COLLATE latin1_swedish_ci -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("for") -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday" -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work") -master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E6379 COLLATE latin1_swedish_ci -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work") -master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E6379 COLLATE latin1_swedish_ci -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("for") -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday" -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work") -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E6379 COLLATE latin1_swedish_ci +master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E63795F31375F COLLATE latin1_swedish_ci master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday" +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_21_" master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E6379 COLLATE latin1_swedish_ci +master-bin.000001 # User var 1 # @`string`=_latin1 0x656D657267656E63795F31375F COLLATE latin1_swedish_ci master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string' master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday" +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_24_" master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TABLE `t2` ( - `UUID()` varchar(36) CHARACTER SET utf8 NOT NULL DEFAULT '' + `rpad(UUID(),100,' ')` varchar(100) CHARACTER SET utf8 NOT NULL DEFAULT '' ) master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F @@ -239,17 +424,17 @@ master-bin.000001 # Write_rows 1 # table_id: # master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo() begin -insert into t1 values("work"); -insert into t1 values(concat("for",UUID())); -insert into t1 select "yesterday"; +insert into t1 values("work_25_"); +insert into t1 values(concat("for_26_",UUID())); +insert into t1 select "yesterday_27_"; end master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo2() begin -insert into t1 values(concat("emergency",UUID())); -insert into t1 values("work"); -insert into t1 values(concat("for",UUID())); +insert into t1 values(concat("emergency_28_",UUID())); +insert into t1 values("work_29_"); +insert into t1 values(concat("for_30_",UUID())); set session binlog_format=row; # accepted for stored procs -insert into t1 values("more work"); +insert into t1 values("more work_31_"); set session binlog_format=mixed; end master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function foo3() returns bigint unsigned @@ -258,15 +443,200 @@ set session binlog_format=row; # rejected for stored funcs insert into t1 values("alarm"); return 100; end -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work") +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo4(x varchar(100)) +begin +insert into t1 values(concat("work_250_",x)); +insert into t1 select "yesterday_270_"; +end +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work_25_") master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday" +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_27_" master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work") +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work_29_") master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',... +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_270_" +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',... +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_270_" +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',... +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_270_" +master-bin.000001 # Query 1 # use `mysqltest1`; drop function foo3 +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function foo3() returns bigint unsigned +begin +insert into t1 values("foo3_32_"); +call foo(); +return 100; +end +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function foo4() returns bigint unsigned +begin +insert into t2 select foo3(); +return 100; +end +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function foo5() returns bigint unsigned +begin +insert into t2 select UUID(); +return 100; +end +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function foo6(x varchar(100)) returns bigint unsigned +begin +insert into t2 select x; +return 100; +end +master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `foo6`(_latin1'foo6_1_') +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select uuid() +master-bin.000001 # Query 1 # use `mysqltest1`; create table t11 (data varchar(255)) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t11) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t11 select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='mysqltest1' and TABLE_NAME IN ('v1','t11') +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t11 select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='mysqltest1' and TABLE_NAME IN ('v1','t11') +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t11 select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='mysqltest1' and TABLE_NAME IN ('v1','t11') +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` trigger t11_bi before insert on t11 for each row +begin +set NEW.data = concat(NEW.data,UUID()); +end +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t11) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; create table t20 select * from t1 +master-bin.000001 # Query 1 # use `mysqltest1`; create table t21 select * from t2 +master-bin.000001 # Query 1 # use `mysqltest1`; create table t22 select * from t3 +master-bin.000001 # Query 1 # use `mysqltest1`; drop table t1,t2,t3 +master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a int primary key auto_increment, b varchar(100)) +master-bin.000001 # Query 1 # use `mysqltest1`; create table t2 (a int primary key auto_increment, b varchar(100)) +master-bin.000001 # Query 1 # use `mysqltest1`; create table t3 (b varchar(100)) +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function f (x varchar(100)) returns int deterministic +begin +insert into t1 values(null,x); +insert into t2 values(null,x); +return 1; +end +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Intvar 1 # INSERT_ID=3 +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(null,"try_44_") +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; create table t12 select * from t1 +master-bin.000001 # Query 1 # use `mysqltest1`; drop table t1 +master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a int, b varchar(100), key(a)) +master-bin.000001 # Intvar 1 # INSERT_ID=4 +master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `f`(_latin1'try_45_') +master-bin.000001 # Query 1 # use `mysqltest1`; create table t13 select * from t1 +master-bin.000001 # Query 1 # use `mysqltest1`; drop table t1 +master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a int primary key auto_increment, b varchar(100)) +master-bin.000001 # Query 1 # use `mysqltest1`; drop function f +master-bin.000001 # Query 1 # use `mysqltest1`; truncate table t2 +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function f1 (x varchar(100)) returns int deterministic +begin +insert into t1 values(null,x); +return 1; +end +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function f2 (x varchar(100)) returns int deterministic +begin +insert into t2 values(null,x); +return 1; +end +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t3) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; drop function f2 +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function f2 (x varchar(100)) returns int deterministic +begin +declare y int; +insert into t1 values(null,x); +set y = (select count(*) from t2); +return y; +end +master-bin.000001 # Intvar 1 # INSERT_ID=4 +master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `f1`(_latin1'try_53_') +master-bin.000001 # Intvar 1 # INSERT_ID=5 +master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `f2`(_latin1'try_54_') +master-bin.000001 # Query 1 # use `mysqltest1`; drop function f2 +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` trigger t1_bi before insert on t1 for each row +begin +insert into t2 values(null,"try_55_"); +end +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; alter table t1 modify a int, drop primary key +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(null,"try_57_") +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TEMPORARY TABLE `t15` ( + `UUID()` varchar(36) CHARACTER SET utf8 NOT NULL DEFAULT '' +) +master-bin.000001 # Query 1 # use `mysqltest1`; COMMIT +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TABLE `t16` ( + `UUID()` varchar(36) CHARACTER SET utf8 NOT NULL DEFAULT '' +) +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t16) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t16) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t16 values("try_66_") drop database mysqltest1; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 6f26847f8d7..a0b61f21047 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -32,7 +32,6 @@ rpl_ndb_ddl : BUG#18946 result file needs update + test needs to ch rpl_ndb_innodb2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement #rpl_ndb_log : BUG#18947 2006-03-21 tomas CRBR: order in binlog of create table and insert (on different table) not determ rpl_ndb_myisam2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement -rpl_switch_stm_row_mixed : BUG#18590 2006-03-28 brian rpl_row_blob_innodb : BUG#18980 2006-04-10 kent Test fails randomly rpl_row_func003 : BUG#19074 2006-13-04 andrei test failed rpl_sp : BUG#16456 2006-02-16 jmiller diff --git a/mysql-test/t/rpl_switch_stm_row_mixed.test b/mysql-test/t/rpl_switch_stm_row_mixed.test index 4a79b3995c4..ee86a769820 100644 --- a/mysql-test/t/rpl_switch_stm_row_mixed.test +++ b/mysql-test/t/rpl_switch_stm_row_mixed.test @@ -15,22 +15,22 @@ select @@global.binlog_format, @@session.binlog_format; CREATE TABLE t1 (a varchar(100)); prepare stmt1 from 'insert into t1 select concat(UUID(),?)'; -set @string="emergency"; -insert into t1 values("work"); +set @string="emergency_1_"; +insert into t1 values("work_2_"); execute stmt1 using @string; deallocate prepare stmt1; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values(concat(UUID(),"work")); +insert into t1 values(concat(UUID(),"work_3_")); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values(concat("for",UUID())); -insert into t1 select "yesterday"; +insert into t1 values(concat("for_4_",UUID())); +insert into t1 select "yesterday_5_"; # verify that temp tables prevent a switch to SBR -create temporary table tmp(a char(3)); -insert into tmp values("see"); +create temporary table tmp(a char(100)); +insert into tmp values("see_6_"); --error ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR set binlog_format=statement; insert into t1 select * from tmp; @@ -47,18 +47,18 @@ show session variables like "binlog_format%"; select @@global.binlog_format, @@session.binlog_format; prepare stmt1 from 'insert into t1 select ?'; -set @string="emergency"; -insert into t1 values("work"); +set @string="emergency_7_"; +insert into t1 values("work_8_"); execute stmt1 using @string; deallocate prepare stmt1; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values("work"); +insert into t1 values("work_9_"); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values("for"); -insert into t1 select "yesterday"; +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; @@ -69,18 +69,18 @@ set global binlog_format=default; select @@global.binlog_format, @@session.binlog_format; prepare stmt1 from 'insert into t1 select ?'; -set @string="emergency"; -insert into t1 values("work"); +set @string="emergency_12_"; +insert into t1 values("work_13_"); execute stmt1 using @string; deallocate prepare stmt1; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values("work"); +insert into t1 values("work_14_"); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values("for"); -insert into t1 select "yesterday"; +insert into t1 values("for_15_"); +insert into t1 select "yesterday_16_"; # and now the mixed mode @@ -90,53 +90,52 @@ set global binlog_format=mixed; select @@global.binlog_format, @@session.binlog_format; prepare stmt1 from 'insert into t1 select concat(UUID(),?)'; -set @string="emergency"; -insert into t1 values("work"); +set @string="emergency_17_"; +insert into t1 values("work_18_"); execute stmt1 using @string; deallocate prepare stmt1; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values(concat(UUID(),"work")); +insert into t1 values(concat(UUID(),"work_19_")); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values(concat("for",UUID())); -insert into t1 select "yesterday"; +insert into t1 values(concat("for_20_",UUID())); +insert into t1 select "yesterday_21_"; prepare stmt1 from 'insert into t1 select ?'; -insert into t1 values(concat(UUID(),"work")); +insert into t1 values(concat(UUID(),"work_22_")); execute stmt1 using @string; deallocate prepare stmt1; -insert into t1 values(concat("for",UUID())); -insert into t1 select "yesterday"; +insert into t1 values(concat("for_23_",UUID())); +insert into t1 select "yesterday_24_"; # Test of CREATE TABLE SELECT -create table t2 select UUID(); +create table t2 select rpad(UUID(),100,' '); create table t3 select 1 union select UUID(); create table t4 select * from t1 where 3 in (select 1 union select 2 union select UUID() union select 3); create table t5 select * from t1 where 3 in (select 1 union select 2 union select curdate() union select 3); # what if UUID() is first: insert into t5 select UUID() from t1 where 3 in (select 1 union select 2 union select 3 union select * from t4); -# inside a stored procedure (inside a function or trigger won't -# work) +# inside a stored procedure delimiter |; create procedure foo() begin -insert into t1 values("work"); -insert into t1 values(concat("for",UUID())); -insert into t1 select "yesterday"; +insert into t1 values("work_25_"); +insert into t1 values(concat("for_26_",UUID())); +insert into t1 select "yesterday_27_"; end| create procedure foo2() begin -insert into t1 values(concat("emergency",UUID())); -insert into t1 values("work"); -insert into t1 values(concat("for",UUID())); +insert into t1 values(concat("emergency_28_",UUID())); +insert into t1 values("work_29_"); +insert into t1 values(concat("for_30_",UUID())); set session binlog_format=row; # accepted for stored procs -insert into t1 values("more work"); +insert into t1 values("more work_31_"); set session binlog_format=mixed; end| create function foo3() returns bigint unsigned @@ -145,15 +144,116 @@ begin insert into t1 values("alarm"); return 100; end| +create procedure foo4(x varchar(100)) +begin +insert into t1 values(concat("work_250_",x)); +insert into t1 select "yesterday_270_"; +end| delimiter ;| call foo(); call foo2(); +call foo4("hello"); +call foo4(UUID()); +call foo4("world"); # test that can't SET in a stored function --error ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT select foo3(); select * from t1 where a="alarm"; +# Tests of stored functions/triggers/views for BUG#20930 "Mixed +# binlogging mode does not work with stored functions, triggers, +# views" + +# Function which calls procedure +drop function foo3; +delimiter |; +create function foo3() returns bigint unsigned +begin + insert into t1 values("foo3_32_"); + call foo(); + return 100; +end| +delimiter ;| +insert into t2 select foo3(); + +prepare stmt1 from 'insert into t2 select foo3()'; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + +# Test if stored function calls stored function which calls procedure +# which requires row-based. + +delimiter |; +create function foo4() returns bigint unsigned +begin + insert into t2 select foo3(); + return 100; +end| +delimiter ;| +select foo4(); + +prepare stmt1 from 'select foo4()'; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + +# A simple stored function +delimiter |; +create function foo5() returns bigint unsigned +begin + insert into t2 select UUID(); + return 100; +end| +delimiter ;| +select foo5(); + +prepare stmt1 from 'select foo5()'; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + +# A simple stored function where UUID() is in the argument +delimiter |; +create function foo6(x varchar(100)) returns bigint unsigned +begin + insert into t2 select x; + return 100; +end| +delimiter ;| +select foo6("foo6_1_"); +select foo6(concat("foo6_2_",UUID())); + +prepare stmt1 from 'select foo6(concat("foo6_3_",UUID()))'; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + + +# Test of views using UUID() + +create view v1 as select uuid(); +create table t11 (data varchar(255)); +insert into t11 select * from v1; +# Test of querying INFORMATION_SCHEMA which parses the view's body, +# to verify that it binlogs statement-based (is not polluted by +# the parsing of the view's body). +insert into t11 select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='mysqltest1' and TABLE_NAME IN ('v1','t11'); +prepare stmt1 from "insert into t11 select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='mysqltest1' and TABLE_NAME IN ('v1','t11')"; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + +# Test of triggers with UUID() +delimiter |; +create trigger t11_bi before insert on t11 for each row +begin + set NEW.data = concat(NEW.data,UUID()); +end| +delimiter ;| +insert into t11 values("try_560_"); + # If you want to do manual testing of the mixed mode regarding UDFs (not # testable automatically as quite platform- and compiler-dependent), # you just need to set the variable below to 1, and to @@ -164,30 +264,182 @@ if ($you_want_to_test_UDF) { CREATE FUNCTION metaphon RETURNS STRING SONAME 'udf_example.so'; prepare stmt1 from 'insert into t1 select metaphon(?)'; - set @string="emergency"; - insert into t1 values("work"); + set @string="emergency_133_"; + insert into t1 values("work_134_"); execute stmt1 using @string; deallocate prepare stmt1; prepare stmt1 from 'insert into t1 select ?'; - insert into t1 values(metaphon("work")); + insert into t1 values(metaphon("work_135_")); execute stmt1 using @string; deallocate prepare stmt1; - insert into t1 values(metaphon("for")); - insert into t1 select "yesterday"; - create table t6 select metaphon("for"); - create table t7 select 1 union select metaphon("for"); - create table t8 select * from t1 where 3 in (select 1 union select 2 union select metaphon("for") union select 3); + insert into t1 values(metaphon("for_136_")); + insert into t1 select "yesterday_137_"; + create table t6 select metaphon("for_138_"); + create table t7 select 1 union select metaphon("for_139_"); + create table t8 select * from t1 where 3 in (select 1 union select 2 union select metaphon("for_140_") union select 3); create table t9 select * from t1 where 3 in (select 1 union select 2 union select curdate() union select 3); } +create table t20 select * from t1; # save for comparing later +create table t21 select * from t2; +create table t22 select * from t3; +drop table t1,t2,t3; + +# This tests the fix to +# BUG#19630 stored function inserting into two auto_increment breaks statement-based binlog +# We verify that under the mixed binlog mode, a stored function +# modifying at least two tables having an auto_increment column, +# is binlogged row-based. Indeed in statement-based binlogging, +# only the auto_increment value generated for the first table +# is recorded in the binlog, the value generated for the 2nd table +# lacking. + +create table t1 (a int primary key auto_increment, b varchar(100)); +create table t2 (a int primary key auto_increment, b varchar(100)); +create table t3 (b varchar(100)); +delimiter |; +create function f (x varchar(100)) returns int deterministic +begin + insert into t1 values(null,x); + insert into t2 values(null,x); + return 1; +end| +delimiter ;| +select f("try_41_"); +# Two operations which compensate each other except that their net +# effect is that they advance the auto_increment counter of t2 on slave: +sync_slave_with_master; +use mysqltest1; +insert into t2 values(2,null),(3,null),(4,null); +delete from t2 where a>=2; + +connection master; +# this is the call which didn't replicate well +select f("try_42_"); +sync_slave_with_master; + +# now use prepared statement and test again, just to see that the RBB +# mode isn't set at PREPARE but at EXECUTE. + +insert into t2 values(3,null),(4,null); +delete from t2 where a>=3; + +connection master; +prepare stmt1 from 'select f(?)'; +set @string="try_43_"; +insert into t1 values(null,"try_44_"); # should be SBB +execute stmt1 using @string; # should be RBB +deallocate prepare stmt1; +sync_slave_with_master; + +# verify that if only one table has auto_inc, it does not trigger RBB +# (we'll check in binlog further below) + +connection master; +create table t12 select * from t1; # save for comparing later +drop table t1; +create table t1 (a int, b varchar(100), key(a)); +select f("try_45_"); + +# restore table's key +create table t13 select * from t1; +drop table t1; +create table t1 (a int primary key auto_increment, b varchar(100)); + +# now test if it's two functions, each of them inserts in one table + +drop function f; +# Manifestation of BUG#20341! re-enable this line after merging fix +# for that bug +#create table t14 select * from t2; +truncate table t2; +delimiter |; +create function f1 (x varchar(100)) returns int deterministic +begin + insert into t1 values(null,x); + return 1; +end| +create function f2 (x varchar(100)) returns int deterministic +begin + insert into t2 values(null,x); + return 1; +end| +delimiter ;| +select f1("try_46_"),f2("try_47_"); + +sync_slave_with_master; +insert into t2 values(2,null),(3,null),(4,null); +delete from t2 where a>=2; + +connection master; +# Test with SELECT and INSERT +select f1("try_48_"),f2("try_49_"); +insert into t3 values(concat("try_50_",f1("try_51_"),f2("try_52_"))); +sync_slave_with_master; + +# verify that if f2 does only read on an auto_inc table, this does not +# switch to RBB +connection master; +drop function f2; +delimiter |; +create function f2 (x varchar(100)) returns int deterministic +begin + declare y int; + insert into t1 values(null,x); + set y = (select count(*) from t2); + return y; +end| +delimiter ;| +select f1("try_53_"),f2("try_54_"); +sync_slave_with_master; + +# And now, a normal statement with a trigger (no stored functions) + +connection master; +drop function f2; +delimiter |; +create trigger t1_bi before insert on t1 for each row +begin + insert into t2 values(null,"try_55_"); +end| +delimiter ;| +insert into t1 values(null,"try_56_"); +# and now remove one auto_increment and verify SBB +alter table t1 modify a int, drop primary key; +insert into t1 values(null,"try_57_"); +sync_slave_with_master; + +# Test for BUG#20499 "mixed mode with temporary table breaks binlog" +# Slave used to have only 2 rows instead of 3. +connection master; +CREATE TEMPORARY TABLE t15 SELECT UUID(); +create table t16 like t15; +INSERT INTO t16 SELECT * FROM t15; +# we'll verify that this one is done RBB +insert into t16 values("try_65_"); +drop table t15; +# we'll verify that this one is done SBB +insert into t16 values("try_66_"); +sync_slave_with_master; + # and now compare: +connection master; + # first check that data on master is sensible select count(*) from t1; select count(*) from t2; select count(*) from t3; select count(*) from t4; select count(*) from t5; +select count(*) from t11; +select count(*) from t20; +select count(*) from t21; +select count(*) from t22; +select count(*) from t12; +select count(*) from t13; +#select count(*) from t14; +select count(*) from t16; if ($you_want_to_test_UDF) { select count(*) from t6; @@ -196,21 +448,48 @@ if ($you_want_to_test_UDF) select count(*) from t9; } ---replace_column 2 # 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ -show binlog events from 102; sync_slave_with_master; # as we're using UUID we don't SELECT but use "diff" like in rpl_row_UUID --exec $MYSQL_DUMP --compact --order-by-primary --skip-extended-insert --no-create-info mysqltest1 > $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_master.sql --exec $MYSQL_DUMP_SLAVE --compact --order-by-primary --skip-extended-insert --no-create-info mysqltest1 > $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_slave.sql -connection master; -drop database mysqltest1; -sync_slave_with_master; - # Let's compare. Note: If they match test will pass, if they do not match # the test will show that the diff statement failed and not reject file # will be created. You will need to go to the mysql-test dir and diff # the files your self to see what is not matching --exec diff $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_master.sql $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_slave.sql; + +connection master; +# As one stored function's parameter is UUID(), its value ends up in a +# NAME_CONST in the binlog, we must hide it for repeatability +--replace_column 2 # 5 # +--replace_regex /table_id: [0-9]+/table_id: #/ /NAME_CONST\('x',.*/NAME_CONST('x',.../ +show binlog events from 102; + +# Now test that mysqlbinlog works fine on a binlog generated by the +# mixed mode + +# BUG#11312 "DELIMITER is not written to the binary log that causes +# syntax error" makes that mysqlbinlog will fail if we pass it the +# text of queries; this forces us to use --base64-output here. + +# BUG#20929 "BINLOG command causes invalid free plus assertion +# failure" makes mysqld segfault when receiving --base64-output + +# So I can't enable this piece of test +# SIGH + +if ($enable_when_11312_or_20929_fixed) +{ +--exec $MYSQL_BINLOG --base64-output $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_mixed.sql +drop database mysqltest1; +--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/mysqlbinlog_mixed.sql +--exec $MYSQL_DUMP --compact --order-by-primary --skip-extended-insert --no-create-info mysqltest1 > $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_master.sql +# the old mysqldump output on slave is the same as what it was on +# master before restoring on master. +--exec diff $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_master.sql $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_slave.sql; +} + +drop database mysqltest1; +sync_slave_with_master; diff --git a/sql/item_create.cc b/sql/item_create.cc index 6eca6209438..bf4af2232f7 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -426,7 +426,9 @@ Item *create_func_unhex(Item* a) Item *create_func_uuid(void) { THD *thd= current_thd; - thd->lex->binlog_row_based_if_mixed= 1; +#ifdef HAVE_ROW_BASED_REPLICATION + thd->lex->binlog_row_based_if_mixed= TRUE; +#endif return new(thd->mem_root) Item_func_uuid(); } diff --git a/sql/set_var.cc b/sql/set_var.cc index b0ecc7eccef..cac23673016 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1343,9 +1343,9 @@ bool sys_var_thd_binlog_format::is_readonly() const return 1; } /* - if in a stored function, it's too late to change mode + if in a stored function/trigger, it's too late to change mode */ - if (thd->spcont && thd->prelocked_mode) + if (thd->in_sub_stmt) { my_error(ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT, MYF(0)); return 1; diff --git a/sql/sp.cc b/sql/sp.cc index 93e21170156..eb5bc7ecc14 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1632,6 +1632,7 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, sp->add_used_tables_to_table_list(thd, &lex->query_tables_last, rt->belong_to_view); } + sp->propagate_attributes(lex); } first= FALSE; } @@ -1729,14 +1730,16 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, { for (int j= 0; j < (int)TRG_ACTION_MAX; j++) { - if (triggers->bodies[i][j]) + sp_head *trigger_body= triggers->bodies[i][j]; + if (trigger_body) { - (void)triggers->bodies[i][j]-> - add_used_tables_to_table_list(thd, &lex->query_tables_last, - table->belong_to_view); + (void)trigger_body-> + add_used_tables_to_table_list(thd, &lex->query_tables_last, + table->belong_to_view); sp_update_stmt_used_routines(thd, lex, - &triggers->bodies[i][j]->m_sroutines, + &trigger_body->m_sroutines, table->belong_to_view); + trigger_body->propagate_attributes(lex); } } } diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 88460337526..b486aad1be4 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1675,6 +1675,16 @@ sp_head::restore_lex(THD *thd) oldlex->next_state= sublex->next_state; oldlex->trg_table_fields.push_back(&sublex->trg_table_fields); +#ifdef HAVE_ROW_BASED_REPLICATION + /* + If this substatement needs row-based, the entire routine does too (we + cannot switch from statement-based to row-based only for this + substatement). + */ + if (sublex->binlog_row_based_if_mixed) + m_flags|= BINLOG_ROW_BASED_IF_MIXED; +#endif + /* Add routines which are used by statement to respective set for this routine. diff --git a/sql/sp_head.h b/sql/sp_head.h index 791343f0061..0ad0c496b7e 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -126,7 +126,8 @@ public: /* Is set if a procedure with COMMIT (implicit or explicit) | ROLLBACK */ HAS_COMMIT_OR_ROLLBACK= 128, LOG_SLOW_STATEMENTS= 256, // Used by events - LOG_GENERAL_LOG= 512 // Used by events + LOG_GENERAL_LOG= 512, // Used by events + BINLOG_ROW_BASED_IF_MIXED= 1024 }; /* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */ @@ -351,6 +352,25 @@ public: int show_routine_code(THD *thd); #endif + /* + This method is intended for attributes of a routine which need + to propagate upwards to the LEX of the caller (when a property of a + sp_head needs to "taint" the caller). + */ + void propagate_attributes(LEX *lex) + { +#ifdef HAVE_ROW_BASED_REPLICATION + /* + If this routine needs row-based binary logging, the entire top statement + too (we cannot switch from statement-based to row-based only for this + routine, as in statement-based the top-statement may be binlogged and + the substatements not). + */ + if (m_flags & BINLOG_ROW_BASED_IF_MIXED) + lex->binlog_row_based_if_mixed= TRUE; +#endif + } + private: diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 5b039f6bcc0..98877cd5770 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -49,6 +49,8 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, static void close_old_data_files(THD *thd, TABLE *table, bool abort_locks, bool send_refresh); static bool reopen_table(TABLE *table); +static bool +has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables); extern "C" byte *table_cache_key(const byte *record,uint *length, @@ -3315,6 +3317,18 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) *need_reopen= FALSE; +#ifdef HAVE_ROW_BASED_REPLICATION + /* + CREATE ... SELECT UUID() locks no tables, we have to test here. + Note that we will not do the resetting if inside a stored + function/trigger, because the binlogging of those is decided earlier (by + the caller) and can't be changed afterwards. + */ + thd->reset_current_stmt_binlog_row_based(); + if (thd->lex->binlog_row_based_if_mixed) + thd->set_current_stmt_binlog_row_based_if_mixed(); +#endif /*HAVE_ROW_BASED_REPLICATION*/ + if (!tables) DBUG_RETURN(0); @@ -3345,6 +3359,19 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) { thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; +#ifdef HAVE_ROW_BASED_REPLICATION + /* + If we have >= 2 different tables to update with auto_inc columns, + statement-based binlogging won't work. We can solve this problem in + mixed mode by switching to row-based binlogging: + */ + if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED && + has_two_write_locked_tables_with_auto_increment(tables)) + { + thd->lex->binlog_row_based_if_mixed= TRUE; + thd->set_current_stmt_binlog_row_based_if_mixed(); + } +#endif } if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), @@ -6477,3 +6504,46 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table DBUG_VOID_RETURN; } + +/* + Tells if two (or more) tables have auto_increment columns and we want to + lock those tables with a write lock. + + SYNOPSIS + has_two_write_locked_tables_with_auto_increment + tables Table list + + NOTES: + Call this function only when you have established the list of all tables + which you'll want to update (including stored functions, triggers, views + inside your statement). + + RETURN + 0 No + 1 Yes +*/ + +static bool +has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables) +{ + char *first_table_name= NULL, *first_db; + for (TABLE_LIST *table= tables; table; table= table->next_global) + { + /* we must do preliminary checks as table->table may be NULL */ + if (!table->placeholder() && !table->schema_table && + table->table->found_next_number_field && + (table->lock_type >= TL_WRITE_ALLOW_WRITE)) + { + if (first_table_name == NULL) + { + first_table_name= table->table_name; + first_db= table->db; + DBUG_ASSERT(first_db); + } + else if (strcmp(first_db, table->db) || + strcmp(first_table_name, table->table_name)) + return 1; + } + } + return 0; +} diff --git a/sql/sql_class.h b/sql/sql_class.h index 5222e75f309..8fa6db4506b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1418,7 +1418,17 @@ public: inline void set_current_stmt_binlog_row_based_if_mixed() { #ifdef HAVE_ROW_BASED_REPLICATION - if (variables.binlog_format == BINLOG_FORMAT_MIXED) + /* + If in a stored/function trigger, the caller should already have done the + change. We test in_sub_stmt to prevent introducing bugs where people + wouldn't ensure that, and would switch to row-based mode in the middle + of executing a stored function/trigger (which is too late, see also + reset_current_stmt_binlog_row_based()); this condition will make their + tests fail and so force them to propagate the + lex->binlog_row_based_if_mixed upwards to the caller. + */ + if ((variables.binlog_format == BINLOG_FORMAT_MIXED) && + (in_sub_stmt == 0)) current_stmt_binlog_row_based= TRUE; #endif } @@ -1437,8 +1447,26 @@ public: inline void reset_current_stmt_binlog_row_based() { #ifdef HAVE_ROW_BASED_REPLICATION - current_stmt_binlog_row_based= - test(variables.binlog_format == BINLOG_FORMAT_ROW); + /* + If there are temporary tables, don't reset back to + statement-based. Indeed it could be that: + CREATE TEMPORARY TABLE t SELECT UUID(); # row-based + # and row-based does not store updates to temp tables + # in the binlog. + INSERT INTO u SELECT * FROM t; # stmt-based + and then the INSERT will fail as data inserted into t was not logged. + So we continue with row-based until the temp table is dropped. + If we are in a stored function or trigger, we mustn't reset in the + middle of its execution (as the binary logging way of a stored function + or trigger is decided when it starts executing, depending for example on + the caller (for a stored function: if caller is SELECT or + INSERT/UPDATE/DELETE...). + */ + if ((temporary_tables == NULL) && (in_sub_stmt == 0)) + { + current_stmt_binlog_row_based= + test(variables.binlog_format == BINLOG_FORMAT_ROW); + } #else current_stmt_binlog_row_based= FALSE; #endif diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f6031a1f2fd..7e24436fc39 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -183,7 +183,6 @@ void lex_start(THD *thd, const uchar *buf, uint length) lex->nest_level=0 ; lex->allow_sum_func= 0; lex->in_sum_func= NULL; - lex->binlog_row_based_if_mixed= 0; DBUG_VOID_RETURN; } @@ -1625,6 +1624,9 @@ void Query_tables_list::reset_query_tables_list(bool init) sroutines_list.empty(); sroutines_list_own_last= sroutines_list.next; sroutines_list_own_elements= 0; +#ifdef HAVE_ROW_BASED_REPLICATION + binlog_row_based_if_mixed= FALSE; +#endif } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a46aaa0bab7..9d8a918e2a3 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -793,6 +793,16 @@ public: byte **sroutines_list_own_last; uint sroutines_list_own_elements; +#ifdef HAVE_ROW_BASED_REPLICATION + /* + Tells if the parsing stage detected that some items require row-based + binlogging to give a reliable binlog/replication, or if we will use + stored functions or triggers which themselves need require row-based + binlogging. + */ + bool binlog_row_based_if_mixed; +#endif + /* These constructor and destructor serve for creation/destruction of Query_tables_list instances which are used as backup storage. @@ -970,11 +980,7 @@ typedef struct st_lex : public Query_tables_list uint8 create_view_check; bool drop_if_exists, drop_temporary, local_file, one_shot_set; bool in_comment, ignore_space, verbose, no_write_to_binlog; - /* - binlog_row_based_if_mixed tells if the parsing stage detected that some - items require row-based binlogging to give a reliable binlog/replication. - */ - bool tx_chain, tx_release, binlog_row_based_if_mixed; + bool tx_chain, tx_release; /* Special JOIN::prepare mode: changing of query is prohibited. When creating a view, we need to just check its syntax omitting diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 232df095816..1e89e9466ad 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2503,11 +2503,6 @@ mysql_execute_command(THD *thd) statistic_increment(thd->status_var.com_stat[lex->sql_command], &LOCK_status); -#ifdef HAVE_ROW_BASED_REPLICATION - if (lex->binlog_row_based_if_mixed) - thd->set_current_stmt_binlog_row_based_if_mixed(); -#endif /*HAVE_ROW_BASED_REPLICATION*/ - switch (lex->sql_command) { case SQLCOM_SHOW_EVENTS: if ((res= check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, @@ -5166,9 +5161,6 @@ end: */ if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION) reset_one_shot_variables(thd); -#ifdef HAVE_ROW_BASED_REPLICATION - thd->reset_current_stmt_binlog_row_based(); -#endif /*HAVE_ROW_BASED_REPLICATION*/ /* The return value for ROW_COUNT() is "implementation dependent" if the @@ -5846,6 +5838,11 @@ void mysql_reset_thd_for_next_command(THD *thd) thd->rand_used= 0; thd->sent_row_count= thd->examined_row_count= 0; } +#ifdef HAVE_ROW_BASED_REPLICATION + /* If in a routine, we reset only at end of top statement. */ + thd->reset_current_stmt_binlog_row_based(); +#endif /*HAVE_ROW_BASED_REPLICATION*/ + DBUG_VOID_RETURN; } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index d1e7ba80ecf..c81c4294e1c 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -997,6 +997,15 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) table->next_global= view_tables; } +#ifdef HAVE_ROW_BASED_REPLICATION + /* + If the view's body needs row-based binlogging (e.g. the VIEW is created + from SELECT UUID()), the top statement also needs it. + */ + if (lex->binlog_row_based_if_mixed) + old_lex->binlog_row_based_if_mixed= TRUE; +#endif + /* If we are opening this view as part of implicit LOCK TABLES, then this view serves as simple placeholder and we should not continue diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0632e2298cd..6d989feb4ce 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6374,7 +6374,7 @@ simple_expr: if (udf->type == UDFTYPE_AGGREGATE) Select->in_sum_expr--; - Lex->binlog_row_based_if_mixed= 1; + Lex->binlog_row_based_if_mixed= TRUE; switch (udf->returns) { case STRING_RESULT: From 0594e1b84b143da5e2f3a339d7092531d00f290b Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Sun, 9 Jul 2006 17:52:19 +0200 Subject: [PATCH 24/32] WL#3146 "less locking in auto_increment": this is a cleanup patch for our current auto_increment handling: new names for auto_increment variables in THD, new methods to manipulate them (see sql_class.h), some move into handler::, causing less backup/restore work when executing substatements. This makes the logic hopefully clearer, less work is is needed in mysql_insert(). By cleaning up, using different variables for different purposes (instead of one for 3 things...), we fix those bugs, which someone may want to fix in 5.0 too: BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate statement-based" BUG#20341 "stored function inserting into one auto_increment puts bad data in slave" BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE" (now if a row is updated, LAST_INSERT_ID() will return its id) and re-fixes: BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT" (already fixed differently by Ramil in 4.1) Test of documented behaviour of mysql_insert_id() (there was no test). The behaviour changes introduced are: - LAST_INSERT_ID() now returns "the first autogenerated auto_increment value successfully inserted", instead of "the first autogenerated auto_increment value if any row was successfully inserted", see auto_increment.test. Same for mysql_insert_id(), see mysql_client_test.c. - LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY UPDATE, see auto_increment.test. Same for mysql_insert_id(), see mysql_client_test.c. - LAST_INSERT_ID() does not change if no autogenerated value was successfully inserted (it used to then be 0), see auto_increment.test. - if in INSERT SELECT no autogenerated value was successfully inserted, mysql_insert_id() now returns the id of the last inserted row (it already did this for INSERT VALUES), see mysql_client_test.c. - if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X (it already did this for INSERT VALUES), see mysql_client_test.c. - NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE, the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID influences not only the first row now. Additionally, when unlocking a table we check that the thread is not keeping a next_insert_id (as the table is unlocked that id is potentially out-of-date); forgetting about this next_insert_id is done in a new handler::ha_release_auto_increment(). Finally we prepare for engines capable of reserving finite-length intervals of auto_increment values: we store such intervals in THD. The next step (to be done by the replication team in 5.1) is to read those intervals from THD and actually store them in the statement-based binary log. NDB will be a good engine to test that. --- mysql-test/extra/binlog_tests/binlog.test | 32 ++ mysql-test/extra/rpl_tests/rpl_insert_id.test | 55 ++- mysql-test/extra/rpl_tests/rpl_loaddata.test | 3 + mysql-test/r/auto_increment.result | 58 +++- mysql-test/r/binlog_row_binlog.result | 34 ++ mysql-test/r/binlog_stm_binlog.result | 32 ++ mysql-test/r/insert.result | 15 + mysql-test/r/rpl_insert_id.result | 59 ++++ mysql-test/r/rpl_loaddata.result | 6 + mysql-test/r/rpl_ndb_auto_inc.result | 8 +- mysql-test/t/auto_increment.test | 47 +++ mysql-test/t/insert.test | 7 + sql/ha_federated.cc | 5 +- sql/ha_ndbcluster.cc | 4 +- sql/handler.cc | 313 ++++++++++-------- sql/handler.h | 50 ++- sql/item_func.cc | 14 +- sql/log.cc | 48 +-- sql/log_event.cc | 16 +- sql/set_var.cc | 18 +- sql/sql_class.cc | 73 ++-- sql/sql_class.h | 166 ++++++++-- sql/sql_insert.cc | 149 +++++---- sql/sql_load.cc | 23 +- sql/sql_parse.cc | 13 +- sql/sql_select.cc | 10 +- sql/sql_table.cc | 8 +- sql/sql_update.cc | 17 +- sql/structs.h | 96 ++++++ tests/mysql_client_test.c | 189 ++++++++++- 30 files changed, 1225 insertions(+), 343 deletions(-) diff --git a/mysql-test/extra/binlog_tests/binlog.test b/mysql-test/extra/binlog_tests/binlog.test index 6f7990893f0..993b3fbf634 100644 --- a/mysql-test/extra/binlog_tests/binlog.test +++ b/mysql-test/extra/binlog_tests/binlog.test @@ -49,3 +49,35 @@ show binlog events in 'master-bin.000001' from 102; --replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events in 'master-bin.000002' from 102; + +# Test of a too big SET INSERT_ID: see if the truncated value goes +# into binlog (right), or the too big value (wrong); we look at the +# binlog further down with SHOW BINLOG EVENTS. +reset master; +create table t1 (id tinyint auto_increment primary key); +set insert_id=128; +insert into t1 values(null); +select * from t1; +drop table t1; + +# Test of binlogging of INSERT_ID with INSERT DELAYED +create table t1 (a int not null auto_increment, primary key (a)) engine=myisam; +# First, avoid BUG#20627: +set @@session.auto_increment_increment=1, @@session.auto_increment_offset=1; +# Verify that only one INSERT_ID event is binlogged. +insert delayed into t1 values (207); + +# We use sleeps between statements, that's the only way to get a +# repeatable binlog in a normal test run and under Valgrind. +# It may be that the "binlog missing rows" of BUG#20821 shows up +# here. +sleep 2; +insert delayed into t1 values (null); +sleep 2; +insert delayed into t1 values (300); +sleep 2; # time for the delayed queries to reach disk +select * from t1; +--replace_column 2 # 5 # +--replace_regex /table_id: [0-9]+/table_id: #/ +show binlog events from 102; +drop table t1; diff --git a/mysql-test/extra/rpl_tests/rpl_insert_id.test b/mysql-test/extra/rpl_tests/rpl_insert_id.test index 68e39c54381..cbdd88738f5 100644 --- a/mysql-test/extra/rpl_tests/rpl_insert_id.test +++ b/mysql-test/extra/rpl_tests/rpl_insert_id.test @@ -144,6 +144,23 @@ insert into t1 (last_id) values (bug15728()); # This should be exactly one greater than in the previous call. select last_insert_id(); +# BUG#20339 - stored procedure using LAST_INSERT_ID() does not +# replicate statement-based +--disable_warnings +drop procedure if exists foo; +--enable_warnings +delimiter |; +create procedure foo() +begin + declare res int; + insert into t2 (last_id) values (bug15728()); + insert into t1 (last_id) values (bug15728()); +end| +delimiter ;| +call foo(); + +select * from t1; +select * from t2; save_master_pos; connection slave; sync_with_master; @@ -153,8 +170,44 @@ connection master; drop function bug15728; drop function bug15728_insert; -drop table t1, t2; +drop procedure foo; +drop table t1; # End of 5.0 tests +# Test for BUG#20341 "stored function inserting into one +# auto_increment puts bad data in slave" + +truncate table t2; +create table t1 (id tinyint primary key); # no auto_increment + +delimiter |; +create function insid() returns int +begin + insert into t2 (last_id) values (0); + return 0; +end| +delimiter ;| +set sql_log_bin=0; +insert into t2 (id) values(1),(2),(3); +delete from t2; +set sql_log_bin=1; +#inside SELECT, then inside INSERT +select insid(); +set sql_log_bin=0; +insert into t2 (id) values(5),(6),(7); +delete from t2 where id>=5; +set sql_log_bin=1; +insert into t1 select insid(); +select * from t1; +select * from t2; + +sync_slave_with_master; +select * from t1; +select * from t2; + +connection master; +drop table t1, t2; +drop function insid; + sync_slave_with_master; diff --git a/mysql-test/extra/rpl_tests/rpl_loaddata.test b/mysql-test/extra/rpl_tests/rpl_loaddata.test index 5d7c69bd959..61f58ff5279 100644 --- a/mysql-test/extra/rpl_tests/rpl_loaddata.test +++ b/mysql-test/extra/rpl_tests/rpl_loaddata.test @@ -20,8 +20,11 @@ connection slave; reset master; connection master; +select last_insert_id(); create table t1(a int not null auto_increment, b int, primary key(a) ); load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +# verify that LAST_INSERT_ID() is set by LOAD DATA INFILE +select last_insert_id(); create temporary table t2 (day date,id int(9),category enum('a','b','c'),name varchar(60)); load data infile '../std_data_ln/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' starting by '>' ignore 1 lines; diff --git a/mysql-test/r/auto_increment.result b/mysql-test/r/auto_increment.result index 10f26c40553..985d4d2e1f4 100644 --- a/mysql-test/r/auto_increment.result +++ b/mysql-test/r/auto_increment.result @@ -153,7 +153,7 @@ insert into t1 set i = null; ERROR 23000: Duplicate entry '255' for key 'PRIMARY' select last_insert_id(); last_insert_id() -0 +255 drop table t1; create table t1 (i tinyint unsigned not null auto_increment, key (i)); insert into t1 set i = 254; @@ -181,7 +181,7 @@ insert into t1 values (NULL, 10); ERROR 23000: Duplicate entry '10' for key 'b' select last_insert_id(); last_insert_id() -0 +2 drop table t1; create table t1(a int auto_increment,b int null,primary key(a)); SET SQL_MODE=NO_AUTO_VALUE_ON_ZERO; @@ -446,3 +446,57 @@ INSERT INTO t1 VALUES(1, 1); ALTER TABLE t1 CHANGE t1 t1 INT(10) auto_increment; ERROR 23000: ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '1' for key 'PRIMARY' DROP TABLE t1; +CREATE TABLE `t2` ( +`k` int(11) NOT NULL auto_increment, +`a` int(11) default NULL, +`c` int(11) default NULL, +PRIMARY KEY (`k`), +UNIQUE KEY `idx_1` (`a`) +) ENGINE=InnoDB; +insert into t2 ( a ) values ( 6 ) on duplicate key update c = +ifnull( c, +0 ) + 1; +insert into t2 ( a ) values ( 7 ) on duplicate key update c = +ifnull( c, +0 ) + 1; +select last_insert_id(); +last_insert_id() +2 +select * from t2; +k a c +1 6 NULL +2 7 NULL +insert into t2 ( a ) values ( 6 ) on duplicate key update c = +ifnull( c, +0 ) + 1; +select last_insert_id(); +last_insert_id() +1 +select * from t2; +k a c +1 6 1 +2 7 NULL +insert ignore into t2 values (null,6,1),(10,8,1); +select last_insert_id(); +last_insert_id() +1 +insert ignore into t2 values (null,6,1),(null,8,1),(null,15,1),(null,20,1); +select last_insert_id(); +last_insert_id() +11 +select * from t2; +k a c +1 6 1 +2 7 NULL +10 8 1 +11 15 1 +12 20 1 +drop table t2; +create table t1 (a int primary key auto_increment, b int, c int, d timestamp default current_timestamp, unique(b),unique(c)); +insert into t1 values(null,1,1,now()); +insert into t1 values(null,0,0,null); +replace into t1 values(null,1,0,null); +select last_insert_id(); +last_insert_id() +3 +drop table t1; diff --git a/mysql-test/r/binlog_row_binlog.result b/mysql-test/r/binlog_row_binlog.result index 6cb086109b4..17c1d171b5c 100644 --- a/mysql-test/r/binlog_row_binlog.result +++ b/mysql-test/r/binlog_row_binlog.result @@ -235,3 +235,37 @@ master-bin.000001 # Rotate 1 # master-bin.000002;pos=4 show binlog events in 'master-bin.000002' from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000002 # Query 1 # use `test`; drop table t1 +reset master; +create table t1 (id tinyint auto_increment primary key); +set insert_id=128; +insert into t1 values(null); +Warnings: +Warning 1264 Out of range value for column 'id' at row 1 +select * from t1; +id +127 +drop table t1; +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; +insert delayed into t1 values (207); +insert delayed into t1 values (null); +insert delayed into t1 values (300); +select * from t1; +a +207 +208 +300 +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) +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; drop table t1 +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) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +drop table t1; diff --git a/mysql-test/r/binlog_stm_binlog.result b/mysql-test/r/binlog_stm_binlog.result index f9180b69ab6..4e23db4828f 100644 --- a/mysql-test/r/binlog_stm_binlog.result +++ b/mysql-test/r/binlog_stm_binlog.result @@ -145,3 +145,35 @@ master-bin.000001 # Rotate 1 # master-bin.000002;pos=4 show binlog events in 'master-bin.000002' from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000002 # Query 1 # use `test`; drop table t1 +reset master; +create table t1 (id tinyint auto_increment primary key); +set insert_id=128; +insert into t1 values(null); +Warnings: +Warning 1264 Out of range value for column 'id' at row 1 +select * from t1; +id +127 +drop table t1; +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; +insert delayed into t1 values (207); +insert delayed into t1 values (null); +insert delayed into t1 values (300); +select * from t1; +a +207 +208 +300 +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) +master-bin.000001 # Intvar 1 # INSERT_ID=127 +master-bin.000001 # Query 1 # use `test`; insert into t1 values(null) +master-bin.000001 # Query 1 # use `test`; drop table t1 +master-bin.000001 # Query 1 # use `test`; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam +master-bin.000001 # Query 1 # use `test`; insert delayed into t1 values (207) +master-bin.000001 # Intvar 1 # INSERT_ID=208 +master-bin.000001 # Query 1 # use `test`; insert delayed into t1 values (null) +master-bin.000001 # Query 1 # use `test`; insert delayed into t1 values (300) +drop table t1; diff --git a/mysql-test/r/insert.result b/mysql-test/r/insert.result index 235c3f61fe9..b090f0f52c0 100644 --- a/mysql-test/r/insert.result +++ b/mysql-test/r/insert.result @@ -353,3 +353,18 @@ select row_count(); row_count() 1 drop table t1; +create table t1 (id int primary key auto_increment, data int, unique(data)); +insert ignore into t1 values(NULL,100),(NULL,110),(NULL,120); +insert ignore into t1 values(NULL,10),(NULL,20),(NULL,110),(NULL,120),(NULL,100),(NULL,90); +insert ignore into t1 values(NULL,130),(NULL,140),(500,110),(550,120),(450,100),(NULL,150); +select * from t1 order by id; +id data +1 100 +2 110 +3 120 +4 10 +5 20 +6 90 +7 130 +8 140 +9 150 diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index 622b1489f91..fbec8dfa428 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -117,6 +117,14 @@ insert into t1 (last_id) values (bug15728()); select last_insert_id(); last_insert_id() 5 +drop procedure if exists foo; +create procedure foo() +begin +declare res int; +insert into t2 (last_id) values (bug15728()); +insert into t1 (last_id) values (bug15728()); +end| +call foo(); select * from t1; id last_id 1 0 @@ -124,10 +132,61 @@ id last_id 3 2 4 1 5 4 +6 3 select * from t2; id last_id 1 3 2 4 +3 5 +select * from t1; +id last_id +1 0 +2 1 +3 2 +4 1 +5 4 +6 3 +select * from t2; +id last_id +1 3 +2 4 +3 5 drop function bug15728; drop function bug15728_insert; +drop procedure foo; +drop table t1; +truncate table t2; +create table t1 (id tinyint primary key); +create function insid() returns int +begin +insert into t2 (last_id) values (0); +return 0; +end| +set sql_log_bin=0; +insert into t2 (id) values(1),(2),(3); +delete from t2; +set sql_log_bin=1; +select insid(); +insid() +0 +set sql_log_bin=0; +insert into t2 (id) values(5),(6),(7); +delete from t2 where id>=5; +set sql_log_bin=1; +insert into t1 select insid(); +select * from t1; +id +0 +select * from t2; +id last_id +4 0 +8 0 +select * from t1; +id +0 +select * from t2; +id last_id +4 0 +8 0 drop table t1, t2; +drop function insid; diff --git a/mysql-test/r/rpl_loaddata.result b/mysql-test/r/rpl_loaddata.result index 47e056429ce..4ffa65c2c82 100644 --- a/mysql-test/r/rpl_loaddata.result +++ b/mysql-test/r/rpl_loaddata.result @@ -5,8 +5,14 @@ reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; reset master; +select last_insert_id(); +last_insert_id() +0 create table t1(a int not null auto_increment, b int, primary key(a) ); load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +select last_insert_id(); +last_insert_id() +1 create temporary table t2 (day date,id int(9),category enum('a','b','c'),name varchar(60)); load data infile '../std_data_ln/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' starting by '>' ignore 1 lines; create table t3 (day date,id int(9),category enum('a','b','c'),name varchar(60)); diff --git a/mysql-test/r/rpl_ndb_auto_inc.result b/mysql-test/r/rpl_ndb_auto_inc.result index 71217442698..dd4cc90a75f 100644 --- a/mysql-test/r/rpl_ndb_auto_inc.result +++ b/mysql-test/r/rpl_ndb_auto_inc.result @@ -71,8 +71,8 @@ a 250 251 400 +401 1000 -1001 ******* Select from Slave ************* select * from t1 ORDER BY a; @@ -83,8 +83,8 @@ a 250 251 400 +401 1000 -1001 drop table t1; create table t1 (a int not null auto_increment, primary key (a)) engine=NDB; insert into t1 values (NULL),(5),(NULL),(NULL); @@ -120,8 +120,6 @@ a 502 503 600 -603 -604 610 611 ******* Select from Slave ************* @@ -137,8 +135,6 @@ a 502 503 600 -603 -604 610 611 drop table t1; diff --git a/mysql-test/t/auto_increment.test b/mysql-test/t/auto_increment.test index 2674639d0ac..7cef1bad784 100644 --- a/mysql-test/t/auto_increment.test +++ b/mysql-test/t/auto_increment.test @@ -303,3 +303,50 @@ INSERT INTO t1 VALUES(1, 1); --error ER_DUP_ENTRY ALTER TABLE t1 CHANGE t1 t1 INT(10) auto_increment; DROP TABLE t1; + +# Fix for BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY +# UPDATE": now LAST_INSERT_ID() will return the id of the updated +# row. +CREATE TABLE `t2` ( + `k` int(11) NOT NULL auto_increment, + `a` int(11) default NULL, + `c` int(11) default NULL, + PRIMARY KEY (`k`), + UNIQUE KEY `idx_1` (`a`) +) ENGINE=InnoDB; + insert into t2 ( a ) values ( 6 ) on duplicate key update c = +ifnull( c, +0 ) + 1; +insert into t2 ( a ) values ( 7 ) on duplicate key update c = +ifnull( c, +0 ) + 1; +select last_insert_id(); +select * from t2; +insert into t2 ( a ) values ( 6 ) on duplicate key update c = +ifnull( c, +0 ) + 1; +select last_insert_id(); +select * from t2; + +# Test of LAST_INSERT_ID() when autogenerated will fail: +# last_insert_id() should not change +insert ignore into t2 values (null,6,1),(10,8,1); +select last_insert_id(); +# First and second autogenerated will fail, last_insert_id() should +# point to third +insert ignore into t2 values (null,6,1),(null,8,1),(null,15,1),(null,20,1); +select last_insert_id(); +select * from t2; + +drop table t2; + +# Test of REPLACE when it does INSERT+DELETE and not UPDATE: +# see if it sets LAST_INSERT_ID() ok +create table t1 (a int primary key auto_increment, b int, c int, d timestamp default current_timestamp, unique(b),unique(c)); +insert into t1 values(null,1,1,now()); +insert into t1 values(null,0,0,null); +# this will delete two rows +replace into t1 values(null,1,0,null); +select last_insert_id(); + +drop table t1; diff --git a/mysql-test/t/insert.test b/mysql-test/t/insert.test index 3711e2986ed..0cc25469705 100644 --- a/mysql-test/t/insert.test +++ b/mysql-test/t/insert.test @@ -234,3 +234,10 @@ select row_count(); insert into t1 values (5, 5) on duplicate key update data= data + 10; select row_count(); drop table t1; + +# Test of INSERT IGNORE and re-using auto_increment values +create table t1 (id int primary key auto_increment, data int, unique(data)); +insert ignore into t1 values(NULL,100),(NULL,110),(NULL,120); +insert ignore into t1 values(NULL,10),(NULL,20),(NULL,110),(NULL,120),(NULL,100),(NULL,90); +insert ignore into t1 values(NULL,130),(NULL,140),(500,110),(550,120),(450,100),(NULL,150); +select * from t1 order by id; diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index 91111a433dc..e592102f53e 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -1710,14 +1710,15 @@ int ha_federated::write_row(byte *buf) This method ensures that last_insert_id() works properly. What it simply does is calls last_insert_id() on the foreign database immediately after insert (if the table has an auto_increment field) and sets the insert id via - thd->insert_id(ID) (as well as storing thd->prev_insert_id) + thd->insert_id(ID)). */ void ha_federated::update_auto_increment(void) { THD *thd= current_thd; DBUG_ENTER("ha_federated::update_auto_increment"); - thd->insert_id(mysql->last_used_con->insert_id); + thd->first_successful_insert_id_in_cur_stmt= + mysql->last_used_con->insert_id; DBUG_PRINT("info",("last_insert_id %d", stats.auto_increment_value)); DBUG_VOID_RETURN; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index bbeea2ca1ba..d09907781e1 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -2473,9 +2473,7 @@ int ha_ndbcluster::write_row(byte *record) m_skip_auto_increment= FALSE; update_auto_increment(); - /* Ensure that handler is always called for auto_increment values */ - thd->next_insert_id= 0; - m_skip_auto_increment= !auto_increment_column_changed; + m_skip_auto_increment= (insert_id_for_cur_row == 0); } } diff --git a/sql/handler.cc b/sql/handler.cc index 0dfb31fba8c..51b95030929 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1514,7 +1514,10 @@ int handler::read_first_row(byte * buf, uint primary_key) } /* - Generate the next auto-increment number based on increment and offset + Generate the next auto-increment number based on increment and offset: + computes the lowest number + - strictly greater than "nr" + - of the form: auto_increment_offset + N * auto_increment_increment In most cases increment= offset= 1, in which case we get: 1,2,3,4,5,... @@ -1523,8 +1526,10 @@ int handler::read_first_row(byte * buf, uint primary_key) */ inline ulonglong -next_insert_id(ulonglong nr,struct system_variables *variables) +compute_next_insert_id(ulonglong nr,struct system_variables *variables) { + if (variables->auto_increment_increment == 1) + return (nr+1); // optimization of the formula below nr= (((nr+ variables->auto_increment_increment - variables->auto_increment_offset)) / (ulonglong) variables->auto_increment_increment); @@ -1546,7 +1551,7 @@ next_insert_id(ulonglong nr,struct system_variables *variables) IMPLEMENTATION - Updates columns with type NEXT_NUMBER if: + Updates the record's Field of type NEXT_NUMBER if: - If column value is set to NULL (in which case auto_increment_field_not_null is 0) @@ -1554,25 +1559,31 @@ next_insert_id(ulonglong nr,struct system_variables *variables) set. In the future we will only set NEXT_NUMBER fields if one sets them to NULL (or they are not included in the insert list). + In those cases, we check if the currently reserved interval still has + values we have not used. If yes, we pick the smallest one and use it. + Otherwise: - There are two different cases when the above is true: + - If a list of intervals has been provided to the statement via SET + INSERT_ID or via an Intvar_log_event (in a replication slave), we pick the + first unused interval from this list, consider it as reserved. - - thd->next_insert_id == 0 (This is the normal case) - In this case we set the set the column for the first row to the value - next_insert_id(get_auto_increment(column))) which is normally - max-used-column-value +1. + - Otherwise we set the column for the first row to the value + next_insert_id(get_auto_increment(column))) which is usually + max-used-column-value+1. + We call get_auto_increment() for the first row in a multi-row + statement. get_auto_increment() will tell us the interval of values it + reserved for us. - We call get_auto_increment() only for the first row in a multi-row - statement. For the following rows we generate new numbers based on the - last used number. + - In both cases, for the following rows we use those reserved values without + calling the handler again (we just progress in the interval, computing + each new value from the previous one). Until we have exhausted them, then + we either take the next provided interval or call get_auto_increment() + again to reserve a new interval. - - thd->next_insert_id != 0. This happens when we have read an Intvar event - of type INSERT_ID_EVENT from the binary log or when one has used SET - INSERT_ID=#. - - In this case we will set the column to the value of next_insert_id. - The next row will be given the id - next_insert_id(next_insert_id) + - In both cases, the reserved intervals are remembered in + thd->auto_inc_intervals_in_cur_stmt_for_binlog if statement-based + binlogging; the last reserved interval is remembered in + auto_inc_interval_for_cur_row. The idea is that generated auto_increment values are predictable and independent of the column values in the table. This is needed to be @@ -1583,7 +1594,13 @@ next_insert_id(ulonglong nr,struct system_variables *variables) inserts a column with a higher value than the last used one, we will start counting from the inserted value. - thd->next_insert_id is cleared after it's been used for a statement. + This function's "outputs" are: the table's auto_increment field is filled + with a value, thd->next_insert_id is filled with the value to use for the + next row, if a value was autogenerated for the current row it is stored in + thd->insert_id_for_cur_row, if get_auto_increment() was called + thd->auto_inc_interval_for_cur_row is modified, if that interval is not + present in thd->auto_inc_intervals_in_cur_stmt_for_binlog it is added to + this list. TODO @@ -1600,7 +1617,8 @@ next_insert_id(ulonglong nr,struct system_variables *variables) bool handler::update_auto_increment() { - ulonglong nr; + ulonglong nr, nb_reserved_values; + bool append= FALSE; THD *thd= table->in_use; struct system_variables *variables= &thd->variables; bool auto_increment_field_not_null; @@ -1608,10 +1626,10 @@ bool handler::update_auto_increment() DBUG_ENTER("handler::update_auto_increment"); /* - We must save the previous value to be able to restore it if the - row was not inserted + next_insert_id is a "cursor" into the reserved interval, it may go greater + than the interval, but not smaller. */ - thd->prev_insert_id= thd->next_insert_id; + DBUG_ASSERT(next_insert_id >= auto_inc_interval_for_cur_row.minimum()); auto_increment_field_not_null= table->auto_increment_field_not_null; table->auto_increment_field_not_null= FALSE; // to reset for next row @@ -1619,134 +1637,141 @@ bool handler::update_auto_increment() auto_increment_field_not_null && thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) { - /* - The user did specify a value for the auto_inc column, we don't generate - a new value, write it down. - */ - auto_increment_column_changed=0; - /* Update next_insert_id if we had already generated a value in this statement (case of INSERT VALUES(null),(3763),(null): the last NULL needs to insert 3764, not the value of the first NULL plus 1). */ - if (thd->clear_next_insert_id && nr >= thd->next_insert_id) - { - if (variables->auto_increment_increment != 1) - nr= next_insert_id(nr, variables); - else - nr++; - thd->next_insert_id= nr; - DBUG_PRINT("info",("next_insert_id: %lu", (ulong) nr)); - } + if ((next_insert_id > 0) && (nr >= next_insert_id)) + set_next_insert_id(compute_next_insert_id(nr, variables)); + insert_id_for_cur_row= 0; // didn't generate anything DBUG_RETURN(0); } - if (!(nr= thd->next_insert_id)) - { - ulonglong nb_desired_values= 1, nb_reserved_values; -#ifdef TO_BE_ENABLED_SOON - /* - Reserved intervals will be stored in "THD::auto_inc_intervals". - handler::estimation_rows_to_insert will be the argument passed by - handler::ha_start_bulk_insert(). - */ - uint estimation_known= test(estimation_rows_to_insert > 0); - uint nb_already_reserved_intervals= thd->auto_inc_intervals.nb_elements(); - /* - If an estimation was given to the engine: - - use it. - - if we already reserved numbers, it means the estimation was - not accurate, then we'll reserve 2*AUTO_INC_DEFAULT_NB_VALUES the 2nd - time, twice that the 3rd time etc. - If no estimation was given, use those increasing defaults from the - start, starting from AUTO_INC_DEFAULT_NB_VALUES. - Don't go beyond a max to not reserve "way too much" (because reservation - means potentially losing unused values). - */ - if (nb_already_reserved_intervals == 0 && estimation_known) - nb_desired_values= estimation_rows_to_insert; - else /* go with the increasing defaults */ - { - /* avoid overflow in formula, with this if() */ - if (nb_already_reserved_intervals <= AUTO_INC_DEFAULT_NB_MAX_BITS) - { - nb_desired_values= AUTO_INC_DEFAULT_NB_VALUES * - (1 << nb_already_reserved_intervals); - set_if_smaller(nb_desired_values, AUTO_INC_DEFAULT_NB_MAX); - } - else - nb_desired_values= AUTO_INC_DEFAULT_NB_MAX; - } -#endif - /* This call ignores all its parameters but nr, currently */ - get_auto_increment(variables->auto_increment_offset, - variables->auto_increment_increment, - nb_desired_values, &nr, - &nb_reserved_values); - if (nr == ~(ulonglong) 0) - result= 1; // Mark failure - /* - That should not be needed when engines actually use offset and increment - above. - */ - if (variables->auto_increment_increment != 1) - nr= next_insert_id(nr-1, variables); - /* - Update next row based on the found value. This way we don't have to - call the handler for every generated auto-increment value on a - multi-row statement - */ - thd->next_insert_id= nr; + if ((nr= next_insert_id) >= auto_inc_interval_for_cur_row.maximum()) + { + /* next_insert_id is beyond what is reserved, so we reserve more. */ + const Discrete_interval *forced= + thd->auto_inc_intervals_forced.get_next(); + if (forced != NULL) + { + nr= forced->minimum(); + nb_reserved_values= forced->values(); + } + else + { + /* + handler::estimation_rows_to_insert was set by + handler::ha_start_bulk_insert(); if 0 it means "unknown". + */ + uint nb_already_reserved_intervals= + thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements(); + ulonglong nb_desired_values; + /* + If an estimation was given to the engine: + - use it. + - if we already reserved numbers, it means the estimation was + not accurate, then we'll reserve 2*AUTO_INC_DEFAULT_NB_ROWS the 2nd + time, twice that the 3rd time etc. + If no estimation was given, use those increasing defaults from the + start, starting from AUTO_INC_DEFAULT_NB_ROWS. + Don't go beyond a max to not reserve "way too much" (because + reservation means potentially losing unused values). + */ + if (nb_already_reserved_intervals == 0 && + (estimation_rows_to_insert > 0)) + nb_desired_values= estimation_rows_to_insert; + else /* go with the increasing defaults */ + { + /* avoid overflow in formula, with this if() */ + if (nb_already_reserved_intervals <= AUTO_INC_DEFAULT_NB_MAX_BITS) + { + nb_desired_values= AUTO_INC_DEFAULT_NB_ROWS * + (1 << nb_already_reserved_intervals); + set_if_smaller(nb_desired_values, AUTO_INC_DEFAULT_NB_MAX); + } + else + nb_desired_values= AUTO_INC_DEFAULT_NB_MAX; + } + /* This call ignores all its parameters but nr, currently */ + get_auto_increment(variables->auto_increment_offset, + variables->auto_increment_increment, + nb_desired_values, &nr, + &nb_reserved_values); + if (nr == ~(ulonglong) 0) + result= 1; // Mark failure + + /* + That rounding below should not be needed when all engines actually + respect offset and increment in get_auto_increment(). But they don't + so we still do it. Wonder if for the not-first-in-index we should do + it. Hope that this rounding didn't push us out of the interval; even + if it did we cannot do anything about it (calling the engine again + will not help as we inserted no row). + */ + nr= compute_next_insert_id(nr-1, variables); + } + + if (table->s->next_number_key_offset == 0) + { + /* We must defer the appending until "nr" has been possibly truncated */ + append= TRUE; + } + else + { + /* + For such auto_increment there is no notion of interval, just a + singleton. The interval is not even stored in + thd->auto_inc_interval_for_cur_row, so we are sure to call the engine + for next row. + */ + DBUG_PRINT("info",("auto_increment: special not-first-in-index")); + } } DBUG_PRINT("info",("auto_increment: %lu", (ulong) nr)); - /* Mark that we should clear next_insert_id before next stmt */ - thd->clear_next_insert_id= 1; - - if (!table->next_number_field->store((longlong) nr, TRUE)) - thd->insert_id((ulonglong) nr); - else - thd->insert_id(table->next_number_field->val_int()); - - /* - We can't set next_insert_id if the auto-increment key is not the - first key part, as there is no guarantee that the first parts will be in - sequence - */ - if (!table->s->next_number_key_offset) + if (unlikely(table->next_number_field->store((longlong) nr, TRUE))) { /* - Set next insert id to point to next auto-increment value to be able to - handle multi-row statements - This works even if auto_increment_increment > 1 + field refused this value (overflow) and truncated it, use the result of + the truncation (which is going to be inserted). + That will shift the left bound of the reserved interval, we don't + bother shifting the right bound (anyway any other value from this + interval will cause a duplicate key). */ - thd->next_insert_id= next_insert_id(nr, variables); + nr= table->next_number_field->val_int(); + } + if (append) + { + auto_inc_interval_for_cur_row.replace(nr, nb_reserved_values, + variables->auto_increment_increment); + /* Row-based replication does not need to store intervals in binlog */ + if (!thd->current_stmt_binlog_row_based) + result= result || + thd->auto_inc_intervals_in_cur_stmt_for_binlog.append(auto_inc_interval_for_cur_row.minimum(), + auto_inc_interval_for_cur_row.values(), + variables->auto_increment_increment); } - else - thd->next_insert_id= 0; - /* Mark that we generated a new value */ - auto_increment_column_changed=1; + /* + Record this autogenerated value. If the caller then + succeeds to insert this value, it will call + record_first_successful_insert_id_in_cur_stmt() + which will set first_successful_insert_id_in_cur_stmt if it's not + already set. + */ + insert_id_for_cur_row= nr; + /* + Set next insert id to point to next auto-increment value to be able to + handle multi-row statements. + */ + set_next_insert_id(compute_next_insert_id(nr, variables)); + DBUG_RETURN(result); } -/* - restore_auto_increment - - In case of error on write, we restore the last used next_insert_id value - because the previous value was not used. -*/ - -void handler::restore_auto_increment() -{ - THD *thd= table->in_use; - if (thd->next_insert_id) - thd->next_insert_id= thd->prev_insert_id; -} - /* MySQL signal that it changed the column bitmap @@ -1840,6 +1865,23 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment, } +void handler::ha_release_auto_increment() +{ + release_auto_increment(); + insert_id_for_cur_row= 0; + auto_inc_interval_for_cur_row.replace(0, 0, 0); + if (next_insert_id > 0) + { + next_insert_id= 0; + /* + this statement used forced auto_increment values if there were some, + wipe them away for other statements. + */ + table->in_use->auto_inc_intervals_forced.empty(); + } +} + + void handler::print_keydup_error(uint key_nr, const char *msg) { /* Write the duplicated key in the error message */ @@ -3369,10 +3411,13 @@ namespace int handler::ha_external_lock(THD *thd, int lock_type) { DBUG_ENTER("handler::ha_external_lock"); - int error; - if (unlikely(error= external_lock(thd, lock_type))) - DBUG_RETURN(error); - DBUG_RETURN(0); + /* + Whether this is lock or unlock, this should be true, and is to verify that + if get_auto_increment() was called (thus may have reserved intervals or + taken a table lock), ha_release_auto_increment() was too. + */ + DBUG_ASSERT(next_insert_id == 0); + DBUG_RETURN(external_lock(thd, lock_type)); } diff --git a/sql/handler.h b/sql/handler.h index 94f4519a2e7..d1ed6669037 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -906,16 +906,37 @@ public: uint ref_length; FT_INFO *ft_handler; enum {NONE=0, INDEX, RND} inited; - bool auto_increment_column_changed; bool implicit_emptied; /* Can be !=0 only if HEAP */ const COND *pushed_cond; + /* + next_insert_id is the next value which should be inserted into the + auto_increment column: in a inserting-multi-row statement (like INSERT + SELECT), for the first row where the autoinc value is not specified by the + statement, get_auto_increment() called and asked to generate a value, + next_insert_id is set to the next value, then for all other rows + next_insert_id is used (and increased each time) without calling + get_auto_increment(). + */ + ulonglong next_insert_id; + /* + insert id for the current row (*autogenerated*; if not + autogenerated, it's 0). + At first successful insertion, this variable is stored into + THD::first_successful_insert_id_in_cur_stmt. + */ + ulonglong insert_id_for_cur_row; + /* + Interval returned by get_auto_increment() and being consumed by the + inserter. + */ + Discrete_interval auto_inc_interval_for_cur_row; handler(const handlerton *ht_arg, TABLE_SHARE *share_arg) :table_share(share_arg), estimation_rows_to_insert(0), ht(ht_arg), ref(0), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), ref_length(sizeof(my_off_t)), ft_handler(0), inited(NONE), implicit_emptied(0), - pushed_cond(NULL) + pushed_cond(NULL), next_insert_id(0), insert_id_for_cur_row(0) {} virtual ~handler(void) { @@ -1227,9 +1248,30 @@ public: ulonglong nb_desired_values, ulonglong *first_value, ulonglong *nb_reserved_values); +private: virtual void release_auto_increment() { return; }; - virtual void restore_auto_increment(); - +public: + void ha_release_auto_increment(); + void set_next_insert_id(ulonglong id) + { + DBUG_PRINT("info",("auto_increment: next value %lu", (ulong)id)); + next_insert_id= id; + } + void restore_auto_increment(ulonglong prev_insert_id) + { + /* + Insertion of a row failed, re-use the lastly generated auto_increment + id, for the next row. This is achieved by resetting next_insert_id to + what it was before the failed insertion (that old value is provided by + the caller). If that value was 0, it was the first row of the INSERT; + then if insert_id_for_cur_row contains 0 it means no id was generated + for this first row, so no id was generated since the INSERT started, so + we should set next_insert_id to 0; if insert_id_for_cur_row is not 0, it + is the generated id of the first and failed row, so we use it. + */ + next_insert_id= (prev_insert_id > 0) ? prev_insert_id : + insert_id_for_cur_row; + } /* Reset the auto-increment counter to the given value, i.e. the next row inserted will get the given value. This is called e.g. after TRUNCATE diff --git a/sql/item_func.cc b/sql/item_func.cc index 8139ba81777..b24f61d9011 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3283,12 +3283,20 @@ longlong Item_func_last_insert_id::val_int() if (arg_count) { longlong value= args[0]->val_int(); - thd->insert_id(value); null_value= args[0]->null_value; - return value; // Avoid side effect of insert_id() + /* + LAST_INSERT_ID(X) must affect the client's mysql_insert_id() as + documented in the manual. We don't want to touch + first_successful_insert_id_in_cur_stmt because it would make + LAST_INSERT_ID(X) take precedence over an generated auto_increment + value for this row. + */ + thd->arg_of_last_insert_id_function= TRUE; + thd->first_successful_insert_id_in_prev_stmt= value; + return value; } thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id(); + return thd->read_first_successful_insert_id_in_prev_stmt(); } /* This function is just used to test speed of different functions */ diff --git a/sql/log.cc b/sql/log.cc index ec73400ea3c..c5f5743c51a 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -430,16 +430,23 @@ bool Log_to_csv_event_handler:: table->field[6]->set_notnull(); } - if (thd->last_insert_id_used) + if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt) { - table->field[7]->store((longlong) thd->current_insert_id, TRUE); + table->field[7]->store((longlong) + thd->first_successful_insert_id_in_prev_stmt_for_binlog, TRUE); table->field[7]->set_notnull(); } - /* set value if we do an insert on autoincrement column */ - if (thd->insert_id_used) + /* + Set value if we do an insert on autoincrement column. Note that for + some engines (those for which get_auto_increment() does not leave a + table lock until the statement ends), this is just the first value and + the next ones used may not be contiguous to it. + */ + if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0) { - table->field[8]->store((longlong) thd->last_insert_id, TRUE); + table->field[8]->store((longlong) + thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(), TRUE); table->field[8]->set_notnull(); } @@ -729,7 +736,6 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, Security_context *sctx= thd->security_ctx; uint message_buff_len= 0, user_host_len= 0; longlong query_time= 0, lock_time= 0; - longlong last_insert_id= 0, insert_id= 0; /* Print the message to the buffer if we have slow log enabled @@ -764,13 +770,6 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, lock_time= (longlong) (thd->time_after_lock - query_start_arg); } - if (thd->last_insert_id_used) - last_insert_id= (longlong) thd->current_insert_id; - - /* set value if we do an insert on autoincrement column */ - if (thd->insert_id_used) - insert_id= (longlong) thd->last_insert_id; - if (!query) { is_command= TRUE; @@ -1922,18 +1921,22 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, tmp_errno= errno; strmov(db,thd->db); } - if (thd->last_insert_id_used) + if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt) { end=strmov(end, ",last_insert_id="); - end=longlong10_to_str((longlong) thd->current_insert_id, end, -10); + end=longlong10_to_str((longlong) + thd->first_successful_insert_id_in_prev_stmt_for_binlog, + end, -10); } // Save value if we do an insert. - if (thd->insert_id_used) + if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0) { if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) { end=strmov(end,",insert_id="); - end=longlong10_to_str((longlong) thd->last_insert_id, end, -10); + end=longlong10_to_str((longlong) + thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(), + end, -10); } } @@ -3354,21 +3357,24 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) { if (!thd->current_stmt_binlog_row_based) { - if (thd->last_insert_id_used) + if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt) { Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT, - thd->current_insert_id); + thd->first_successful_insert_id_in_prev_stmt_for_binlog); if (e.write(file)) goto err; } - if (thd->insert_id_used) + if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0) { + DBUG_PRINT("info",("number of auto_inc intervals: %lu", + thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements())); /* If the auto_increment was second in a table's index (possible with MyISAM or BDB) (table->next_number_key_offset != 0), such event is in fact not necessary. We could avoid logging it. */ - Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id); + Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT, + thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum()); if (e.write(file)) goto err; } diff --git a/sql/log_event.cc b/sql/log_event.cc index 36805e0043d..2ecf0b1844f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1921,6 +1921,16 @@ end: thd->query_length= thd->db_length =0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); close_thread_tables(thd); + /* + As a disk space optimization, future masters will not log an event for + LAST_INSERT_ID() if that function returned 0 (and thus they will be able + to replace the THD::stmt_depends_on_first_successful_insert_id_in_prev_stmt + variable by (THD->first_successful_insert_id_in_prev_stmt > 0) ; with the + resetting below we are ready to support that. + */ + thd->first_successful_insert_id_in_prev_stmt_for_binlog= 0; + thd->first_successful_insert_id_in_prev_stmt= 0; + thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); /* If there was an error we stop. Otherwise we increment positions. Note that @@ -3399,11 +3409,11 @@ int Intvar_log_event::exec_event(struct st_relay_log_info* rli) { switch (type) { case LAST_INSERT_ID_EVENT: - thd->last_insert_id_used = 1; - thd->last_insert_id = val; + thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1; + thd->first_successful_insert_id_in_prev_stmt= val; break; case INSERT_ID_EVENT: - thd->next_insert_id = val; + thd->force_one_auto_inc_interval(val); break; } rli->inc_event_relay_log_pos(); diff --git a/sql/set_var.cc b/sql/set_var.cc index b0ecc7eccef..8616f0c5e60 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2794,7 +2794,8 @@ byte *sys_var_timestamp::value_ptr(THD *thd, enum_var_type type, bool sys_var_last_insert_id::update(THD *thd, set_var *var) { - thd->insert_id(var->save_result.ulonglong_value); + thd->first_successful_insert_id_in_prev_stmt= + var->save_result.ulonglong_value; return 0; } @@ -2802,14 +2803,19 @@ bool sys_var_last_insert_id::update(THD *thd, set_var *var) byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - thd->sys_var_tmp.long_value= (long) thd->insert_id(); - return (byte*) &thd->last_insert_id; + /* + this tmp var makes it robust againt change of type of + read_first_successful_insert_id_in_prev_stmt(). + */ + thd->sys_var_tmp.ulonglong_value= + thd->read_first_successful_insert_id_in_prev_stmt(); + return (byte*) &thd->sys_var_tmp.ulonglong_value; } bool sys_var_insert_id::update(THD *thd, set_var *var) { - thd->next_insert_id= var->save_result.ulonglong_value; + thd->force_one_auto_inc_interval(var->save_result.ulonglong_value); return 0; } @@ -2817,7 +2823,9 @@ bool sys_var_insert_id::update(THD *thd, set_var *var) byte *sys_var_insert_id::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - return (byte*) &thd->current_insert_id; + thd->sys_var_tmp.ulonglong_value= + thd->auto_inc_intervals_forced.minimum(); + return (byte*) &thd->sys_var_tmp.ulonglong_value; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index c8c8ff16199..2d2f27256ba 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -208,8 +208,12 @@ THD::THD() #endif /*HAVE_ROW_BASED_REPLICATION*/ global_read_lock(0), is_fatal_error(0), rand_used(0), time_zone_used(0), - last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0), + arg_of_last_insert_id_function(FALSE), + first_successful_insert_id_in_prev_stmt(0), + first_successful_insert_id_in_prev_stmt_for_binlog(0), + first_successful_insert_id_in_cur_stmt(0), in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE), + stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE), spcont(NULL) { stmt_arena= this; @@ -224,7 +228,6 @@ THD::THD() killed= NOT_KILLED; db_length= col_access=0; query_error= tmp_table_used= 0; - next_insert_id=last_insert_id=0; hash_clear(&handler_tables_hash); tmp_table=0; used_tables=0; @@ -628,11 +631,26 @@ bool THD::store_globals() void THD::cleanup_after_query() { - if (clear_next_insert_id) + /* + If in stored function or trigger, where statement-based binlogging logs + only the caller, the insert_id/last_insert_id stored in binlog must + describe their first values inside the routine or caller (the values when + they were first set). Otherwise (e.g. stored procedure) it must describe + their values for the current substatement. + */ + if (!prelocked_mode) { - clear_next_insert_id= 0; - next_insert_id= 0; + stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; + auto_inc_intervals_in_cur_stmt_for_binlog.empty(); } + if (first_successful_insert_id_in_cur_stmt > 0) + { + /* set what LAST_INSERT_ID() will return */ + first_successful_insert_id_in_prev_stmt= + first_successful_insert_id_in_cur_stmt; + first_successful_insert_id_in_cur_stmt= 0; + } + arg_of_last_insert_id_function= 0; /* Free Items that were created during this execution */ free_items(); /* Reset where. */ @@ -2139,18 +2157,16 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, backup->in_sub_stmt= in_sub_stmt; backup->no_send_ok= net.no_send_ok; backup->enable_slow_log= enable_slow_log; - backup->last_insert_id= last_insert_id; - backup->next_insert_id= next_insert_id; - backup->current_insert_id= current_insert_id; - backup->insert_id_used= insert_id_used; - backup->last_insert_id_used= last_insert_id_used; - backup->clear_next_insert_id= clear_next_insert_id; backup->limit_found_rows= limit_found_rows; backup->examined_row_count= examined_row_count; backup->sent_row_count= sent_row_count; backup->cuted_fields= cuted_fields; backup->client_capabilities= client_capabilities; backup->savepoints= transaction.savepoints; + backup->first_successful_insert_id_in_prev_stmt= + first_successful_insert_id_in_prev_stmt; + backup->first_successful_insert_id_in_cur_stmt= + first_successful_insert_id_in_cur_stmt; if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) && !current_stmt_binlog_row_based) @@ -2160,12 +2176,11 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; - next_insert_id= 0; - insert_id_used= 0; examined_row_count= 0; sent_row_count= 0; cuted_fields= 0; transaction.savepoints= 0; + first_successful_insert_id_in_cur_stmt= 0; /* Surpress OK packets in case if we will execute statements */ net.no_send_ok= TRUE; @@ -2193,12 +2208,10 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) in_sub_stmt= backup->in_sub_stmt; net.no_send_ok= backup->no_send_ok; enable_slow_log= backup->enable_slow_log; - last_insert_id= backup->last_insert_id; - next_insert_id= backup->next_insert_id; - current_insert_id= backup->current_insert_id; - insert_id_used= backup->insert_id_used; - last_insert_id_used= backup->last_insert_id_used; - clear_next_insert_id= backup->clear_next_insert_id; + first_successful_insert_id_in_prev_stmt= + backup->first_successful_insert_id_in_prev_stmt; + first_successful_insert_id_in_cur_stmt= + backup->first_successful_insert_id_in_cur_stmt; limit_found_rows= backup->limit_found_rows; sent_row_count= backup->sent_row_count; client_capabilities= backup->client_capabilities; @@ -2779,4 +2792,26 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, DBUG_RETURN(0); } +bool Discrete_intervals_list::append(ulonglong start, ulonglong val, + ulonglong incr) +{ + DBUG_ENTER("Discrete_intervals_list::append"); + /* first, see if this can be merged with previous */ + if ((head == NULL) || tail->merge_if_contiguous(start, val, incr)) + { + /* it cannot, so need to add a new interval */ + Discrete_interval *new_interval= new Discrete_interval(start, val, incr); + if (unlikely(new_interval == NULL)) // out of memory + DBUG_RETURN(1); + DBUG_PRINT("info",("adding new auto_increment interval")); + if (head == NULL) + head= current= new_interval; + else + tail->next= new_interval; + tail= new_interval; + elements++; + } + DBUG_RETURN(0); +} + #endif /* !defined(MYSQL_CLIENT) */ diff --git a/sql/sql_class.h b/sql/sql_class.h index 5222e75f309..ee18ddec225 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -770,12 +770,14 @@ class Sub_statement_state { public: ulonglong options; - ulonglong last_insert_id, next_insert_id, current_insert_id; + ulonglong first_successful_insert_id_in_prev_stmt; + ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row; + Discrete_interval auto_inc_interval_for_cur_row; ulonglong limit_found_rows; ha_rows cuted_fields, sent_row_count, examined_row_count; ulong client_capabilities; uint in_sub_stmt; - bool enable_slow_log, insert_id_used, clear_next_insert_id; + bool enable_slow_log; bool last_insert_id_used; my_bool no_send_ok; SAVEPOINT *savepoints; @@ -1071,24 +1073,136 @@ public: Note: in the parser, stmt_arena == thd, even for PS/SP. */ Query_arena *stmt_arena; + /* Tells if LAST_INSERT_ID(#) was called for the current statement */ + bool arg_of_last_insert_id_function; /* - next_insert_id is set on SET INSERT_ID= #. This is used as the next - generated auto_increment value in handler.cc + ALL OVER THIS FILE, "insert_id" means "*automatically generated* value for + insertion into an auto_increment column". */ - ulonglong next_insert_id; - /* Remember last next_insert_id to reset it if something went wrong */ - ulonglong prev_insert_id; /* - The insert_id used for the last statement or set by SET LAST_INSERT_ID=# - or SELECT LAST_INSERT_ID(#). Used for binary log and returned by - LAST_INSERT_ID() + This is the first autogenerated insert id which was *successfully* + inserted by the previous statement (exactly, if the previous statement + didn't successfully insert an autogenerated insert id, then it's the one + of the statement before, etc). + It can also be set by SET LAST_INSERT_ID=# or SELECT LAST_INSERT_ID(#). + It is returned by LAST_INSERT_ID(). */ - ulonglong last_insert_id; + ulonglong first_successful_insert_id_in_prev_stmt; /* - Set to the first value that LAST_INSERT_ID() returned for the last - statement. When this is set, last_insert_id_used is set to true. + Variant of the above, used for storing in statement-based binlog. The + difference is that the one above can change as the execution of a stored + function progresses, while the one below is set once and then does not + change (which is the value which statement-based binlog needs). */ - ulonglong current_insert_id; + ulonglong first_successful_insert_id_in_prev_stmt_for_binlog; + /* + This is the first autogenerated insert id which was *successfully* + inserted by the current statement. It is maintained only to set + first_successful_insert_id_in_prev_stmt when statement ends. + */ + ulonglong first_successful_insert_id_in_cur_stmt; + /* + We follow this logic: + - when stmt starts, first_successful_insert_id_in_prev_stmt contains the + first insert id successfully inserted by the previous stmt. + - as stmt makes progress, handler::insert_id_for_cur_row changes; every + time get_auto_increment() is called, auto_inc_intervals_for_binlog is + augmented with the reserved interval (if statement-based binlogging). + - at first successful insertion of an autogenerated value, + first_successful_insert_id_in_cur_stmt is set to + handler::insert_id_for_cur_row. + - when stmt goes to binlog, auto_inc_intervals_for_binlog is + binlogged if non-empty. + - when stmt ends, first_successful_insert_id_in_prev_stmt is set to + first_successful_insert_id_in_cur_stmt. + */ + /* + stmt_depends_on_first_successful_insert_id_in_prev_stmt is set when + LAST_INSERT_ID() is used by a statement. + If it is set, first_successful_insert_id_in_prev_stmt_for_binlog will be + stored in the statement-based binlog. + This variable is CUMULATIVE along the execution of a stored function or + trigger: if one substatement sets it to 1 it will stay 1 until the + function/trigger ends, thus making sure that + first_successful_insert_id_in_prev_stmt_for_binlog does not change anymore + and is propagated to the caller for binlogging. + */ + bool stmt_depends_on_first_successful_insert_id_in_prev_stmt; + /* + List of auto_increment intervals reserved by the thread so far, for + storage in the statement-based binlog. + Note that its minimum is not first_successful_insert_id_in_cur_stmt: + assuming a table with an autoinc column, and this happens: + INSERT INTO ... VALUES(3); + SET INSERT_ID=3; INSERT IGNORE ... VALUES (NULL); + then the latter INSERT will insert no rows + (first_successful_insert_id_in_cur_stmt == 0), but storing "INSERT_ID=3" + in the binlog is still needed; the list's minimum will contain 3. + */ + Discrete_intervals_list auto_inc_intervals_in_cur_stmt_for_binlog; + /* Used by replication and SET INSERT_ID */ + Discrete_intervals_list auto_inc_intervals_forced; + /* + There is BUG#19630 where statement-based replication of stored + functions/triggers with two auto_increment columns breaks. + We however ensure that it works when there is 0 or 1 auto_increment + column; our rules are + a) on master, while executing a top statement involving substatements, + first top- or sub- statement to generate auto_increment values wins the + exclusive right to write them to binlog (so the losers won't write their + values to binlog). + b) on slave, while replicating a top statement involving substatements, + first top- or sub- statement to need to read auto_increment values from + the master's binlog wins the exclusive right to read them (so the losers + won't read their values from binlog but instead generate on their own). + a) implies that we mustn't backup/restore + auto_inc_intervals_in_cur_stmt_for_binlog. + b) implies that we mustn't backup/restore auto_inc_intervals_forced. + + If there are more than 1 auto_increment columns, then intervals for + different columns may mix into the + auto_inc_intervals_in_cur_stmt_for_binlog list, which is logically wrong, + but there is no point in preventing this mixing by preventing intervals + from the secondly inserted column to come into the list, as such + prevention would be wrong too. + What will happen in the case of + INSERT INTO t1 (auto_inc) VALUES(NULL); + where t1 has a trigger which inserts into an auto_inc column of t2, is + that in binlog we'll store the interval of t1 and the interval of t2 (when + we store intervals, soon), then in slave, t1 will use both intervals, t2 + will use none; if t1 inserts the same number of rows as on master, + normally the 2nd interval will not be used by t1, which is fine. t2's + values will be wrong if t2's internal auto_increment counter is different + from what it was on master (which is likely). In 5.1, in mixed binlogging + mode, row-based binlogging is used for such cases where two + auto_increment columns are inserted. + */ + inline void record_first_successful_insert_id_in_cur_stmt(ulonglong id) + { + if (first_successful_insert_id_in_cur_stmt == 0) + first_successful_insert_id_in_cur_stmt= id; + } + inline ulonglong read_first_successful_insert_id_in_prev_stmt(void) + { + if (!stmt_depends_on_first_successful_insert_id_in_prev_stmt) + { + /* It's the first time we read it */ + first_successful_insert_id_in_prev_stmt_for_binlog= + first_successful_insert_id_in_prev_stmt; + stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1; + } + return first_successful_insert_id_in_prev_stmt; + } + /* + Used by Intvar_log_event::exec_event() and by "SET INSERT_ID=#" + (mysqlbinlog). We'll soon add a variant which can take many intervals in + argument. + */ + inline void force_one_auto_inc_interval(ulonglong next_id) + { + auto_inc_intervals_forced.append(next_id, ULONGLONG_MAX, 0); + } + ulonglong limit_found_rows; ulonglong options; /* Bitmap of states */ longlong row_count_func; /* For the ROW_COUNT() function */ @@ -1157,7 +1271,6 @@ public: bool last_cuted_field; bool no_errors, password, is_fatal_error; bool query_start_used, rand_used, time_zone_used; - bool last_insert_id_used,insert_id_used, clear_next_insert_id; bool in_lock_tables; bool query_error, bootstrap, cleanup_done; bool tmp_table_used; @@ -1185,9 +1298,10 @@ public: /* Used by the sys_var class to store temporary values */ union { - my_bool my_bool_value; - long long_value; - ulong ulong_value; + my_bool my_bool_value; + long long_value; + ulong ulong_value; + ulonglong ulonglong_value; } sys_var_tmp; struct { @@ -1288,20 +1402,6 @@ public: inline void end_time() { time(&start_time); } inline void set_time(time_t t) { time_after_lock=start_time=user_time=t; } inline void lock_time() { time(&time_after_lock); } - inline void insert_id(ulonglong id_arg) - { - last_insert_id= id_arg; - insert_id_used=1; - } - inline ulonglong insert_id(void) - { - if (!last_insert_id_used) - { - last_insert_id_used=1; - current_insert_id=last_insert_id; - } - return last_insert_id; - } inline ulonglong found_rows(void) { return limit_found_rows; @@ -1589,7 +1689,7 @@ class select_insert :public select_result_interceptor { TABLE_LIST *table_list; TABLE *table; List *fields; - ulonglong last_insert_id; + ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not COPY_INFO info; bool insert_into_view; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 7b017ad7317..c279148ab5a 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -411,7 +411,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, table->next_number_field=table->found_next_number_field; error=0; - id=0; thd->proc_info="update"; if (duplic != DUP_ERROR || ignore) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); @@ -517,16 +516,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, else #endif error=write_record(thd, table ,&info); - /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. - */ - if (! id && thd->insert_id_used) - { // Get auto increment value - id= thd->last_insert_id; - } if (error) break; thd->row_count++; @@ -534,6 +523,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, free_underlaid_joins(thd, &thd->lex->select_lex); joins_freed= TRUE; + table->file->ha_release_auto_increment(); /* Now all rows are inserted. Time to update logs and sends response to @@ -544,7 +534,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, { if (!error) { - id=0; // No auto_increment id info.copied=values_list.elements; end_delayed_insert(thd); } @@ -558,11 +547,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, table->file->print_error(my_errno,MYF(0)); error=1; } - if (id && values_list.elements != 1) - thd->insert_id(id); // For update log - else if (table->next_number_field && info.copied) - id=table->next_number_field->val_int(); // Return auto_increment value - transactional_table= table->file->has_transactions(); if ((changed= (info.copied || info.deleted || info.updated))) @@ -611,18 +595,27 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } } thd->proc_info="end"; + /* + We'll report to the client this id: + - if the table contains an autoincrement column and we successfully + inserted an autogenerated value, the autogenerated value. + - if the table contains no autoincrement column and LAST_INSERT_ID(X) was + called, X. + - if the table contains an autoincrement column, and some rows were + inserted, the id of the last "inserted" row (if IGNORE, that value may not + have been really inserted but ignored). + */ + id= (thd->first_successful_insert_id_in_cur_stmt > 0) ? + thd->first_successful_insert_id_in_cur_stmt : + (thd->arg_of_last_insert_id_function ? + thd->first_successful_insert_id_in_prev_stmt : + ((table->next_number_field && info.copied) ? + table->next_number_field->val_int() : 0)); table->next_number_field=0; thd->count_cuted_fields= CHECK_FIELD_IGNORE; - thd->next_insert_id=0; // Reset this if wrongly used if (duplic != DUP_ERROR || ignore) table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - /* Reset value of LAST_INSERT_ID if no rows where inserted */ - if (!info.copied && thd->insert_id_used) - { - thd->insert_id(0); - id=0; - } if (error) goto abort; if (values_list.elements == 1 && (!(thd->options & OPTION_WARNINGS) || @@ -644,8 +637,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->row_count_func= info.copied+info.deleted+info.updated; ::send_ok(thd, (ulong) thd->row_count_func, id, buff); } - if (table != NULL) - table->file->release_auto_increment(); thd->abort_on_warning= 0; DBUG_RETURN(FALSE); @@ -655,7 +646,7 @@ abort: end_delayed_insert(thd); #endif if (table != NULL) - table->file->release_auto_increment(); + table->file->ha_release_auto_increment(); if (!joins_freed) free_underlaid_joins(thd, &thd->lex->select_lex); thd->abort_on_warning= 0; @@ -964,6 +955,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) int error, trg_error= 0; char *key=0; MY_BITMAP *save_read_set, *save_write_set; + ulonglong prev_insert_id= table->file->next_insert_id; + ulonglong insert_id_for_cur_row= 0; DBUG_ENTER("write_record"); info->records++; @@ -976,9 +969,19 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) while ((error=table->file->ha_write_row(table->record[0]))) { uint key_nr; + /* + If we do more than one iteration of this loop, from the second one the + row will have an explicit value in the autoinc field, which was set at + the first call of handler::update_auto_increment(). So we must save + the autogenerated value to avoid thd->insert_id_for_cur_row to become + 0. + */ + if (table->file->insert_id_for_cur_row > 0) + insert_id_for_cur_row= table->file->insert_id_for_cur_row; + else + table->file->insert_id_for_cur_row= insert_id_for_cur_row; if (error != HA_WRITE_SKIP) goto err; - table->file->restore_auto_increment(); // it's too early here! BUG#20188 if ((int) (key_nr = table->file->get_dup_key(error)) < 0) { error=HA_WRITE_SKIP; /* Database can't find key */ @@ -994,7 +997,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (info->handle_duplicates == DUP_REPLACE && table->next_number_field && key_nr == table->s->next_number_index && - table->file->auto_increment_column_changed) + (insert_id_for_cur_row > 0)) goto err; if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { @@ -1053,12 +1056,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (res == VIEW_CHECK_ERROR) goto before_trg_err; - if (thd->clear_next_insert_id) - { - /* Reset auto-increment cacheing if we do an update */ - thd->clear_next_insert_id= 0; - thd->next_insert_id= 0; - } if ((error=table->file->ha_update_row(table->record[1], table->record[0]))) { @@ -1067,7 +1064,14 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) goto err; } info->updated++; - + /* + If ON DUP KEY UPDATE updates a row instead of inserting one, and + there is an auto_increment column, then SELECT LAST_INSERT_ID() + returns the id of the updated row: + */ + if (table->next_number_field) + thd->record_first_successful_insert_id_in_cur_stmt(table->next_number_field->val_int()); + trg_error= (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE)); @@ -1096,16 +1100,11 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH) && (!table->triggers || !table->triggers->has_delete_triggers())) { - if (thd->clear_next_insert_id) - { - /* Reset auto-increment cacheing if we do an update */ - thd->clear_next_insert_id= 0; - thd->next_insert_id= 0; - } if ((error=table->file->ha_update_row(table->record[1], table->record[0]))) goto err; info->deleted++; + thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row); /* Since we pretend that we have done insert we should call its after triggers. @@ -1134,6 +1133,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } } } + thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row); /* Restore column maps if they where replaced during an duplicate key problem. @@ -1147,12 +1147,13 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (!info->ignore || (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE)) goto err; - table->file->restore_auto_increment(); + table->file->restore_auto_increment(prev_insert_id); goto ok_or_after_trg_err; } after_trg_n_copied_inc: info->copied++; + thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row); trg_error= (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE)); @@ -1172,6 +1173,7 @@ err: table->file->print_error(error,MYF(0)); before_trg_err: + table->file->restore_auto_increment(prev_insert_id); if (key) my_safe_afree(key, table->s->max_unique_length, MAX_KEY_LENGTH); table->column_bitmaps_set(save_read_set, save_write_set); @@ -1234,8 +1236,9 @@ public: char *record; enum_duplicates dup; time_t start_time; - bool query_start_used,last_insert_id_used,insert_id_used, ignore, log_query; - ulonglong last_insert_id; + bool query_start_used, ignore, log_query; + bool stmt_depends_on_first_successful_insert_id_in_prev_stmt; + ulonglong first_successful_insert_id_in_prev_stmt; timestamp_auto_set_type timestamp_field_type; delayed_row(enum_duplicates dup_arg, bool ignore_arg, bool log_query_arg) @@ -1639,9 +1642,16 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, di->set_query(query, query_length); row->start_time= thd->start_time; row->query_start_used= thd->query_start_used; - row->last_insert_id_used= thd->last_insert_id_used; - row->insert_id_used= thd->insert_id_used; - row->last_insert_id= thd->last_insert_id; + /* + those are for the binlog: LAST_INSERT_ID() has been evaluated at this + time, so record does not need it, but statement-based binlogging of the + INSERT will need when the row is actually inserted. + As for SET INSERT_ID, DELAYED does not honour it (BUG#20830). + */ + row->stmt_depends_on_first_successful_insert_id_in_prev_stmt= + thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt; + row->first_successful_insert_id_in_prev_stmt= + thd->first_successful_insert_id_in_prev_stmt; row->timestamp_field_type= table->timestamp_field_type; di->rows.push_back(row); @@ -1895,6 +1905,7 @@ pthread_handler_t handle_delayed_insert(void *arg) MYSQL_LOCK *lock=thd->lock; thd->lock=0; pthread_mutex_unlock(&di->mutex); + di->table->file->ha_release_auto_increment(); mysql_unlock_tables(thd, lock); di->group_count=0; pthread_mutex_lock(&di->mutex); @@ -2007,13 +2018,6 @@ bool delayed_insert::handle_inserts(void) table->file->extra(HA_EXTRA_WRITE_CACHE); pthread_mutex_lock(&mutex); - /* Reset auto-increment cacheing */ - if (thd.clear_next_insert_id) - { - thd.next_insert_id= 0; - thd.clear_next_insert_id= 0; - } - while ((row=rows.get())) { stacked_inserts--; @@ -2022,9 +2026,12 @@ bool delayed_insert::handle_inserts(void) thd.start_time=row->start_time; thd.query_start_used=row->query_start_used; - thd.last_insert_id=row->last_insert_id; - thd.last_insert_id_used=row->last_insert_id_used; - thd.insert_id_used=row->insert_id_used; + /* for the binlog, forget auto_increment ids generated by previous rows */ + thd.auto_inc_intervals_in_cur_stmt_for_binlog.empty(); + thd.first_successful_insert_id_in_prev_stmt= + row->first_successful_insert_id_in_prev_stmt; + thd.stmt_depends_on_first_successful_insert_id_in_prev_stmt= + row->stmt_depends_on_first_successful_insert_id_in_prev_stmt; table->timestamp_field_type= row->timestamp_field_type; info.ignore= row->ignore; @@ -2187,7 +2194,7 @@ select_insert::select_insert(TABLE_LIST *table_list_par, TABLE *table_par, enum_duplicates duplic, bool ignore_check_option_errors) :table_list(table_list_par), table(table_par), fields(fields_par), - last_insert_id(0), + autoinc_value_of_last_inserted_row(0), insert_into_view(table_list_par && table_list_par->view != 0) { bzero((char*) &info,sizeof(info)); @@ -2395,16 +2402,21 @@ bool select_insert::send_data(List &values) } if (table->next_number_field) { + /* + If no value has been autogenerated so far, we need to remember the + value we just saw, we may need to send it to client in the end. + */ + if (thd->first_successful_insert_id_in_cur_stmt == 0) // optimization + autoinc_value_of_last_inserted_row= + table->next_number_field->val_int(); /* Clear auto-increment field for the next record, if triggers are used we will clear it twice, but this should be cheap. */ table->next_number_field->reset(); - if (!last_insert_id && thd->insert_id_used) - last_insert_id= thd->insert_id(); } } - table->file->release_auto_increment(); + table->file->ha_release_auto_increment(); DBUG_RETURN(error); } @@ -2466,8 +2478,6 @@ void select_insert::send_error(uint errcode,const char *err) { if (!table->file->has_transactions()) { - if (last_insert_id) - thd->insert_id(last_insert_id); // For binary log if (mysql_bin_log.is_open()) { thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, @@ -2487,6 +2497,7 @@ void select_insert::send_error(uint errcode,const char *err) bool select_insert::send_eof() { int error,error2; + ulonglong id; DBUG_ENTER("select_insert::send_eof"); error= (!thd->prelocked_mode) ? table->file->ha_end_bulk_insert():0; @@ -2512,8 +2523,6 @@ bool select_insert::send_eof() thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; } - if (last_insert_id) - thd->insert_id(last_insert_id); // For binary log /* Write to binlog before commiting transaction. No statement will be written by the binlog_query() below in RBR mode. All the @@ -2543,7 +2552,13 @@ bool select_insert::send_eof() sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, (ulong) (info.deleted+info.updated), (ulong) thd->cuted_fields); thd->row_count_func= info.copied+info.deleted+info.updated; - ::send_ok(thd, (ulong) thd->row_count_func, last_insert_id, buff); + + id= (thd->first_successful_insert_id_in_cur_stmt > 0) ? + thd->first_successful_insert_id_in_cur_stmt : + (thd->arg_of_last_insert_id_function ? + thd->first_successful_insert_id_in_prev_stmt : + (info.copied ? autoinc_value_of_last_inserted_row : 0)); + ::send_ok(thd, (ulong) thd->row_count_func, id, buff); DBUG_RETURN(0); } diff --git a/sql/sql_load.cc b/sql/sql_load.cc index f8debbedc62..215ea303f0a 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -497,13 +497,12 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, error=ha_autocommit_or_rollback(thd,error); err: + table->file->ha_release_auto_increment(); if (thd->lock) { mysql_unlock_tables(thd, thd->lock); thd->lock=0; } - if (table != NULL) - table->file->release_auto_increment(); thd->abort_on_warning= 0; DBUG_RETURN(error); } @@ -638,14 +637,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, DBUG_RETURN(1); thd->no_trans_update= no_trans_update; - /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the binary/update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. - */ - if (!id && thd->insert_id_used) - id= thd->last_insert_id; /* We don't need to reset auto-increment field since we are restoring its default value at the beginning of each loop iteration. @@ -662,8 +653,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, thd->row_count++; continue_loop:; } - if (id && !read_info.error) - thd->insert_id(id); // For binary/update log DBUG_RETURN(test(read_info.error)); } @@ -806,14 +795,6 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (write_record(thd, table, &info)) DBUG_RETURN(1); - /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the binary/update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. - */ - if (!id && thd->insert_id_used) - id= thd->last_insert_id; /* We don't need to reset auto-increment field since we are restoring its default value at the beginning of each loop iteration. @@ -833,8 +814,6 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, thd->row_count++; continue_loop:; } - if (id && !read_info.error) - thd->insert_id(id); // For binary/update log DBUG_RETURN(test(read_info.error)); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 232df095816..5d108000286 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3348,8 +3348,9 @@ end_with_restore_list: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, lex->update_list, lex->value_list, lex->duplicates, lex->ignore); + /* do not show last insert ID if VIEW does not have auto_inc */ if (first_table->view && !first_table->contain_auto_increment) - thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it + thd->first_successful_insert_id_in_cur_stmt= 0; break; } case SQLCOM_REPLACE_SELECT: @@ -3401,9 +3402,9 @@ end_with_restore_list: /* revert changes for SP */ select_lex->table_list.first= (byte*) first_table; } - + /* do not show last insert ID if VIEW does not have auto_inc */ if (first_table->view && !first_table->contain_auto_increment) - thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it + thd->first_successful_insert_id_in_cur_stmt= 0; break; } case SQLCOM_TRUNCATE: @@ -5807,6 +5808,7 @@ mysql_init_query(THD *thd, uchar *buf, uint length) DESCRIPTION This needs to be called before execution of every statement (prepared or conventional). + It is not called by substatements of routines. TODO Make it a method of THD and align its name with the rest of @@ -5817,9 +5819,12 @@ mysql_init_query(THD *thd, uchar *buf, uint length) void mysql_reset_thd_for_next_command(THD *thd) { DBUG_ENTER("mysql_reset_thd_for_next_command"); + DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */ thd->free_list= 0; thd->select_number= 1; - thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; + thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty(); + thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= + thd->query_start_used= 0; thd->is_fatal_error= thd->time_zone_used= 0; thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | SERVER_QUERY_NO_INDEX_USED | diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 127409a01bf..57969479448 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7895,7 +7895,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) Field *field=((Item_field*) args[0])->field; if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null && (thd->options & OPTION_AUTO_IS_NULL) && - thd->insert_id()) + (thd->first_successful_insert_id_in_prev_stmt > 0)) { #ifdef HAVE_QUERY_CACHE query_cache_abort(&thd->net); @@ -7903,7 +7903,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) COND *new_cond; if ((new_cond= new Item_func_eq(args[0], new Item_int("last_insert_id()", - thd->insert_id(), + thd->read_first_successful_insert_id_in_prev_stmt(), 21)))) { cond=new_cond; @@ -7914,7 +7914,11 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) */ cond->fix_fields(thd, &cond); } - thd->insert_id(0); // Clear for next request + /* + IS NULL should be mapped to LAST_INSERT_ID only for first row, so + clear for next row + */ + thd->first_successful_insert_id_in_prev_stmt= 0; } /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */ else if (((field->type() == FIELD_TYPE_DATE) || diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 7d8631e3236..4a33231cc19 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4953,7 +4953,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, char path[FN_REFLEN]; char reg_path[FN_REFLEN+1]; ha_rows copied,deleted; - ulonglong next_insert_id; uint db_create_options, used_fields; handlerton *old_db_type, *new_db_type; HA_CREATE_INFO *create_info; @@ -5773,7 +5772,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields thd->cuted_fields=0L; thd->proc_info="copy to tmp table"; - next_insert_id=thd->next_insert_id; // Remember for logging copied=deleted=0; if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)) { @@ -5784,7 +5782,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, handle_duplicates, ignore, order_num, order, &copied, &deleted); } - thd->last_insert_id=next_insert_id; // Needed for correct log thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* If we did not need to copy, we might still need to add/drop indexes. */ @@ -6214,6 +6211,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, ha_rows examined_rows; bool auto_increment_field_copied= 0; ulong save_sql_mode; + ulonglong prev_insert_id; DBUG_ENTER("copy_data_between_tables"); /* @@ -6320,6 +6318,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, { copy_ptr->do_copy(copy_ptr); } + prev_insert_id= to->file->next_insert_id; if ((error=to->file->ha_write_row((byte*) to->record[0]))) { if ((!ignore && @@ -6345,7 +6344,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, to->file->print_error(error,MYF(0)); break; } - to->file->restore_auto_increment(); + to->file->restore_auto_increment(prev_insert_id); delete_count++; } else @@ -6379,6 +6378,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, free_io_cache(from); *copied= found_count; *deleted=delete_count; + to->file->ha_release_auto_increment(); if (to->file->ha_external_lock(thd,F_UNLCK)) error=1; DBUG_RETURN(error > 0 ? -1 : 0); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index da529cc0070..495c71c42f9 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -135,7 +135,8 @@ int mysql_update(THD *thd, SQL_SELECT *select; READ_RECORD info; SELECT_LEX *select_lex= &thd->lex->select_lex; - bool need_reopen; + bool need_reopen; + ulonglong id; DBUG_ENTER("mysql_update"); for ( ; ; ) @@ -675,6 +676,10 @@ int mysql_update(THD *thd, thd->lock=0; } + /* If LAST_INSERT_ID(X) was used, report X */ + id= thd->arg_of_last_insert_id_function ? + thd->first_successful_insert_id_in_prev_stmt : 0; + if (error < 0) { char buff[STRING_BUFFER_USUAL_SIZE]; @@ -682,8 +687,7 @@ int mysql_update(THD *thd, (ulong) thd->cuted_fields); thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; - send_ok(thd, (ulong) thd->row_count_func, - thd->insert_id_used ? thd->insert_id() : 0L,buff); + send_ok(thd, (ulong) thd->row_count_func, id, buff); DBUG_PRINT("info",("%d records updated",updated)); } thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */ @@ -1632,6 +1636,7 @@ err2: bool multi_update::send_eof() { char buff[STRING_BUFFER_USUAL_SIZE]; + ulonglong id; thd->proc_info="updating reference tables"; /* Does updates for the last n - 1 tables, returns 0 if ok */ @@ -1684,12 +1689,12 @@ bool multi_update::send_eof() return TRUE; } - + id= thd->arg_of_last_insert_id_function ? + thd->first_successful_insert_id_in_prev_stmt : 0; sprintf(buff, ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated, (ulong) thd->cuted_fields); thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; - ::send_ok(thd, (ulong) thd->row_count_func, - thd->insert_id_used ? thd->insert_id() : 0L,buff); + ::send_ok(thd, (ulong) thd->row_count_func, id, buff); return FALSE; } diff --git a/sql/structs.h b/sql/structs.h index 38bb441fc03..83ae6cac032 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -250,3 +250,99 @@ typedef struct user_conn { #define STATUS_UPDATED 16 /* Record is updated by formula */ #define STATUS_NULL_ROW 32 /* table->null_row is set */ #define STATUS_DELETED 64 + +/* + Such interval is "discrete": it is the set of + { auto_inc_interval_min + k * increment, + 0 <= k <= (auto_inc_interval_values-1) } + Where "increment" is maintained separately by the user of this class (and is + currently only thd->variables.auto_increment_increment). + It mustn't derive from Sql_alloc, because SET INSERT_ID needs to + allocate memory which must stay allocated for use by the next statement. +*/ +class Discrete_interval { +private: + ulonglong interval_min; + ulonglong interval_values; + ulonglong interval_max; // excluded bound. Redundant. +public: + Discrete_interval *next; // used when linked into Discrete_intervals_list + void replace(ulonglong start, ulonglong val, ulonglong incr) + { + interval_min= start; + interval_values= val; + interval_max= (val == ULONGLONG_MAX) ? val : start + val * incr; + } + Discrete_interval(ulonglong start, ulonglong val, ulonglong incr) : + next(NULL) { replace(start, val, incr); }; + Discrete_interval() : next(NULL) { replace(0, 0, 0); }; + ulonglong minimum() const { return interval_min; }; + ulonglong values() const { return interval_values; }; + ulonglong maximum() const { return interval_max; }; + /* + If appending [3,5] to [1,2], we merge both in [1,5] (they should have the + same increment for that, user of the class has to ensure that). That is + just a space optimization. Returns 0 if merge succeeded. + */ + bool merge_if_contiguous(ulonglong start, ulonglong val, ulonglong incr) + { + if (interval_max == start) + { + if (val == ULONGLONG_MAX) + { + interval_values= interval_max= val; + } + else + { + interval_values+= val; + interval_max= start + val * incr; + } + return 0; + } + return 1; + }; +}; + +/* List of Discrete_interval objects */ +class Discrete_intervals_list { +private: + Discrete_interval *head; + Discrete_interval *tail; + /* + When many intervals are provided at the beginning of the execution of a + statement (in a replication slave or SET INSERT_ID), "current" points to + the interval being consumed by the thread now (so "current" goes from + "head" to "tail" then to NULL). + */ + Discrete_interval *current; + uint elements; // number of elements +public: + Discrete_intervals_list() : head(NULL), current(NULL), elements(0) {}; + void empty_no_free() + { + head= current= NULL; + elements= 0; + } + void empty() + { + for (Discrete_interval *i= head; i;) + { + Discrete_interval *next= i->next; + delete i; + i= next; + } + empty_no_free(); + } + const Discrete_interval* get_next() + { + Discrete_interval *tmp= current; + if (current != NULL) + current= current->next; + return tmp; + } + ~Discrete_intervals_list() { empty(); }; + bool append(ulonglong start, ulonglong val, ulonglong incr); + ulonglong minimum() const { return (head ? head->minimum() : 0); }; + ulonglong maximum() const { return (head ? tail->maximum() : 0); }; + uint nb_elements() const { return elements; } +}; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index e3159cfa5e5..3173b896075 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -14947,7 +14947,193 @@ static void test_bug14169() rc= mysql_query(mysql, "drop table t1"); myquery(rc); -}/* +} + + +/* + Test that mysql_insert_id() behaves as documented in our manual +*/ +static void test_mysql_insert_id() +{ + my_ulonglong res; + int rc; + + myheader("test_mysql_insert_id"); + + rc= mysql_query(mysql, "drop table if exists t1"); + myquery(rc); + /* table without auto_increment column */ + rc= mysql_query(mysql, "create table t1 (f1 int, f2 varchar(255), key(f1))"); + myquery(rc); + rc= mysql_query(mysql, "insert into t1 values (1,'a')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + rc= mysql_query(mysql, "insert into t1 values (null,'b')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + rc= mysql_query(mysql, "insert into t1 select 5,'c'"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + rc= mysql_query(mysql, "insert into t1 select null,'d'"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + rc= mysql_query(mysql, "insert into t1 values (null,last_insert_id(300))"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 300); + rc= mysql_query(mysql, "insert into t1 select null,last_insert_id(400)"); + myquery(rc); + res= mysql_insert_id(mysql); + /* + Behaviour change: old code used to return 0; but 400 is consistent + with INSERT VALUES, and the manual's section of mysql_insert_id() does not + say INSERT SELECT should be different. + */ + DIE_UNLESS(res == 400); + + /* table with auto_increment column */ + rc= mysql_query(mysql, "create table t2 (f1 int not null primary key auto_increment, f2 varchar(255))"); + myquery(rc); + rc= mysql_query(mysql, "insert into t2 values (1,'a')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 1); + /* this should not influence next INSERT if it doesn't have auto_inc */ + rc= mysql_query(mysql, "insert into t1 values (10,'e')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + + rc= mysql_query(mysql, "insert into t2 values (null,'b')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 2); + rc= mysql_query(mysql, "insert into t2 select 5,'c'"); + myquery(rc); + res= mysql_insert_id(mysql); + /* + Manual says that for multirow insert this should have been 5, but does not + say for INSERT SELECT. This is a behaviour change: old code used to return + 0. We try to be consistent with INSERT VALUES. + */ + DIE_UNLESS(res == 5); + rc= mysql_query(mysql, "insert into t2 select null,'d'"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 6); + /* with more than one row */ + rc= mysql_query(mysql, "insert into t2 values (10,'a'),(11,'b')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 11); + rc= mysql_query(mysql, "insert into t2 select 12,'a' union select 13,'b'"); + myquery(rc); + res= mysql_insert_id(mysql); + /* + Manual says that for multirow insert this should have been 13, but does + not say for INSERT SELECT. This is a behaviour change: old code used to + return 0. We try to be consistent with INSERT VALUES. + */ + DIE_UNLESS(res == 13); + rc= mysql_query(mysql, "insert into t2 values (null,'a'),(null,'b')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 14); + rc= mysql_query(mysql, "insert into t2 select null,'a' union select null,'b'"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 16); + rc= mysql_query(mysql, "insert into t2 select 12,'a' union select 13,'b'"); + myquery_r(rc); + rc= mysql_query(mysql, "insert ignore into t2 select 12,'a' union select 13,'b'"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + rc= mysql_query(mysql, "insert into t2 values (12,'a'),(13,'b')"); + myquery_r(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + rc= mysql_query(mysql, "insert ignore into t2 values (12,'a'),(13,'b')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + /* mixing autogenerated and explicit values */ + rc= mysql_query(mysql, "insert into t2 values (null,'e'),(12,'a'),(13,'b')"); + myquery_r(rc); + rc= mysql_query(mysql, "insert into t2 values (null,'e'),(12,'a'),(13,'b'),(25,'g')"); + myquery_r(rc); + rc= mysql_query(mysql, "insert into t2 values (null,last_insert_id(300))"); + myquery(rc); + res= mysql_insert_id(mysql); + /* + according to the manual, this might be 20 or 300, but it looks like + auto_increment column takes priority over last_insert_id(). + */ + DIE_UNLESS(res == 20); + /* If first autogenerated number fails and 2nd works: */ + rc= mysql_query(mysql, "drop table t2"); + myquery(rc); + rc= mysql_query(mysql, "create table t2 (f1 int not null primary key " + "auto_increment, f2 varchar(255), unique (f2))"); + myquery(rc); + rc= mysql_query(mysql, "insert into t2 values (null,'e')"); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 1); + rc= mysql_query(mysql, "insert ignore into t2 values (null,'e'),(null,'a'),(null,'e')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 2); + /* If autogenerated fails and explicit works: */ + rc= mysql_query(mysql, "insert ignore into t2 values (null,'e'),(12,'c'),(null,'d')"); + myquery(rc); + res= mysql_insert_id(mysql); + /* + Behaviour change: old code returned 3 (first autogenerated, even if it + fails); we now return first successful autogenerated. + */ + DIE_UNLESS(res == 13); + /* UPDATE may update mysql_insert_id() if it uses LAST_INSERT_ID(#) */ + rc= mysql_query(mysql, "update t2 set f1=14 where f1=12"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + rc= mysql_query(mysql, "update t2 set f1=NULL where f1=14"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + rc= mysql_query(mysql, "update t2 set f2=last_insert_id(372) where f1=0"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 372); + /* check that LAST_INSERT_ID() does not update mysql_insert_id(): */ + rc= mysql_query(mysql, "insert into t2 values (null,'g')"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 15); + rc= mysql_query(mysql, "update t2 set f2=(@li:=last_insert_id()) where f1=15"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 0); + /* + Behaviour change: now if ON DUPLICATE KEY UPDATE updates a row, + mysql_insert_id() returns the id of the row, instead of not being + affected. + */ + rc= mysql_query(mysql, "insert into t2 values (null,@li) on duplicate key " + "update f2=concat('we updated ',f2)"); + myquery(rc); + res= mysql_insert_id(mysql); + DIE_UNLESS(res == 15); + + rc= mysql_query(mysql, "drop table t1,t2"); + myquery(rc); +} + +/* Read and parse arguments and MySQL options from my.cnf */ @@ -15214,6 +15400,7 @@ static struct my_tests_st my_tests[]= { { "test_bug15613", test_bug15613 }, { "test_bug14169", test_bug14169 }, { "test_bug17667", test_bug17667 }, + { "test_mysql_insert_id", test_mysql_insert_id }, { 0, 0 } }; From 87fb4fb4a8703134f109cca32da7b1521a50e2d4 Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Sun, 9 Jul 2006 18:45:16 +0200 Subject: [PATCH 25/32] Manual merge of test from 5.0 (needs to be manual because the test files were copied/split between 5.0 and 5.1). --- .../extra/rpl_tests/rpl_auto_increment.test | 40 +++++++++++- mysql-test/extra/rpl_tests/rpl_insert_id.test | 63 +++++++++++++++++++ mysql-test/r/rpl_auto_increment.result | 6 +- 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_auto_increment.test b/mysql-test/extra/rpl_tests/rpl_auto_increment.test index 42f8fcfc5fb..dbae317e8ab 100644 --- a/mysql-test/extra/rpl_tests/rpl_auto_increment.test +++ b/mysql-test/extra/rpl_tests/rpl_auto_increment.test @@ -104,9 +104,47 @@ select * from t1; sync_slave_with_master; select * from t1; + +# Test for BUG#20524 "auto_increment_* not observed when inserting +# a too large value". When an autogenerated value was bigger than the +# maximum possible value of the field, it was truncated to that max +# possible value, without being "rounded down" to still honour +# auto_increment_* variables. + +connection master; +drop table t1; +create table t1 (a tinyint not null auto_increment primary key) engine=myisam; +insert into t1 values(103); +set auto_increment_increment=11; +set auto_increment_offset=4; +insert into t1 values(null); +insert into t1 values(null); +--error 1062 +insert into t1 values(null); +select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t1 order by a; + +# same but with a larger value +create table t2 (a tinyint unsigned not null auto_increment primary key) engine=myisam; +set auto_increment_increment=10; +set auto_increment_offset=1; +set insert_id=1000; +insert into t2 values(null); +select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t2 order by a; + +# An offset so big that even first value does not fit +create table t3 like t1; +set auto_increment_increment=1000; +set auto_increment_offset=700; +insert into t3 values(null); +select * from t3 order by a; +sync_slave_with_master; +select * from t1 order by a; +select * from t2 order by a; +select * from t3 order by a; + connection master; -drop table t1; +drop table t1,t2,t3; # End cleanup sync_slave_with_master; diff --git a/mysql-test/extra/rpl_tests/rpl_insert_id.test b/mysql-test/extra/rpl_tests/rpl_insert_id.test index 68e39c54381..03e8f00caae 100644 --- a/mysql-test/extra/rpl_tests/rpl_insert_id.test +++ b/mysql-test/extra/rpl_tests/rpl_insert_id.test @@ -155,6 +155,69 @@ drop function bug15728; drop function bug15728_insert; drop table t1, t2; +# test of BUG#20188 REPLACE or ON DUPLICATE KEY UPDATE in +# auto_increment breaks binlog + +create table t1 (n int primary key auto_increment not null, +b int, unique(b)); + +# First, test that we do not call restore_auto_increment() too early +# in write_record(): +set sql_log_bin=0; +insert into t1 values(null,100); +replace into t1 values(null,50),(null,100),(null,150); +select * from t1 order by n; +truncate table t1; +set sql_log_bin=1; + +insert into t1 values(null,100); +select * from t1 order by n; +sync_slave_with_master; +# make slave's table autoinc counter bigger +insert into t1 values(null,200),(null,300); +delete from t1 where b <> 100; +# check that slave's table content is identical to master +select * from t1 order by n; +# only the auto_inc counter differs. + +connection master; +replace into t1 values(null,100),(null,350); +select * from t1 order by n; +sync_slave_with_master; +select * from t1 order by n; + +# Same test as for REPLACE, but for ON DUPLICATE KEY UPDATE + +# We first check that if we update a row using a value larger than the +# table's counter, the counter for next row is bigger than the +# after-value of the updated row. +connection master; +insert into t1 values (NULL,400),(3,500),(NULL,600) on duplicate key UPDATE n=1000; +select * from t1 order by n; +sync_slave_with_master; +select * from t1 order by n; + +# and now test for the bug: +connection master; +drop table t1; +create table t1 (n int primary key auto_increment not null, +b int, unique(b)); +insert into t1 values(null,100); +select * from t1 order by n; +sync_slave_with_master; +insert into t1 values(null,200),(null,300); +delete from t1 where b <> 100; +select * from t1 order by n; + +connection master; +insert into t1 values(null,100),(null,350) on duplicate key update n=2; +select * from t1 order by n; +sync_slave_with_master; +select * from t1 order by n; + +connection master; +drop table t1; + # End of 5.0 tests sync_slave_with_master; diff --git a/mysql-test/r/rpl_auto_increment.result b/mysql-test/r/rpl_auto_increment.result index a1f56440ec8..083f3a4e901 100644 --- a/mysql-test/r/rpl_auto_increment.result +++ b/mysql-test/r/rpl_auto_increment.result @@ -190,7 +190,7 @@ set auto_increment_offset=4; insert into t1 values(null); insert into t1 values(null); insert into t1 values(null); -ERROR 23000: Duplicate entry '125' for key 1 +ERROR 23000: Duplicate entry '125' for key 'PRIMARY' select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t1 order by a; a mod(a-@@auto_increment_offset,@@auto_increment_increment) 103 0 @@ -202,7 +202,7 @@ set auto_increment_offset=1; set insert_id=1000; insert into t2 values(null); Warnings: -Warning 1264 Out of range value adjusted for column 'a' at row 1 +Warning 1264 Out of range value for column 'a' at row 1 select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t2 order by a; a mod(a-@@auto_increment_offset,@@auto_increment_increment) 251 0 @@ -211,7 +211,7 @@ set auto_increment_increment=1000; set auto_increment_offset=700; insert into t3 values(null); Warnings: -Warning 1264 Out of range value adjusted for column 'a' at row 1 +Warning 1264 Out of range value for column 'a' at row 1 select * from t3 order by a; a 127 From f6c996f0a25cee913130cad69d40c1b7eb2cccc0 Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Sun, 9 Jul 2006 19:47:54 +0200 Subject: [PATCH 26/32] post-merge fixes --- mysql-test/r/rpl_switch_stm_row_mixed.result | 2 ++ sql/sql_insert.cc | 31 ++++++++------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/mysql-test/r/rpl_switch_stm_row_mixed.result b/mysql-test/r/rpl_switch_stm_row_mixed.result index 93fc7f450d0..359b6679219 100644 --- a/mysql-test/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/r/rpl_switch_stm_row_mixed.result @@ -286,6 +286,8 @@ master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `mysqltest1`; create table t12 (a int, b float) master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t12) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 9d8e648eb25..6183157377c 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2066,9 +2066,19 @@ bool delayed_insert::handle_inserts(void) } if (row->log_query && row->query.str != NULL && mysql_bin_log.is_open()) + { + /* + If the query has several rows to insert, only the first row will come + here. In row-based binlogging, this means that the first row will be + written to binlog as one Table_map event and one Rows event (due to an + event flush done in binlog_query()), then all other rows of this query + will be binlogged together as one single Table_map event and one + single Rows event. + */ thd.binlog_query(THD::ROW_QUERY_TYPE, row->query.str, row->query.length, FALSE, FALSE); + } if (table->s->blob_fields) free_delayed_insert_blobs(table); @@ -2744,22 +2754,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, } -class MY_HOOKS : public TABLEOP_HOOKS -{ -public: - MY_HOOKS(select_create *x) : ptr(x) { } - virtual void do_prelock(TABLE **tables, uint count) - { - if (ptr->get_thd()->current_stmt_binlog_row_based && - !(ptr->get_create_info()->options & HA_LEX_CREATE_TMP_TABLE)) - ptr->binlog_show_create_table(tables, count); - } - -private: - select_create *ptr; -}; - - int select_create::prepare(List &values, SELECT_LEX_UNIT *u) { @@ -2772,8 +2766,9 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) MY_HOOKS(select_create *x) : ptr(x) { } virtual void do_prelock(TABLE **tables, uint count) { - if (ptr->get_thd()->current_stmt_binlog_row_based) - ptr->binlog_show_create_table(tables, count); + if (ptr->get_thd()->current_stmt_binlog_row_based && + !(ptr->get_create_info()->options & HA_LEX_CREATE_TMP_TABLE)) + ptr->binlog_show_create_table(tables, count); } private: From b3977379892ca210eec3d72e846d62857a70f0d9 Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Sun, 9 Jul 2006 21:11:52 +0200 Subject: [PATCH 27/32] fixes in test results (using # for positions in SHOW BINLOG EVENTS). --- .../mix_innodb_myisam_binlog.test | 27 +- .../r/binlog_row_mix_innodb_myisam.result | 304 ++++++++---------- 2 files changed, 152 insertions(+), 179 deletions(-) diff --git a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test index b5052620f91..bdc49573ae5 100644 --- a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test +++ b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test @@ -29,7 +29,7 @@ insert into t1 values(1); insert into t2 select * from t1; commit; ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -43,7 +43,7 @@ insert into t2 select * from t1; # should say some changes to non-transact1onal tables couldn't be rolled back rollback; ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -59,7 +59,7 @@ insert into t2 select * from t1; rollback to savepoint my_savepoint; commit; ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -77,7 +77,7 @@ insert into t1 values(7); commit; select a from t1 order by a; # check that savepoints work :) ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -99,7 +99,7 @@ connection con2; # so SHOW BINLOG EVENTS may come before con1 does the loggin. To be sure that # logging has been done, we use a user lock. select get_lock("a",10); ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -111,7 +111,7 @@ reset master; insert into t1 values(9); insert into t2 select * from t1; ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -124,13 +124,13 @@ reset master; insert into t1 values(10); # first make t1 non-empty begin; insert into t2 select * from t1; ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; insert into t1 values(11); commit; ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -149,7 +149,7 @@ insert into t1 values(12); insert into t2 select * from t1; commit; ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -162,7 +162,7 @@ insert into t1 values(13); insert into t2 select * from t1; rollback; ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -178,7 +178,7 @@ insert into t2 select * from t1; rollback to savepoint my_savepoint; commit; ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -196,7 +196,7 @@ insert into t1 values(18); commit; select a from t1 order by a; # check that savepoints work :) ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -257,7 +257,7 @@ insert into t2 values (3); disconnect con2; connection con3; select get_lock("lock1",60); ---replace_column 5 # +--replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; do release_lock("lock1"); @@ -324,6 +324,7 @@ CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * ROLLBACK; SELECT * from t2; DROP TABLE t1,t2; +--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_mix_innodb_myisam.result b/mysql-test/r/binlog_row_mix_innodb_myisam.result index 32c21a01f27..ae66f98739d 100644 --- a/mysql-test/r/binlog_row_mix_innodb_myisam.result +++ b/mysql-test/r/binlog_row_mix_innodb_myisam.result @@ -8,12 +8,12 @@ insert into t2 select * from t1; commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Table_map 1 # table_id: # (test.t1) -master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 243 Table_map 1 # table_id: # (test.t2) -master-bin.000001 282 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 316 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -25,12 +25,12 @@ Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Table_map 1 # table_id: # (test.t1) -master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 243 Table_map 1 # table_id: # (test.t2) -master-bin.000001 282 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 316 Query 1 # use `test`; ROLLBACK +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; ROLLBACK delete from t1; delete from t2; reset master; @@ -45,16 +45,16 @@ Warning 1196 Some non-transactional changed tables couldn't be rolled back commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Table_map 1 # table_id: # (test.t1) -master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 243 Query 1 # use `test`; savepoint my_savepoint -master-bin.000001 328 Table_map 1 # table_id: # (test.t1) -master-bin.000001 367 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 401 Table_map 1 # table_id: # (test.t2) -master-bin.000001 440 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 479 Query 1 # use `test`; rollback to savepoint my_savepoint -master-bin.000001 576 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; savepoint my_savepoint +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; rollback to savepoint my_savepoint +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -74,18 +74,18 @@ a 7 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Table_map 1 # table_id: # (test.t1) -master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 243 Query 1 # use `test`; savepoint my_savepoint -master-bin.000001 328 Table_map 1 # table_id: # (test.t1) -master-bin.000001 367 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 401 Table_map 1 # table_id: # (test.t2) -master-bin.000001 440 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 479 Query 1 # use `test`; rollback to savepoint my_savepoint -master-bin.000001 576 Table_map 1 # table_id: # (test.t1) -master-bin.000001 615 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 649 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; savepoint my_savepoint +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; rollback to savepoint my_savepoint +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -100,12 +100,12 @@ get_lock("a",10) 1 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Table_map 1 # table_id: # (test.t1) -master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 243 Table_map 1 # table_id: # (test.t2) -master-bin.000001 282 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 316 Query 1 # use `test`; ROLLBACK +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; ROLLBACK delete from t1; delete from t2; reset master; @@ -113,11 +113,11 @@ insert into t1 values(9); insert into t2 select * from t1; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Table_map 1 # table_id: # (test.t1) -master-bin.000001 141 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 175 Xid 1 # COMMIT /* xid= */ -master-bin.000001 202 Table_map 1 # table_id: # (test.t2) -master-bin.000001 241 Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F delete from t1; delete from t2; reset master; @@ -126,24 +126,24 @@ begin; insert into t2 select * from t1; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Table_map 1 # table_id: # (test.t1) -master-bin.000001 141 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 175 Xid 1 # COMMIT /* xid= */ -master-bin.000001 202 Table_map 1 # table_id: # (test.t2) -master-bin.000001 241 Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F insert into t1 values(11); commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Table_map 1 # table_id: # (test.t1) -master-bin.000001 141 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 175 Xid 1 # COMMIT /* xid= */ -master-bin.000001 202 Table_map 1 # table_id: # (test.t2) -master-bin.000001 241 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 275 Query 1 # use `test`; BEGIN -master-bin.000001 343 Table_map 1 # table_id: # (test.t1) -master-bin.000001 382 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 416 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ alter table t2 engine=INNODB; delete from t1; delete from t2; @@ -154,12 +154,12 @@ insert into t2 select * from t1; commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Table_map 1 # table_id: # (test.t1) -master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 243 Table_map 1 # table_id: # (test.t2) -master-bin.000001 282 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 316 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -181,10 +181,10 @@ rollback to savepoint my_savepoint; commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Table_map 1 # table_id: # (test.t1) -master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 243 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -202,12 +202,12 @@ a 18 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Table_map 1 # table_id: # (test.t1) -master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 243 Table_map 1 # table_id: # (test.t1) -master-bin.000001 282 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 316 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; alter table t2 engine=MyISAM; @@ -254,28 +254,28 @@ get_lock("lock1",60) 1 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Table_map 1 # table_id: # (test.t1) -master-bin.000001 209 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 243 Table_map 1 # table_id: # (test.t1) -master-bin.000001 282 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 316 Xid 1 # COMMIT /* xid= */ -master-bin.000001 343 Table_map 1 # table_id: # (test.t1) -master-bin.000001 382 Delete_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 421 Xid 1 # COMMIT /* xid= */ -master-bin.000001 448 Query 1 # use `test`; alter table t2 engine=MyISAM -master-bin.000001 539 Table_map 1 # table_id: # (test.t1) -master-bin.000001 578 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 612 Xid 1 # COMMIT /* xid= */ -master-bin.000001 639 Table_map 1 # table_id: # (test.t2) -master-bin.000001 678 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 712 Query 1 # use `test`; drop table t1,t2 -master-bin.000001 791 Query 1 # use `test`; create table t0 (n int) -master-bin.000001 877 Table_map 1 # table_id: # (test.t0) -master-bin.000001 916 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 950 Table_map 1 # table_id: # (test.t0) -master-bin.000001 989 Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 1023 Query 1 # use `test`; create table t2 (n int) engine=innodb +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Delete_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; alter table t2 engine=MyISAM +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; drop table t1,t2 +master-bin.000001 # Query 1 # use `test`; create table t0 (n int) +master-bin.000001 # Table_map 1 # table_id: # (test.t0) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t0) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; create table t2 (n int) engine=innodb do release_lock("lock1"); drop table t0,t2; set autocommit=0; @@ -357,83 +357,55 @@ a b DROP TABLE t1,t2; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Table_map 1 142 table_id: # (test.t1) -master-bin.000001 142 Write_rows 1 189 table_id: # flags: STMT_END_F -master-bin.000001 189 Query 1 257 use `test`; BEGIN -master-bin.000001 257 Query 1 182 use `test`; CREATE TABLE `t2` ( +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; CREATE TABLE `t2` ( `a` int(11) NOT NULL DEFAULT '0', `b` int(11) DEFAULT NULL, PRIMARY KEY (`a`) ) ENGINE=InnoDB -master-bin.000001 439 Table_map 1 222 table_id: # (test.t2) -master-bin.000001 479 Write_rows 1 260 table_id: # flags: STMT_END_F -master-bin.000001 517 Xid 1 544 COMMIT /* xid= */ -master-bin.000001 544 Query 1 630 use `test`; DROP TABLE if exists t2 -master-bin.000001 630 Table_map 1 670 table_id: # (test.t1) -master-bin.000001 670 Write_rows 1 708 table_id: # flags: STMT_END_F -master-bin.000001 708 Query 1 776 use `test`; BEGIN -master-bin.000001 776 Query 1 192 use `test`; CREATE TEMPORARY TABLE `t2` ( +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; DROP TABLE if exists t2 +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; DROP TABLE IF EXISTS t2 +master-bin.000001 # Query 1 # use `test`; CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; CREATE TABLE `t2` ( `a` int(11) NOT NULL DEFAULT '0', `b` int(11) DEFAULT NULL, PRIMARY KEY (`a`) ) ENGINE=InnoDB -master-bin.000001 968 Query 1 1039 use `test`; ROLLBACK -master-bin.000001 1039 Query 1 1125 use `test`; DROP TABLE IF EXISTS t2 -master-bin.000001 1125 Query 1 1249 use `test`; CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb -master-bin.000001 1249 Table_map 1 1289 table_id: # (test.t1) -master-bin.000001 1289 Write_rows 1 1327 table_id: # flags: STMT_END_F -master-bin.000001 1327 Query 1 1395 use `test`; BEGIN -master-bin.000001 1395 Query 1 182 use `test`; CREATE TABLE `t2` ( - `a` int(11) NOT NULL DEFAULT '0', - `b` int(11) DEFAULT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB -master-bin.000001 1577 Table_map 1 222 table_id: # (test.t2) -master-bin.000001 1617 Write_rows 1 260 table_id: # flags: STMT_END_F -master-bin.000001 1655 Xid 1 1682 COMMIT /* xid= */ -master-bin.000001 1682 Query 1 80 use `test`; TRUNCATE table t2 -master-bin.000001 1762 Xid 1 1789 COMMIT /* xid= */ -master-bin.000001 1789 Table_map 1 1829 table_id: # (test.t1) -master-bin.000001 1829 Write_rows 1 1867 table_id: # flags: STMT_END_F -master-bin.000001 1867 Query 1 1935 use `test`; BEGIN -master-bin.000001 1935 Table_map 1 40 table_id: # (test.t2) -master-bin.000001 1975 Write_rows 1 78 table_id: # flags: STMT_END_F -master-bin.000001 2013 Xid 1 2040 COMMIT /* xid= */ -master-bin.000001 2040 Query 1 2116 use `test`; DROP TABLE t2 -master-bin.000001 2116 Table_map 1 2156 table_id: # (test.t1) -master-bin.000001 2156 Write_rows 1 2194 table_id: # flags: STMT_END_F -master-bin.000001 2194 Table_map 1 2234 table_id: # (test.t1) -master-bin.000001 2234 Write_rows 1 2272 table_id: # flags: STMT_END_F -master-bin.000001 2272 Table_map 1 2312 table_id: # (test.t1) -master-bin.000001 2312 Write_rows 1 2350 table_id: # flags: STMT_END_F -master-bin.000001 2350 Query 1 2418 use `test`; BEGIN -master-bin.000001 2418 Query 1 192 use `test`; CREATE TEMPORARY TABLE `t2` ( - `a` int(11) NOT NULL DEFAULT '0', - `b` int(11) DEFAULT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB -master-bin.000001 2610 Xid 1 2637 COMMIT /* xid= */ -master-bin.000001 2637 Table_map 1 2677 table_id: # (test.t1) -master-bin.000001 2677 Write_rows 1 2715 table_id: # flags: STMT_END_F -master-bin.000001 2715 Query 1 2783 use `test`; BEGIN -master-bin.000001 2783 Query 1 192 use `test`; CREATE TEMPORARY TABLE `t2` ( - `a` int(11) NOT NULL DEFAULT '0', - `b` int(11) DEFAULT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB -master-bin.000001 2975 Query 1 3046 use `test`; ROLLBACK -master-bin.000001 3046 Query 1 80 use `test`; TRUNCATE table t2 -master-bin.000001 3126 Xid 1 3153 COMMIT /* xid= */ -master-bin.000001 3153 Table_map 1 3193 table_id: # (test.t1) -master-bin.000001 3193 Write_rows 1 3231 table_id: # flags: STMT_END_F -master-bin.000001 3231 Query 1 3299 use `test`; BEGIN -master-bin.000001 3299 Query 1 192 use `test`; CREATE TEMPORARY TABLE `t2` ( - `a` int(11) NOT NULL DEFAULT '0', - `b` int(11) DEFAULT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB -master-bin.000001 3491 Xid 1 3518 COMMIT /* xid= */ -master-bin.000001 3518 Query 1 3622 use `test`; DROP TABLE `t1` /* generated by server */ +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; TRUNCATE table t2 +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Table_map 1 # table_id: # (test.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; DROP TABLE t2 +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; TRUNCATE table t2 +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; DROP TABLE `t1` /* generated by server */ reset master; create table t1 (a int) engine=innodb; create table t2 (a int) engine=myisam; From 17efd43b1aca4baadf4612f1c969f0c0d433a094 Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Sun, 9 Jul 2006 21:39:21 +0200 Subject: [PATCH 28/32] post-merge test/result fixes. --- mysql-test/r/rpl_switch_stm_row_mixed.result | 59 ++++++++------------ mysql-test/t/rpl_switch_stm_row_mixed.test | 7 +-- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/mysql-test/r/rpl_switch_stm_row_mixed.result b/mysql-test/r/rpl_switch_stm_row_mixed.result index 0e7240f7e14..9e8770ce108 100644 --- a/mysql-test/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/r/rpl_switch_stm_row_mixed.result @@ -226,6 +226,13 @@ begin set NEW.data = concat(NEW.data,UUID()); end| insert into t11 values("try_560_"); +insert delayed into t2 values("delay_1_"); +insert delayed into t2 values(concat("delay_2_",UUID())); +insert delayed into t2 values("delay_3_"),(concat("delay_4_",UUID())),("delay_5_"); +insert delayed into t2 values("delay_6_"); +insert delayed into t2 values(rand()); +set @a=2.345; +insert delayed into t2 values(@a); create table t20 select * from t1; create table t21 select * from t2; create table t22 select * from t3; @@ -312,24 +319,12 @@ INSERT INTO t16 SELECT * FROM t15; insert into t16 values("try_65_"); drop table t15; insert into t16 values("try_66_"); -insert delayed into t2 values("delay_1_"); -insert delayed into t2 values(concat("delay_2_",UUID())); -Warnings: -Warning 1265 Data truncated for column 'UUID()' at row 1 -insert delayed into t2 values("delay_3_"),(concat("delay_4_",UUID())),("delay_5_"); -Warnings: -Warning 1265 Data truncated for column 'UUID()' at row 2 -insert delayed into t2 values("delay_6_"); -create table t12 (a int, b float); -insert delayed into t12 values(1,rand()); -set @a=2.345; -insert delayed into t12 values(2,@a); select count(*) from t1; count(*) 7 select count(*) from t2; count(*) -7 +5 select count(*) from t3; count(*) 1 @@ -347,7 +342,7 @@ count(*) 66 select count(*) from t21; count(*) -14 +22 select count(*) from t22; count(*) 2 @@ -360,9 +355,6 @@ count(*) select count(*) from t16; count(*) 3 -select count(*) from t12; -count(*) -2 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # drop database if exists mysqltest1 @@ -474,21 +466,6 @@ master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; create table t12 (a int, b float) -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t12) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t12) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',... master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_270_" master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',... @@ -573,6 +550,20 @@ set NEW.data = concat(NEW.data,UUID()); end master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t11) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `mysqltest1`; create table t20 select * from t1 master-bin.000001 # Query 1 # use `mysqltest1`; create table t21 select * from t2 master-bin.000001 # Query 1 # use `mysqltest1`; create table t22 select * from t3 @@ -657,10 +648,6 @@ master-bin.000001 # Write_rows 1 # table_id: # master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `mysqltest1`; alter table t1 modify a int, drop primary key master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(null,"try_57_") -master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TEMPORARY TABLE `t15` ( - `UUID()` varchar(36) CHARACTER SET utf8 NOT NULL DEFAULT '' -) -master-bin.000001 # Query 1 # use `mysqltest1`; COMMIT master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TABLE `t16` ( `UUID()` varchar(36) CHARACTER SET utf8 NOT NULL DEFAULT '' ) diff --git a/mysql-test/t/rpl_switch_stm_row_mixed.test b/mysql-test/t/rpl_switch_stm_row_mixed.test index b5f08569398..aef69df914a 100644 --- a/mysql-test/t/rpl_switch_stm_row_mixed.test +++ b/mysql-test/t/rpl_switch_stm_row_mixed.test @@ -253,6 +253,7 @@ begin end| delimiter ;| insert into t11 values("try_560_"); + # Test that INSERT DELAYED works in mixed mode (BUG#20649) insert delayed into t2 values("delay_1_"); insert delayed into t2 values(concat("delay_2_",UUID())); @@ -262,10 +263,9 @@ insert delayed into t2 values("delay_6_"); # Test for BUG#20633 (INSERT DELAYED RAND()/user_variable does not # replicate fine in statement-based ; we test that in mixed mode it # works). -create table t12 (a int, b float); -insert delayed into t12 values(1,rand()); +insert delayed into t2 values(rand()); set @a=2.345; -insert delayed into t12 values(2,@a); +insert delayed into t2 values(@a); sleep 4; # time for the delayed inserts to reach disk @@ -455,7 +455,6 @@ select count(*) from t12; select count(*) from t13; #select count(*) from t14; select count(*) from t16; -select count(*) from t12; if ($you_want_to_test_UDF) { select count(*) from t6; From fadbdf27c3efc558ca4b78fb9f3199931cd0a12a Mon Sep 17 00:00:00 2001 From: "aelkin/elkin@dsl-hkigw8-feb1fb00-100.dhcp.inet.fi" <> Date: Mon, 10 Jul 2006 00:26:26 +0300 Subject: [PATCH 29/32] BUG#20919 temp tables closing fails when binlog is off closing temp tables through end_thread had a flaw in binlog-off branch of close_temporary_tables where next table to close was reset via table->next for (table= thd->temporary_tables; table; table= table->next) which was wrong since the current table instance got destoyed at close_temporary(table, 1); The fix adapts binlog-on branch method to engage the loop's internal 'next' variable which holds table->next prior table's destoying. --- sql/sql_base.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 98e4346eafc..0a9529d6067 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -496,11 +496,13 @@ void close_temporary_tables(THD *thd) TABLE *table; if (!thd->temporary_tables) return; - + if (!mysql_bin_log.is_open()) { - for (table= thd->temporary_tables; table; table= table->next) + TABLE *next; + for (table= thd->temporary_tables; table; table= next) { + next= table->next; close_temporary(table, 1); } thd->temporary_tables= 0; @@ -518,7 +520,7 @@ void close_temporary_tables(THD *thd) String s_query= String(buf, sizeof(buf), system_charset_info); bool found_user_tables= false; LINT_INIT(next); - + /* insertion sort of temp tables by pseudo_thread_id to build ordered list of sublists of equal pseudo_thread_id From 7848edd065a6a328c3bdc515f1f2a478bc6c7535 Mon Sep 17 00:00:00 2001 From: "mats@romeo.(none)" <> Date: Mon, 10 Jul 2006 12:06:30 +0200 Subject: [PATCH 30/32] Fixing problem where the command line for MYSQL_DUMP_SLAVE was not set properly. --- mysql-test/mysql-test-run.pl | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index f757c59d90e..ae3cab3f17c 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3273,9 +3273,16 @@ sub run_check_testcase ($) { } +sub generate_cmdline_mysqldump ($) { + my($info) = @_; + return + "$exe_mysqldump --no-defaults -uroot " . + "--port=$info->[0]->{'path_myport'} " . + "--socket=$info->[0]->{'path_mysock'} --password="; +} + sub run_mysqltest ($) { my $tinfo= shift; - my $cmdline_mysqlcheck= "$exe_mysqlcheck --no-defaults -uroot " . "--port=$master->[0]->{'path_myport'} " . "--socket=$master->[0]->{'path_mysock'} --password="; @@ -3285,17 +3292,15 @@ sub run_mysqltest ($) { " --debug=d:t:A,$opt_vardir_trace/log/mysqlcheck.trace"; } - my $cmdline_mysqldump= "$exe_mysqldump --no-defaults -uroot " . - "--port=$master->[0]->{'path_myport'} " . - "--socket=$master->[0]->{'path_mysock'} --password="; - - my $cmdline_mysqldumpslave= "$exe_mysqldump --no-defaults -uroot " . - "--socket=$slave->[0]->{'path_mysock'} --password="; + my $cmdline_mysqldump= generate_cmdline_mysqldump $master; + my $cmdline_mysqldumpslave= generate_cmdline_mysqldump $slave; if ( $opt_debug ) { $cmdline_mysqldump .= - " --debug=d:t:A,$opt_vardir_trace/log/mysqldump.trace"; + " --debug=d:t:A,$opt_vardir_trace/log/mysqldump-master.trace"; + $cmdline_mysqldumpslave .= + " --debug=d:t:A,$opt_vardir_trace/log/mysqldump-slave.trace"; } my $cmdline_mysqlslap; From 1cc3c8007051c5c6d7a81fefc07ae538873161f3 Mon Sep 17 00:00:00 2001 From: "guilhem@gbichot3.local" <> Date: Mon, 10 Jul 2006 18:41:03 +0200 Subject: [PATCH 31/32] fixes after merge. Updates to test's results. We now reset the THD members related to auto_increment+binlog in MYSQL_LOG::write(). This is better than in THD::cleanup_after_query(), which was not able to distinguish between SELECT myfunc1(),myfunc2() and INSERT INTO t SELECT myfunc1(),myfunc2() from a binlogging point of view. Rows_log_event::exec_event() now calls lex_start() instead of mysql_init_query() because the latter now does too much (it resets the binlog format). --- mysql-test/extra/rpl_tests/rpl_insert_id.test | 2 +- mysql-test/mysql-test-run.pl | 2 +- .../r/binlog_stm_mix_innodb_myisam.result | 190 +++++++++--------- mysql-test/r/rpl_insert_id.result | 72 +++---- mysql-test/r/rpl_loaddata.result | 6 +- mysql-test/r/rpl_switch_stm_row_mixed.result | 40 ++-- mysql-test/t/rpl_switch_stm_row_mixed.test | 12 +- sql/ha_partition.cc | 2 +- sql/ha_partition.h | 2 +- sql/handler.cc | 62 +++++- sql/log.cc | 3 + sql/log_event.cc | 18 +- sql/sql_base.cc | 4 - sql/sql_class.cc | 12 -- sql/sql_class.h | 5 +- sql/sql_delete.cc | 4 +- sql/sql_insert.cc | 9 +- sql/sql_parse.cc | 7 +- 18 files changed, 249 insertions(+), 203 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_insert_id.test b/mysql-test/extra/rpl_tests/rpl_insert_id.test index 29a07df2d3c..652ff8881c5 100644 --- a/mysql-test/extra/rpl_tests/rpl_insert_id.test +++ b/mysql-test/extra/rpl_tests/rpl_insert_id.test @@ -170,7 +170,7 @@ connection master; drop function bug15728; drop function bug15728_insert; -drop table t1, t2; +drop table t1; drop procedure foo; # test of BUG#20188 REPLACE or ON DUPLICATE KEY UPDATE in diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index fb000906e1e..6f20e7c55b6 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -291,7 +291,7 @@ our $opt_user_test; our $opt_valgrind= 0; our $opt_valgrind_mysqld= 0; our $opt_valgrind_mysqltest= 0; -our $default_valgrind_options= "-v --show-reachable=yes"; +our $default_valgrind_options= "--show-reachable=yes"; our $opt_valgrind_options; our $opt_valgrind_path; diff --git a/mysql-test/r/binlog_stm_mix_innodb_myisam.result b/mysql-test/r/binlog_stm_mix_innodb_myisam.result index e836cae0b15..95b6eb953a2 100644 --- a/mysql-test/r/binlog_stm_mix_innodb_myisam.result +++ b/mysql-test/r/binlog_stm_mix_innodb_myisam.result @@ -8,10 +8,10 @@ insert into t2 select * from t1; commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Query 1 # use `test`; insert into t1 values(1) -master-bin.000001 257 Query 1 # use `test`; insert into t2 select * from t1 -master-bin.000001 351 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(1) +master-bin.000001 # Query 1 # use `test`; insert into t2 select * from t1 +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -23,10 +23,10 @@ Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Query 1 # use `test`; insert into t1 values(2) -master-bin.000001 257 Query 1 # use `test`; insert into t2 select * from t1 -master-bin.000001 351 Query 1 # use `test`; ROLLBACK +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(2) +master-bin.000001 # Query 1 # use `test`; insert into t2 select * from t1 +master-bin.000001 # Query 1 # use `test`; ROLLBACK delete from t1; delete from t2; reset master; @@ -41,13 +41,13 @@ Warning 1196 Some non-transactional changed tables couldn't be rolled back commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Query 1 # use `test`; insert into t1 values(3) -master-bin.000001 257 Query 1 # use `test`; savepoint my_savepoint -master-bin.000001 342 Query 1 # use `test`; insert into t1 values(4) -master-bin.000001 429 Query 1 # use `test`; insert into t2 select * from t1 -master-bin.000001 523 Query 1 # use `test`; rollback to savepoint my_savepoint -master-bin.000001 620 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(3) +master-bin.000001 # Query 1 # use `test`; savepoint my_savepoint +master-bin.000001 # Query 1 # use `test`; insert into t1 values(4) +master-bin.000001 # Query 1 # use `test`; insert into t2 select * from t1 +master-bin.000001 # Query 1 # use `test`; rollback to savepoint my_savepoint +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -67,14 +67,14 @@ a 7 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Query 1 # use `test`; insert into t1 values(5) -master-bin.000001 257 Query 1 # use `test`; savepoint my_savepoint -master-bin.000001 342 Query 1 # use `test`; insert into t1 values(6) -master-bin.000001 429 Query 1 # use `test`; insert into t2 select * from t1 -master-bin.000001 523 Query 1 # use `test`; rollback to savepoint my_savepoint -master-bin.000001 620 Query 1 # use `test`; insert into t1 values(7) -master-bin.000001 707 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(5) +master-bin.000001 # Query 1 # use `test`; savepoint my_savepoint +master-bin.000001 # Query 1 # use `test`; insert into t1 values(6) +master-bin.000001 # Query 1 # use `test`; insert into t2 select * from t1 +master-bin.000001 # Query 1 # use `test`; rollback to savepoint my_savepoint +master-bin.000001 # Query 1 # use `test`; insert into t1 values(7) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -89,10 +89,10 @@ get_lock("a",10) 1 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Query 1 # use `test`; insert into t1 values(8) -master-bin.000001 257 Query 1 # use `test`; insert into t2 select * from t1 -master-bin.000001 351 Query 1 # use `test`; ROLLBACK +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(8) +master-bin.000001 # Query 1 # use `test`; insert into t2 select * from t1 +master-bin.000001 # Query 1 # use `test`; ROLLBACK delete from t1; delete from t2; reset master; @@ -100,9 +100,9 @@ insert into t1 values(9); insert into t2 select * from t1; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; insert into t1 values(9) -master-bin.000001 189 Xid 1 # COMMIT /* xid= */ -master-bin.000001 216 Query 1 # use `test`; insert into t2 select * from t1 +master-bin.000001 # Query 1 # use `test`; insert into t1 values(9) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; insert into t2 select * from t1 delete from t1; delete from t2; reset master; @@ -111,19 +111,19 @@ begin; insert into t2 select * from t1; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; insert into t1 values(10) -master-bin.000001 190 Xid 1 # COMMIT /* xid= */ -master-bin.000001 217 Query 1 # use `test`; insert into t2 select * from t1 +master-bin.000001 # Query 1 # use `test`; insert into t1 values(10) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; insert into t2 select * from t1 insert into t1 values(11); commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; insert into t1 values(10) -master-bin.000001 190 Xid 1 # COMMIT /* xid= */ -master-bin.000001 217 Query 1 # use `test`; insert into t2 select * from t1 -master-bin.000001 311 Query 1 # use `test`; BEGIN -master-bin.000001 379 Query 1 # use `test`; insert into t1 values(11) -master-bin.000001 467 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; insert into t1 values(10) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; insert into t2 select * from t1 +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(11) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ alter table t2 engine=INNODB; delete from t1; delete from t2; @@ -134,10 +134,10 @@ insert into t2 select * from t1; commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Query 1 # use `test`; insert into t1 values(12) -master-bin.000001 258 Query 1 # use `test`; insert into t2 select * from t1 -master-bin.000001 352 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(12) +master-bin.000001 # Query 1 # use `test`; insert into t2 select * from t1 +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -159,9 +159,9 @@ rollback to savepoint my_savepoint; commit; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Query 1 # use `test`; insert into t1 values(14) -master-bin.000001 258 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(14) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; reset master; @@ -179,10 +179,10 @@ a 18 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Query 1 # use `test`; insert into t1 values(16) -master-bin.000001 258 Query 1 # use `test`; insert into t1 values(18) -master-bin.000001 346 Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(16) +master-bin.000001 # Query 1 # use `test`; insert into t1 values(18) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ delete from t1; delete from t2; alter table t2 engine=MyISAM; @@ -229,29 +229,29 @@ get_lock("lock1",60) 1 show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 # use `test`; BEGIN -master-bin.000001 170 Query 1 # use `test`; insert into t1 values(16) -master-bin.000001 258 Query 1 # use `test`; insert into t1 values(18) -master-bin.000001 346 Xid 1 # COMMIT /* xid= */ -master-bin.000001 373 Query 1 # use `test`; delete from t1 -master-bin.000001 450 Xid 1 # COMMIT /* xid= */ -master-bin.000001 477 Query 1 # use `test`; delete from t2 -master-bin.000001 554 Xid 1 # COMMIT /* xid= */ -master-bin.000001 581 Query 1 # use `test`; alter table t2 engine=MyISAM -master-bin.000001 672 Query 1 # use `test`; insert into t1 values (1) -master-bin.000001 760 Xid 1 # COMMIT /* xid= */ -master-bin.000001 787 Query 1 # use `test`; insert into t2 values (20) -master-bin.000001 876 Query 1 # use `test`; drop table t1,t2 -master-bin.000001 955 Query 1 # use `test`; create temporary table ti (a int) engine=innodb -master-bin.000001 1065 Query 1 # use `test`; insert into ti values(1) -master-bin.000001 1152 Xid 1 # COMMIT /* xid= */ -master-bin.000001 1179 Query 1 # use `test`; create temporary table t1 (a int) engine=myisam -master-bin.000001 1289 Query 1 # use `test`; insert t1 values (1) -master-bin.000001 1372 Query 1 # use `test`; create table t0 (n int) -master-bin.000001 1458 Query 1 # use `test`; insert t0 select * from t1 -master-bin.000001 1547 Query 1 # use `test`; insert into t0 select GET_LOCK("lock1",null) -master-bin.000001 1654 Query 1 # use `test`; create table t2 (n int) engine=innodb -master-bin.000001 1754 Query 1 # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`.`t1`,`test`.`ti` +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; insert into t1 values(16) +master-bin.000001 # Query 1 # use `test`; insert into t1 values(18) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; delete from t1 +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; delete from t2 +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; alter table t2 engine=MyISAM +master-bin.000001 # Query 1 # use `test`; insert into t1 values (1) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; insert into t2 values (20) +master-bin.000001 # Query 1 # use `test`; drop table t1,t2 +master-bin.000001 # Query 1 # use `test`; create temporary table ti (a int) engine=innodb +master-bin.000001 # Query 1 # use `test`; insert into ti values(1) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; create temporary table t1 (a int) engine=myisam +master-bin.000001 # Query 1 # use `test`; insert t1 values (1) +master-bin.000001 # Query 1 # use `test`; create table t0 (n int) +master-bin.000001 # Query 1 # use `test`; insert t0 select * from t1 +master-bin.000001 # Query 1 # use `test`; insert into t0 select GET_LOCK("lock1",null) +master-bin.000001 # Query 1 # use `test`; create table t2 (n int) engine=innodb +master-bin.000001 # Query 1 # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`.`t1`,`test`.`ti` do release_lock("lock1"); drop table t0,t2; set autocommit=0; @@ -333,28 +333,28 @@ a b DROP TABLE t1,t2; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 102 Query 1 198 use `test`; INSERT INTO t1 values (1,1),(1,2) -master-bin.000001 198 Query 1 284 use `test`; DROP TABLE if exists t2 -master-bin.000001 284 Query 1 374 use `test`; INSERT INTO t1 values (3,3) -master-bin.000001 374 Query 1 460 use `test`; DROP TABLE IF EXISTS t2 -master-bin.000001 460 Query 1 584 use `test`; CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb -master-bin.000001 584 Query 1 674 use `test`; INSERT INTO t1 VALUES (4,4) -master-bin.000001 674 Query 1 80 use `test`; TRUNCATE table t2 -master-bin.000001 754 Xid 1 781 COMMIT /* xid= */ -master-bin.000001 781 Query 1 871 use `test`; INSERT INTO t1 VALUES (5,5) -master-bin.000001 871 Query 1 947 use `test`; DROP TABLE t2 -master-bin.000001 947 Query 1 1037 use `test`; INSERT INTO t1 values (6,6) -master-bin.000001 1037 Query 1 1171 use `test`; CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)) engine=innodb -master-bin.000001 1171 Query 1 1261 use `test`; INSERT INTO t1 values (7,7) -master-bin.000001 1261 Query 1 1351 use `test`; INSERT INTO t1 values (8,8) -master-bin.000001 1351 Query 1 1441 use `test`; INSERT INTO t1 values (9,9) -master-bin.000001 1441 Query 1 80 use `test`; TRUNCATE table t2 -master-bin.000001 1521 Xid 1 1548 COMMIT /* xid= */ -master-bin.000001 1548 Query 1 1640 use `test`; INSERT INTO t1 values (10,10) -master-bin.000001 1640 Query 1 1708 use `test`; BEGIN -master-bin.000001 1708 Query 1 94 use `test`; INSERT INTO t2 values (100,100) -master-bin.000001 1802 Xid 1 1829 COMMIT /* xid= */ -master-bin.000001 1829 Query 1 1908 use `test`; DROP TABLE t1,t2 +master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 values (1,1),(1,2) +master-bin.000001 # Query 1 # use `test`; DROP TABLE if exists t2 +master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 values (3,3) +master-bin.000001 # Query 1 # use `test`; DROP TABLE IF EXISTS t2 +master-bin.000001 # Query 1 # use `test`; CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb +master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 VALUES (4,4) +master-bin.000001 # Query 1 # use `test`; TRUNCATE table t2 +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 VALUES (5,5) +master-bin.000001 # Query 1 # use `test`; DROP TABLE t2 +master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 values (6,6) +master-bin.000001 # Query 1 # use `test`; CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)) engine=innodb +master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 values (7,7) +master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 values (8,8) +master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 values (9,9) +master-bin.000001 # Query 1 # use `test`; TRUNCATE table t2 +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 values (10,10) +master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # use `test`; INSERT INTO t2 values (100,100) +master-bin.000001 # Xid 1 # COMMIT /* xid= */ +master-bin.000001 # Query 1 # use `test`; DROP TABLE t1,t2 reset master; create table t1 (a int) engine=innodb; create table t2 (a int) engine=myisam; diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index 56e31b87c89..dbdbcf70944 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -153,43 +153,8 @@ id last_id 3 5 drop function bug15728; drop function bug15728_insert; -drop procedure foo; drop table t1; -truncate table t2; -create table t1 (id tinyint primary key); -create function insid() returns int -begin -insert into t2 (last_id) values (0); -return 0; -end| -set sql_log_bin=0; -insert into t2 (id) values(1),(2),(3); -delete from t2; -set sql_log_bin=1; -select insid(); -insid() -0 -set sql_log_bin=0; -insert into t2 (id) values(5),(6),(7); -delete from t2 where id>=5; -set sql_log_bin=1; -insert into t1 select insid(); -select * from t1; -id -0 -select * from t2; -id last_id -4 0 -8 0 -select * from t1; -id -0 -select * from t2; -id last_id -4 0 -8 0 -drop table t1, t2; -drop function insid; +drop procedure foo; create table t1 (n int primary key auto_increment not null, b int, unique(b)); set sql_log_bin=0; @@ -255,3 +220,38 @@ n b 2 100 3 350 drop table t1; +truncate table t2; +create table t1 (id tinyint primary key); +create function insid() returns int +begin +insert into t2 (last_id) values (0); +return 0; +end| +set sql_log_bin=0; +insert into t2 (id) values(1),(2),(3); +delete from t2; +set sql_log_bin=1; +select insid(); +insid() +0 +set sql_log_bin=0; +insert into t2 (id) values(5),(6),(7); +delete from t2 where id>=5; +set sql_log_bin=1; +insert into t1 select insid(); +select * from t1; +id +0 +select * from t2; +id last_id +4 0 +8 0 +select * from t1; +id +0 +select * from t2; +id last_id +4 0 +8 0 +drop table t1, t2; +drop function insid; diff --git a/mysql-test/r/rpl_loaddata.result b/mysql-test/r/rpl_loaddata.result index 4ffa65c2c82..5f8da2ee384 100644 --- a/mysql-test/r/rpl_loaddata.result +++ b/mysql-test/r/rpl_loaddata.result @@ -28,7 +28,7 @@ day id category name 2003-03-22 2416 a bbbbb show master status; File Position Binlog_Do_DB Binlog_Ignore_DB -slave-bin.000001 1276 +slave-bin.000001 1248 drop table t1; drop table t2; drop table t3; @@ -39,7 +39,7 @@ set global sql_slave_skip_counter=1; start slave; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1793 # # master-bin.000001 Yes Yes # 0 0 1793 # None 0 No # +# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1765 # # master-bin.000001 Yes Yes # 0 0 1765 # None 0 No # set sql_log_bin=0; delete from t1; set sql_log_bin=1; @@ -49,7 +49,7 @@ change master to master_user='test'; change master to master_user='root'; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1828 # # master-bin.000001 No No # 0 0 1828 # None 0 No # +# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1800 # # master-bin.000001 No No # 0 0 1800 # None 0 No # set global sql_slave_skip_counter=1; start slave; set sql_log_bin=0; diff --git a/mysql-test/r/rpl_switch_stm_row_mixed.result b/mysql-test/r/rpl_switch_stm_row_mixed.result index 9e8770ce108..50fdeff2fa1 100644 --- a/mysql-test/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/r/rpl_switch_stm_row_mixed.result @@ -228,7 +228,6 @@ end| insert into t11 values("try_560_"); insert delayed into t2 values("delay_1_"); insert delayed into t2 values(concat("delay_2_",UUID())); -insert delayed into t2 values("delay_3_"),(concat("delay_4_",UUID())),("delay_5_"); insert delayed into t2 values("delay_6_"); insert delayed into t2 values(rand()); set @a=2.345; @@ -274,6 +273,7 @@ create table t13 select * from t1; drop table t1; create table t1 (a int primary key auto_increment, b varchar(100)); drop function f; +create table t14 (unique (a)) select * from t2; truncate table t2; create function f1 (x varchar(100)) returns int deterministic begin @@ -342,7 +342,7 @@ count(*) 66 select count(*) from t21; count(*) -22 +19 select count(*) from t22; count(*) 2 @@ -352,6 +352,9 @@ count(*) select count(*) from t13; count(*) 1 +select count(*) from t14; +count(*) +4 select count(*) from t16; count(*) 3 @@ -455,22 +458,27 @@ begin insert into t1 values(concat("work_250_",x)); insert into t1 select "yesterday_270_"; end -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work_25_") -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_27_" -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values("work_29_") master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',... +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',_latin1'hello'))) master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_270_" -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',... -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_270_" -master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',... +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(concat("work_250_", NAME_CONST('x',_latin1'world'))) master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select "yesterday_270_" master-bin.000001 # Query 1 # use `mysqltest1`; drop function foo3 master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function foo3() returns bigint unsigned @@ -560,10 +568,6 @@ master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) -master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `mysqltest1`; create table t20 select * from t1 master-bin.000001 # Query 1 # use `mysqltest1`; create table t21 select * from t2 master-bin.000001 # Query 1 # use `mysqltest1`; create table t22 select * from t3 @@ -600,6 +604,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; create table t13 select * from t master-bin.000001 # Query 1 # use `mysqltest1`; drop table t1 master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a int primary key auto_increment, b varchar(100)) master-bin.000001 # Query 1 # use `mysqltest1`; drop function f +master-bin.000001 # Query 1 # use `mysqltest1`; create table t14 (unique (a)) select * from t2 master-bin.000001 # Query 1 # use `mysqltest1`; truncate table t2 master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function f1 (x varchar(100)) returns int deterministic begin @@ -647,6 +652,7 @@ master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) master-bin.000001 # Write_rows 1 # table_id: # master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `mysqltest1`; alter table t1 modify a int, drop primary key +master-bin.000001 # Intvar 1 # INSERT_ID=5 master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(null,"try_57_") master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TABLE `t16` ( `UUID()` varchar(36) CHARACTER SET utf8 NOT NULL DEFAULT '' diff --git a/mysql-test/t/rpl_switch_stm_row_mixed.test b/mysql-test/t/rpl_switch_stm_row_mixed.test index aef69df914a..6d282069ba1 100644 --- a/mysql-test/t/rpl_switch_stm_row_mixed.test +++ b/mysql-test/t/rpl_switch_stm_row_mixed.test @@ -257,7 +257,6 @@ insert into t11 values("try_560_"); # Test that INSERT DELAYED works in mixed mode (BUG#20649) insert delayed into t2 values("delay_1_"); insert delayed into t2 values(concat("delay_2_",UUID())); -insert delayed into t2 values("delay_3_"),(concat("delay_4_",UUID())),("delay_5_"); insert delayed into t2 values("delay_6_"); # Test for BUG#20633 (INSERT DELAYED RAND()/user_variable does not @@ -364,9 +363,8 @@ create table t1 (a int primary key auto_increment, b varchar(100)); # now test if it's two functions, each of them inserts in one table drop function f; -# Manifestation of BUG#20341! re-enable this line after merging fix -# for that bug -#create table t14 select * from t2; +# we need a unique key to have sorting of rows by mysqldump +create table t14 (unique (a)) select * from t2; truncate table t2; delimiter |; create function f1 (x varchar(100)) returns int deterministic @@ -453,7 +451,7 @@ select count(*) from t21; select count(*) from t22; select count(*) from t12; select count(*) from t13; -#select count(*) from t14; +select count(*) from t14; select count(*) from t16; if ($you_want_to_test_UDF) { @@ -476,10 +474,8 @@ sync_slave_with_master; --exec diff $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_master.sql $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_slave.sql; connection master; -# As one stored function's parameter is UUID(), its value ends up in a -# NAME_CONST in the binlog, we must hide it for repeatability --replace_column 2 # 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ /NAME_CONST\('x',.*/NAME_CONST('x',.../ +--replace_regex /table_id: [0-9]+/table_id: #/ show binlog events from 102; # Now test that mysqlbinlog works fine on a binlog generated by the diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index c0257e08537..8ed967a0d36 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -5263,7 +5263,7 @@ int ha_partition::cmp_ref(const byte *ref1, const byte *ref2) MODULE auto increment ****************************************************************************/ -void ha_partition::restore_auto_increment() +void ha_partition::restore_auto_increment(ulonglong) { DBUG_ENTER("ha_partition::restore_auto_increment"); diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 4c627cd50f8..f9fe5f2501f 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -811,7 +811,7 @@ public: auto_increment_column_changed ------------------------------------------------------------------------- */ - virtual void restore_auto_increment(); + virtual void restore_auto_increment(ulonglong prev_insert_id); virtual void get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values, ulonglong *first_value, diff --git a/sql/handler.cc b/sql/handler.cc index 51b95030929..b356102a61a 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1538,6 +1538,58 @@ compute_next_insert_id(ulonglong nr,struct system_variables *variables) } +void handler::adjust_next_insert_id_after_explicit_value(ulonglong nr) +{ + /* + If we have set THD::next_insert_id previously and plan to insert an + explicitely-specified value larger than this, we need to increase + THD::next_insert_id to be greater than the explicit value. + */ + if ((next_insert_id > 0) && (nr >= next_insert_id)) + set_next_insert_id(compute_next_insert_id(nr, &table->in_use->variables)); +} + + +/* + Computes the largest number X: + - smaller than or equal to "nr" + - of the form: auto_increment_offset + N * auto_increment_increment + where N>=0. + + SYNOPSIS + prev_insert_id + nr Number to "round down" + variables variables struct containing auto_increment_increment and + auto_increment_offset + + RETURN + The number X if it exists, "nr" otherwise. +*/ + +inline ulonglong +prev_insert_id(ulonglong nr, struct system_variables *variables) +{ + if (unlikely(nr < variables->auto_increment_offset)) + { + /* + There's nothing good we can do here. That is a pathological case, where + the offset is larger than the column's max possible value, i.e. not even + the first sequence value may be inserted. User will receive warning. + */ + DBUG_PRINT("info",("auto_increment: nr: %lu cannot honour " + "auto_increment_offset: %lu", + nr, variables->auto_increment_offset)); + return nr; + } + if (variables->auto_increment_increment == 1) + return nr; // optimization of the formula below + nr= (((nr - variables->auto_increment_offset)) / + (ulonglong) variables->auto_increment_increment); + return (nr * (ulonglong) variables->auto_increment_increment + + variables->auto_increment_offset); +} + + /* Update the auto_increment field if necessary @@ -1643,8 +1695,7 @@ bool handler::update_auto_increment() the last NULL needs to insert 3764, not the value of the first NULL plus 1). */ - if ((next_insert_id > 0) && (nr >= next_insert_id)) - set_next_insert_id(compute_next_insert_id(nr, variables)); + adjust_next_insert_id_after_explicit_value(nr); insert_id_for_cur_row= 0; // didn't generate anything DBUG_RETURN(0); } @@ -1736,12 +1787,15 @@ bool handler::update_auto_increment() { /* field refused this value (overflow) and truncated it, use the result of - the truncation (which is going to be inserted). + the truncation (which is going to be inserted); however we try to + decrease it to honour auto_increment_* variables. That will shift the left bound of the reserved interval, we don't bother shifting the right bound (anyway any other value from this interval will cause a duplicate key). */ - nr= table->next_number_field->val_int(); + nr= prev_insert_id(table->next_number_field->val_int(), variables); + if (unlikely(table->next_number_field->store((longlong) nr, TRUE))) + nr= table->next_number_field->val_int(); } if (append) { diff --git a/sql/log.cc b/sql/log.cc index c5f5743c51a..b8b9de112ff 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3401,6 +3401,9 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) } } } + /* Forget those values, for next binlogger: */ + thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; + thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty(); } /* diff --git a/sql/log_event.cc b/sql/log_event.cc index b61f71a4dcf..5541007e0c1 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5334,10 +5334,10 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) /* lock_tables() reads the contents of thd->lex, so they must be - initialized, so we should call lex_start(); to be even safer, we - call mysql_init_query() which does a more complete set of inits. + initialized. Contrary to in Table_map_log_event::exec_event() we don't + call mysql_init_query() as that may reset the binlog format. */ - mysql_init_query(thd, NULL, 0); + lex_start(thd, NULL, 0); while ((error= lock_tables(thd, rli->tables_to_lock, rli->tables_to_lock_count, &need_reopen))) @@ -5840,6 +5840,12 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) } else { + /* + open_tables() reads the contents of thd->lex, so they must be + initialized, so we should call lex_start(); to be even safer, we + call mysql_init_query() which does a more complete set of inits. + */ + mysql_init_query(thd, NULL, 0); /* Check if the slave is set to use SBR. If so, it should switch to using RBR until the end of the "statement", i.e., next @@ -5856,12 +5862,6 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) Note that for any table that should not be replicated, a filter is needed. */ uint count; - /* - open_tables() reads the contents of thd->lex, so they must be - initialized, so we should call lex_start(); to be even safer, we - call mysql_init_query() which does a more complete set of inits. - */ - mysql_init_query(thd, NULL, 0); if ((error= open_tables(thd, &table_list, &count, 0))) { if (thd->query_error || thd->is_fatal_error) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index a938287ece3..468bdde77e3 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3319,11 +3319,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) #ifdef HAVE_ROW_BASED_REPLICATION /* CREATE ... SELECT UUID() locks no tables, we have to test here. - Note that we will not do the resetting if inside a stored - function/trigger, because the binlogging of those is decided earlier (by - the caller) and can't be changed afterwards. */ - thd->reset_current_stmt_binlog_row_based(); if (thd->lex->binlog_row_based_if_mixed) thd->set_current_stmt_binlog_row_based_if_mixed(); #endif /*HAVE_ROW_BASED_REPLICATION*/ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 634e873fb00..1817697186f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -631,18 +631,6 @@ bool THD::store_globals() void THD::cleanup_after_query() { - /* - If in stored function or trigger, where statement-based binlogging logs - only the caller, the insert_id/last_insert_id stored in binlog must - describe their first values inside the routine or caller (the values when - they were first set). Otherwise (e.g. stored procedure) it must describe - their values for the current substatement. - */ - if (!prelocked_mode) - { - stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; - auto_inc_intervals_in_cur_stmt_for_binlog.empty(); - } if (first_successful_insert_id_in_cur_stmt > 0) { /* set what LAST_INSERT_ID() will return */ diff --git a/sql/sql_class.h b/sql/sql_class.h index fa1205976f4..3a305d984bf 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1149,8 +1149,9 @@ public: column; our rules are a) on master, while executing a top statement involving substatements, first top- or sub- statement to generate auto_increment values wins the - exclusive right to write them to binlog (so the losers won't write their - values to binlog). + exclusive right to see its values be written to binlog (the write + will be done by the statement or its caller), and the losers won't see + their values be written to binlog. b) on slave, while replicating a top statement involving substatements, first top- or sub- statement to need to read auto_increment values from the master's binlog wins the exclusive right to read them (so the losers diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index b9ce1a53aaf..fae1d90cc9a 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -986,12 +986,12 @@ trunc_by_del: thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT); ha_enable_transaction(thd, FALSE); mysql_init_select(thd->lex); -#ifdef HAVE_ROW_BASED_REPLICATION + bool save_binlog_row_based= thd->current_stmt_binlog_row_based; thd->clear_current_stmt_binlog_row_based(); -#endif error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0, HA_POS_ERROR, LL(0), TRUE); ha_enable_transaction(thd, TRUE); thd->options= save_options; + thd->current_stmt_binlog_row_based= save_binlog_row_based; DBUG_RETURN(error); } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 1648c6ee198..cd4fb6ace59 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1076,7 +1076,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (info->ignore && !table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) { - table->file->restore_auto_increment(); + table->file->restore_auto_increment(prev_insert_id); goto ok_or_after_trg_err; } goto err; @@ -1096,7 +1096,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) trg_error= (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE)); - info->copiedgoto ok_or_after_trg_err; + info->copied++; + goto ok_or_after_trg_err; } else /* DUP_REPLACE */ { @@ -1193,7 +1194,7 @@ err: table->file->print_error(error,MYF(0)); before_trg_err: - table->file->restore_auto_increment(); + table->file->restore_auto_increment(prev_insert_id); if (key) my_safe_afree(key, table->s->max_unique_length, MAX_KEY_LENGTH); table->column_bitmaps_set(save_read_set, save_write_set); @@ -2054,7 +2055,7 @@ bool delayed_insert::handle_inserts(void) thd.start_time=row->start_time; thd.query_start_used=row->query_start_used; /* for the binlog, forget auto_increment ids generated by previous rows */ - thd.auto_inc_intervals_in_cur_stmt_for_binlog.empty(); +// thd.auto_inc_intervals_in_cur_stmt_for_binlog.empty(); thd.first_successful_insert_id_in_prev_stmt= row->first_successful_insert_id_in_prev_stmt; thd.stmt_depends_on_first_successful_insert_id_in_prev_stmt= diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 3eeb5bedf41..732aaf2212e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5843,10 +5843,11 @@ void mysql_reset_thd_for_next_command(THD *thd) thd->rand_used= 0; thd->sent_row_count= thd->examined_row_count= 0; } -#ifdef HAVE_ROW_BASED_REPLICATION - /* If in a routine, we reset only at end of top statement. */ + /* + Because we come here only for start of top-statements, binlog format is + constant inside a complex statement (using stored functions) etc. + */ thd->reset_current_stmt_binlog_row_based(); -#endif /*HAVE_ROW_BASED_REPLICATION*/ DBUG_VOID_RETURN; } From f0537bc5032d9347392214846c88a9e40b5d3903 Mon Sep 17 00:00:00 2001 From: "mkindahl@dl145k.mysql.com" <> Date: Wed, 12 Jul 2006 08:52:47 +0200 Subject: [PATCH 32/32] Post-merge fixes for mysql-5.1-new-rpl --- mysql-test/r/rpl_switch_stm_row_mixed.result | 3 --- sql/sql_class.h | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/rpl_switch_stm_row_mixed.result b/mysql-test/r/rpl_switch_stm_row_mixed.result index 50fdeff2fa1..c319005b2a4 100644 --- a/mysql-test/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/r/rpl_switch_stm_row_mixed.result @@ -415,19 +415,16 @@ master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TABLE `t2` ( ) master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t2) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; COMMIT master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TABLE `t3` ( `1` varbinary(108) NOT NULL DEFAULT '' ) master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t3) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; COMMIT master-bin.000001 # Query 1 # use `mysqltest1`; CREATE TABLE `t4` ( `a` varchar(100) DEFAULT NULL ) master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t4) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -master-bin.000001 # Query 1 # use `mysqltest1`; COMMIT master-bin.000001 # Query 1 # use `mysqltest1`; create table t5 select * from t1 where 3 in (select 1 union select 2 union select curdate() union select 3) master-bin.000001 # Table_map 1 # table_id: # (mysqltest1.t5) master-bin.000001 # Write_rows 1 # table_id: # diff --git a/sql/sql_class.h b/sql/sql_class.h index 3255e4995b9..c7a3738c076 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1201,6 +1201,7 @@ public: */ inline void force_one_auto_inc_interval(ulonglong next_id) { + auto_inc_intervals_forced.empty(); // in case of multiple SET INSERT_ID auto_inc_intervals_forced.append(next_id, ULONGLONG_MAX, 0); } @@ -1571,6 +1572,7 @@ public: #else current_stmt_binlog_row_based= FALSE; #endif + } /* Initialize the current database from a NULL-terminated string with length