diff options
author | acurtis@pcgem.rdg.cyberkinetica.com <> | 2005-01-30 10:24:03 +0000 |
---|---|---|
committer | acurtis@pcgem.rdg.cyberkinetica.com <> | 2005-01-30 10:24:03 +0000 |
commit | 500fbf5c0f5c86d0b6ed2e1daefea55febb29b8c (patch) | |
tree | bda1004d18f8e154dab73080aead4599e04c4706 | |
parent | f003fb1f9c8042f061c10e3df9ce0627098218b6 (diff) | |
download | mariadb-git-500fbf5c0f5c86d0b6ed2e1daefea55febb29b8c.tar.gz |
Bug#7011
Fix replication for multi-update
new test - rpl_multi_update2
-rw-r--r-- | mysql-test/r/rpl_multi_update2.result | 42 | ||||
-rw-r--r-- | mysql-test/t/rpl_multi_update2-slave.opt | 1 | ||||
-rw-r--r-- | mysql-test/t/rpl_multi_update2.test | 33 | ||||
-rw-r--r-- | sql/mysql_priv.h | 3 | ||||
-rw-r--r-- | sql/sql_parse.cc | 81 | ||||
-rw-r--r-- | sql/sql_update.cc | 51 |
6 files changed, 192 insertions, 19 deletions
diff --git a/mysql-test/r/rpl_multi_update2.result b/mysql-test/r/rpl_multi_update2.result new file mode 100644 index 00000000000..40193f43e49 --- /dev/null +++ b/mysql-test/r/rpl_multi_update2.result @@ -0,0 +1,42 @@ +slave stop; +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; +slave start; +CREATE TABLE t1 ( +a int unsigned not null auto_increment primary key, +b int unsigned +) TYPE=MyISAM; +CREATE TABLE t2 ( +a int unsigned not null auto_increment primary key, +b int unsigned +) TYPE=MyISAM; +INSERT INTO t1 VALUES (NULL, 0); +INSERT INTO t1 SELECT NULL, 0 FROM t1; +INSERT INTO t2 VALUES (NULL, 0), (NULL,1); +SELECT * FROM t1 ORDER BY a; +a b +1 0 +2 0 +SELECT * FROM t2 ORDER BY a; +a b +1 0 +2 1 +UPDATE t1, t2 SET t1.b = (t2.b+4) WHERE t1.a = t2.a; +SELECT * FROM t1 ORDER BY a; +a b +1 4 +2 5 +SELECT * FROM t2 ORDER BY a; +a b +1 0 +2 1 +SELECT * FROM t1 ORDER BY a; +a b +1 4 +2 5 +SELECT * FROM t2 ORDER BY a; +a b +1 0 +2 1 diff --git a/mysql-test/t/rpl_multi_update2-slave.opt b/mysql-test/t/rpl_multi_update2-slave.opt new file mode 100644 index 00000000000..17d4171af0e --- /dev/null +++ b/mysql-test/t/rpl_multi_update2-slave.opt @@ -0,0 +1 @@ +--replicate-ignore-table=nothing.sensible diff --git a/mysql-test/t/rpl_multi_update2.test b/mysql-test/t/rpl_multi_update2.test new file mode 100644 index 00000000000..8216056aca5 --- /dev/null +++ b/mysql-test/t/rpl_multi_update2.test @@ -0,0 +1,33 @@ +# Let's verify that multi-update is not always skipped by slave if +# some replicate-* rules exist. +# (BUG#7011) + +source include/master-slave.inc; + +CREATE TABLE t1 ( + a int unsigned not null auto_increment primary key, + b int unsigned +) TYPE=MyISAM; + +CREATE TABLE t2 ( + a int unsigned not null auto_increment primary key, + b int unsigned +) TYPE=MyISAM; + +INSERT INTO t1 VALUES (NULL, 0); +INSERT INTO t1 SELECT NULL, 0 FROM t1; + +INSERT INTO t2 VALUES (NULL, 0), (NULL,1); + +SELECT * FROM t1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; + +UPDATE t1, t2 SET t1.b = (t2.b+4) WHERE t1.a = t2.a; +SELECT * FROM t1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; + +save_master_pos; +connection slave; +sync_with_master; +SELECT * FROM t1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b123927d09e..cbbf3b843d3 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -469,6 +469,9 @@ int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, List<Item> &values,COND *conds, ORDER *order, ha_rows limit, enum enum_duplicates handle_duplicates); +int mysql_multi_update_lock(THD *thd, + TABLE_LIST *table_list, + List<Item> *fields); int mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, List<Item> *values, COND *conds, ulong options, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1aeb158dc11..275b76db177 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -56,6 +56,8 @@ static int check_for_max_user_connections(USER_CONN *uc); static void decrease_user_connections(USER_CONN *uc); static bool check_db_used(THD *thd,TABLE_LIST *tables); static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); +static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables, + List<Item> *fields); static void mysql_init_query(THD *thd); static void remove_escape(char *name); static void refresh_status(void); @@ -1338,10 +1340,28 @@ mysql_execute_command(void) LEX *lex= &thd->lex; TABLE_LIST *tables=(TABLE_LIST*) lex->select_lex.table_list.first; SELECT_LEX *select_lex = lex->select; + bool slave_fake_lock= 0; + MYSQL_LOCK *fake_prev_lock= 0; DBUG_ENTER("mysql_execute_command"); if (thd->slave_thread) { + if (lex->sql_command == SQLCOM_MULTI_UPDATE) + { + DBUG_PRINT("info",("need faked locked tables")); + + if (check_multi_update_lock(thd, tables, &select_lex->item_list)) + goto error; + + /* Fix for replication, the tables are opened and locked, + now we pretend that we have performed a LOCK TABLES action */ + + fake_prev_lock= thd->locked_tables; + if (thd->lock) + thd->locked_tables= thd->lock; + thd->lock= 0; + slave_fake_lock= 1; + } /* Skip if we are in the slave thread, some table rules have been given and the table list says the query should not be replicated @@ -1949,7 +1969,7 @@ mysql_execute_command(void) if (select_lex->item_list.elements != lex->value_list.elements) { send_error(&thd->net,ER_WRONG_VALUE_COUNT); - DBUG_VOID_RETURN; + goto error; } { const char *msg= 0; @@ -2641,6 +2661,14 @@ mysql_execute_command(void) send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN : 0); error: + if (unlikely(slave_fake_lock)) + { + DBUG_PRINT("info",("undoing faked lock")); + thd->lock= thd->locked_tables; + thd->locked_tables= fake_prev_lock; + if (thd->lock == thd->locked_tables) + thd->lock= 0; + } DBUG_VOID_RETURN; } @@ -3907,3 +3935,54 @@ bool check_simple_select() } return 0; } + +/* + Setup locking for multi-table updates. Used by the replication slave. + Replication slave SQL thread examines (all_tables_not_ok()) the + locking state of referenced tables to determine if the query has to + be executed or ignored. Since in multi-table update, the + 'default' lock is read-only, this lock is corrected early enough by + calling this function, before the slave decides to execute/ignore. + + SYNOPSIS + check_multi_update_lock() + thd Current thread + tables List of user-supplied tables + fields List of fields requiring update + + RETURN VALUES + 0 ok + 1 error +*/ +static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables, + List<Item> *fields) +{ + bool res= 1; + TABLE_LIST *table; + DBUG_ENTER("check_multi_update_lock"); + + if (check_db_used(thd, tables)) + goto error; + + /* + Ensure that we have UPDATE or SELECT privilege for each table + The exact privilege is checked in mysql_multi_update() + */ + for (table= tables ; table ; table= table->next) + { + TABLE_LIST *save= table->next; + table->next= 0; + if (check_one_table_access(thd, UPDATE_ACL, table, 1) && + check_one_table_access(thd, SELECT_ACL, table, 0)) + goto error; + table->next= save; + } + + if (mysql_multi_update_lock(thd, tables, fields)) + goto error; + + res= 0; + +error: + DBUG_RETURN(res); +} diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 4f7e34ec74f..b1b30a29639 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -403,26 +403,20 @@ static table_map get_table_map(List<Item> *items) } - /* - Setup multi-update handling and call SELECT to do the join + Prepare tables for multi-update + Analyse which tables need specific privileges and perform locking + as required */ -int mysql_multi_update(THD *thd, - TABLE_LIST *table_list, - List<Item> *fields, - List<Item> *values, - COND *conds, - ulong options, - enum enum_duplicates handle_duplicates) +int mysql_multi_update_lock(THD *thd, + TABLE_LIST *table_list, + List<Item> *fields) { int res; - multi_update *result; TABLE_LIST *tl; const bool using_lock_tables= thd->locked_tables != 0; - DBUG_ENTER("mysql_multi_update"); - - thd->select_limit= HA_POS_ERROR; + DBUG_ENTER("mysql_multi_update_lock"); for (;;) { @@ -490,7 +484,7 @@ int mysql_multi_update(THD *thd, (grant_option && check_grant(thd, wants, tl, 0, 0))) { tl->next= save; - DBUG_RETURN(0); + DBUG_RETURN(1); } tl->next= save; } @@ -498,11 +492,7 @@ int mysql_multi_update(THD *thd, /* Relock the tables with the correct modes */ res= lock_tables(thd,table_list); if (using_lock_tables) - { - if (res) - DBUG_RETURN(res); break; // Don't have to do setup_field() - } /* We must setup fields again as the file may have been reopened @@ -535,6 +525,31 @@ int mysql_multi_update(THD *thd, */ close_thread_tables(thd); } + + DBUG_RETURN(res); +} + +/* + Setup multi-update handling and call SELECT to do the join +*/ + +int mysql_multi_update(THD *thd, + TABLE_LIST *table_list, + List<Item> *fields, + List<Item> *values, + COND *conds, + ulong options, + enum enum_duplicates handle_duplicates) +{ + int res; + TABLE_LIST *tl; + multi_update *result; + DBUG_ENTER("mysql_multi_update"); + + thd->select_limit= HA_POS_ERROR; + + if ((res= mysql_multi_update_lock(thd, table_list, fields))) + DBUG_RETURN(res); /* Count tables and setup timestamp handling |