diff --git a/mysql-test/suite/innodb/r/auto_increment_dup,skip-log-bin.rdiff b/mysql-test/suite/innodb/r/auto_increment_dup,skip-log-bin.rdiff deleted file mode 100644 index 7b4ec54eed8..00000000000 --- a/mysql-test/suite/innodb/r/auto_increment_dup,skip-log-bin.rdiff +++ /dev/null @@ -1,51 +0,0 @@ ---- auto_increment_dup.result -+++ auto_increment_dup,skip-log-bin.reject -@@ -89,13 +89,14 @@ - SET DEBUG_SYNC='execute_command_after_close_tables SIGNAL continue'; - affected rows: 0 - INSERT INTO t1(k) VALUES (2), (4), (5) ON DUPLICATE KEY UPDATE c='2'; --ERROR HY000: Lock wait timeout exceeded; try restarting transaction -+affected rows: 3 -+info: Records: 3 Duplicates: 0 Warnings: 0 - connection con1; - # - # 2 duplicates - # --affected rows: 3 --info: Records: 3 Duplicates: 0 Warnings: 0 -+affected rows: 4 -+info: Records: 3 Duplicates: 1 Warnings: 0 - connection default; - # - # 3 rows -@@ -103,19 +104,21 @@ - SELECT * FROM t1 order by k; - id k c - 1 1 NULL --2 2 NULL --3 3 NULL --affected rows: 3 -+4 2 1 -+2 3 NULL -+5 4 NULL -+6 5 NULL -+affected rows: 5 - INSERT INTO t1(k) VALUES (2), (4), (5) ON DUPLICATE KEY UPDATE c='2'; --affected rows: 4 --info: Records: 3 Duplicates: 1 Warnings: 0 -+affected rows: 6 -+info: Records: 3 Duplicates: 3 Warnings: 0 - SELECT * FROM t1 order by k; - id k c - 1 1 NULL --2 2 2 --3 3 NULL --7 4 NULL --8 5 NULL -+4 2 2 -+2 3 NULL -+5 4 2 -+6 5 2 - affected rows: 5 - disconnect con1; - disconnect con2; diff --git a/mysql-test/suite/innodb/r/auto_increment_dup.result b/mysql-test/suite/innodb/r/auto_increment_dup.result index 1467a459fc1..9926047b665 100644 --- a/mysql-test/suite/innodb/r/auto_increment_dup.result +++ b/mysql-test/suite/innodb/r/auto_increment_dup.result @@ -89,13 +89,14 @@ affected rows: 0 SET DEBUG_SYNC='execute_command_after_close_tables SIGNAL continue'; affected rows: 0 INSERT INTO t1(k) VALUES (2), (4), (5) ON DUPLICATE KEY UPDATE c='2'; -ERROR HY000: Lock wait timeout exceeded; try restarting transaction +affected rows: 3 +info: Records: 3 Duplicates: 0 Warnings: 0 connection con1; # # 2 duplicates # -affected rows: 3 -info: Records: 3 Duplicates: 0 Warnings: 0 +affected rows: 4 +info: Records: 3 Duplicates: 1 Warnings: 0 connection default; # # 3 rows @@ -103,19 +104,21 @@ connection default; SELECT * FROM t1 order by k; id k c 1 1 NULL -2 2 NULL -3 3 NULL -affected rows: 3 +4 2 1 +2 3 NULL +5 4 NULL +6 5 NULL +affected rows: 5 INSERT INTO t1(k) VALUES (2), (4), (5) ON DUPLICATE KEY UPDATE c='2'; -affected rows: 4 -info: Records: 3 Duplicates: 1 Warnings: 0 +affected rows: 6 +info: Records: 3 Duplicates: 3 Warnings: 0 SELECT * FROM t1 order by k; id k c 1 1 NULL -2 2 2 -3 3 NULL -7 4 NULL -8 5 NULL +4 2 2 +2 3 NULL +5 4 2 +6 5 2 affected rows: 5 disconnect con1; disconnect con2; diff --git a/mysql-test/suite/innodb/t/auto_increment_dup.test b/mysql-test/suite/innodb/t/auto_increment_dup.test index aa399e5966d..9e54a6a8a66 100644 --- a/mysql-test/suite/innodb/t/auto_increment_dup.test +++ b/mysql-test/suite/innodb/t/auto_increment_dup.test @@ -8,8 +8,6 @@ --source include/have_debug_sync.inc --source include/innodb_binlog.inc -let $stmt= `SELECT @@GLOBAL.log_bin`; - set global transaction isolation level repeatable read; CREATE TABLE t1( @@ -84,13 +82,7 @@ SET DEBUG_SYNC='ha_write_row_end SIGNAL write_row_done WAIT_FOR continue'; --reap SET DEBUG_SYNC='execute_command_after_close_tables SIGNAL continue'; -if ($stmt) { ---error ER_LOCK_WAIT_TIMEOUT INSERT INTO t1(k) VALUES (2), (4), (5) ON DUPLICATE KEY UPDATE c='2'; -} -if (!$stmt) { -INSERT INTO t1(k) VALUES (2), (4), (5) ON DUPLICATE KEY UPDATE c='2'; -} --connection con1 --echo # diff --git a/mysql-test/suite/rpl/disabled.def b/mysql-test/suite/rpl/disabled.def index cfebf0f6ff1..59ca2958e80 100644 --- a/mysql-test/suite/rpl/disabled.def +++ b/mysql-test/suite/rpl/disabled.def @@ -15,4 +15,3 @@ rpl_get_master_version_and_clock : Bug#11766137 Jan 05 2011 joro Valgrind warnin rpl_partition_archive : MDEV-5077 2013-09-27 svoj Cannot exchange partition with archive table rpl_row_binlog_max_cache_size : MDEV-11092 rpl_row_index_choice : MDEV-11666 -rpl_mdev_17614 : MDEV-17614/MDEV-17073 Unexpected lock conflict diff --git a/mysql-test/suite/rpl/r/rpl_mdev_17614.result b/mysql-test/suite/rpl/r/rpl_mdev_17614.result index 28de23e28c9..39057334926 100644 --- a/mysql-test/suite/rpl/r/rpl_mdev_17614.result +++ b/mysql-test/suite/rpl/r/rpl_mdev_17614.result @@ -3,19 +3,24 @@ include/master-slave.inc call mtr.add_suppression("Unsafe statement written to the binary log using statement format"); CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY , b INT, UNIQUE(b), c int) engine=innodb; +connection slave; +connection master; INSERT INTO t1 VALUES (1, 1, 1); BEGIN; INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); Warnings: Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe +connection master1; INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); Warnings: Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe +connection master; COMMIT; SELECT * FROM t1; a b c 1 1 2 2 2 3 +connection slave; include/wait_for_slave_sql_error.inc [errno=1062] Last_SQL_Error = 'Error 'Duplicate entry '1' for key 'b'' on query. Default database: 'test'. Query: 'INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c)'' #Different value from server @@ -26,61 +31,85 @@ a b c stop slave; include/wait_for_slave_to_stop.inc reset slave; +connection master; reset master; drop table t1; +connection slave; start slave; include/wait_for_slave_to_start.inc +connection master; CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT, UNIQUE(b), c int) engine=innodb; +connection slave; +connection master; INSERT INTO t1 VALUES (default, 1, 1); BEGIN; INSERT INTO t1 VALUES (default, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); +connection master1; INSERT INTO t1 VALUES(default, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); +connection master; COMMIT; SELECT * FROM t1; a b c 1 1 2 3 2 3 +connection slave; #same data as master SELECT * FROM t1; a b c 1 1 2 3 2 3 +connection master; drop table t1; +connection slave; +connection master; CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT, UNIQUE(b), c int, d int ) engine=innodb; +connection slave; +connection master; INSERT INTO t1 VALUES (1, 1, 1, 1); BEGIN; INSERT INTO t1 VALUES (2, NULL, 2, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); Warnings: Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe +connection master1; INSERT INTO t1 VALUES(3, NULL, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); Warnings: Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe +connection master; COMMIT; SELECT * FROM t1; a b c d 1 1 1 1 2 NULL 2 2 3 NULL 2 3 +connection slave; #same data as master SELECT * FROM t1; a b c d 1 1 1 1 2 NULL 2 2 3 NULL 2 3 +connection master; drop table t1; +connection slave; +connection master; CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT, UNIQUE(b), c int) engine=innodb; +connection slave; +connection master; INSERT INTO t1 VALUES (1, 1, 1); BEGIN; INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); +connection master1; INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); +connection master; COMMIT; SELECT * FROM t1; a b c 1 1 2 2 2 3 +connection slave; include/wait_for_slave_sql_error.inc [errno=1062] Last_SQL_Error = 'Error 'Duplicate entry '1' for key 'b'' on query. Default database: 'test'. Query: 'INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c)'' #Different value from server @@ -91,8 +120,10 @@ a b c stop slave; include/wait_for_slave_to_stop.inc reset slave; +connection master; reset master; drop table t1; +connection slave; start slave; include/wait_for_slave_to_start.inc include/rpl_end.inc diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0f6003264b6..20366742084 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -4553,12 +4553,6 @@ extern "C" int thd_rpl_is_parallel(const MYSQL_THD thd) return thd->rgi_slave && thd->rgi_slave->is_parallel_exec; } -extern "C" int thd_rpl_stmt_based(const MYSQL_THD thd) -{ - return thd && - !thd->is_current_stmt_binlog_format_row() && - !thd->is_current_stmt_binlog_disabled(); -} /* Returns high resolution timestamp for the start of the current query. */ diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index 7fd0de92562..693dcd15163 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -121,9 +121,6 @@ thd_is_replication_slave_thread( /*============================*/ THD* thd); /*!< in: thread handle */ -/** @return whether statement-based replication is active */ -extern "C" int thd_rpl_stmt_based(const THD* thd); - /******************************************************************//** Returns true if the transaction this thread is processing has edited non-transactional tables. Used by the deadlock detector when deciding diff --git a/storage/innobase/include/row0ins.h b/storage/innobase/include/row0ins.h index 2d4e9f67562..3a85d7a21c4 100644 --- a/storage/innobase/include/row0ins.h +++ b/storage/innobase/include/row0ins.h @@ -196,10 +196,6 @@ struct ins_node_t{ entry_list and sys fields are stored here; if this is NULL, entry list should be created and buffers for sys fields in row allocated */ - dict_index_t* duplicate; - /* This is the first index that reported - DB_DUPLICATE_KEY. Used in the case of REPLACE - or INSERT ... ON DUPLICATE UPDATE. */ ulint magic_n; }; diff --git a/storage/innobase/que/que0que.cc b/storage/innobase/que/que0que.cc index 9b98cdbcdcb..1d3d1573299 100644 --- a/storage/innobase/que/que0que.cc +++ b/storage/innobase/que/que0que.cc @@ -684,11 +684,6 @@ que_thr_stop( trx->lock.wait_thr = thr; thr->state = QUE_THR_LOCK_WAIT; - } else if (trx->duplicates && trx->error_state == DB_DUPLICATE_KEY - && thd_rpl_stmt_based(trx->mysql_thd)) { - - return(FALSE); - } else if (trx->error_state != DB_SUCCESS && trx->error_state != DB_LOCK_WAIT) { diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index ce394390679..ece291378b6 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -88,7 +88,6 @@ ins_node_create( node->select = NULL; node->trx_id = 0; - node->duplicate = NULL; node->entry_sys_heap = mem_heap_create(128); @@ -191,7 +190,6 @@ ins_node_set_new_row( node->state = INS_NODE_SET_IX_LOCK; node->index = NULL; node->entry = NULL; - node->duplicate = NULL; node->row = row; @@ -2320,12 +2318,6 @@ row_ins_duplicate_error_in_clust( true, ULINT_UNDEFINED, &heap); - ulint lock_type = - trx->isolation_level <= TRX_ISO_READ_COMMITTED - || (trx->mysql_thd - && !thd_rpl_stmt_based(trx->mysql_thd)) - ? LOCK_REC_NOT_GAP : LOCK_ORDINARY; - /* We set a lock on the possible duplicate: this is needed in logical logging of MySQL to make sure that in roll-forward we get the same duplicate @@ -2342,13 +2334,13 @@ row_ins_duplicate_error_in_clust( INSERT ON DUPLICATE KEY UPDATE). */ err = row_ins_set_exclusive_rec_lock( - lock_type, + LOCK_REC_NOT_GAP, btr_cur_get_block(cursor), rec, cursor->index, offsets, thr); } else { err = row_ins_set_shared_rec_lock( - lock_type, + LOCK_REC_NOT_GAP, btr_cur_get_block(cursor), rec, cursor->index, offsets, thr); } @@ -2363,7 +2355,10 @@ row_ins_duplicate_error_in_clust( if (row_ins_dupl_error_with_rec( rec, entry, cursor->index, offsets)) { - goto duplicate; +duplicate: + trx->error_info = cursor->index; + err = DB_DUPLICATE_KEY; + goto func_exit; } } } @@ -2406,10 +2401,7 @@ row_ins_duplicate_error_in_clust( if (row_ins_dupl_error_with_rec( rec, entry, cursor->index, offsets)) { -duplicate: - trx->error_info = cursor->index; - err = DB_DUPLICATE_KEY; - goto func_exit; + goto duplicate; } } @@ -3023,46 +3015,6 @@ row_ins_sec_index_entry_low( &cursor, 0, __FILE__, __LINE__, &mtr); } - if (!(flags & BTR_NO_LOCKING_FLAG) - && dict_index_is_unique(index) - && thr_get_trx(thr)->duplicates - && thr_get_trx(thr)->isolation_level >= TRX_ISO_REPEATABLE_READ - && thd_rpl_stmt_based(thr_get_trx(thr)->mysql_thd)) { - - /* In statement-based replication, when replicating a - REPLACE statement or ON DUPLICATE KEY UPDATE clause, a - gap lock is taken on the position of the to-be-inserted record, - to avoid other concurrent transactions from inserting the same - record. */ - - dberr_t err; - const rec_t* rec = page_rec_get_next_const( - btr_cur_get_rec(&cursor)); - - ut_ad(!page_rec_is_infimum(rec)); - - offsets = rec_get_offsets(rec, index, offsets, true, - ULINT_UNDEFINED, &offsets_heap); - - err = row_ins_set_exclusive_rec_lock( - LOCK_GAP, btr_cur_get_block(&cursor), rec, - index, offsets, thr); - - switch (err) { - case DB_SUCCESS: - case DB_SUCCESS_LOCKED_REC: - if (thr_get_trx(thr)->error_state != DB_DUPLICATE_KEY) { - break; - } - /* Fall through (skip actual insert) after we have - successfully acquired the gap lock. */ - default: - goto func_exit; - } - } - - ut_ad(thr_get_trx(thr)->error_state == DB_SUCCESS); - if (dup_chk_only) { goto func_exit; } @@ -3578,13 +3530,6 @@ row_ins( DBUG_PRINT("row_ins", ("table: %s", node->table->name.m_name)); - trx_t* trx = thr_get_trx(thr); - - if (node->duplicate) { - ut_ad(thd_rpl_stmt_based(trx->mysql_thd)); - trx->error_state = DB_DUPLICATE_KEY; - } - if (node->state == INS_NODE_ALLOC_ROW_ID) { row_ins_alloc_row_id_step(node); @@ -3610,91 +3555,7 @@ row_ins( if (node->index->type != DICT_FTS) { dberr_t err = row_ins_index_entry_step(node, thr); - switch (err) { - case DB_SUCCESS: - break; - case DB_NO_REFERENCED_ROW: - if (!dict_index_is_unique(node->index)) { - DBUG_RETURN(err); - } - /* fall through */ - case DB_DUPLICATE_KEY: - ut_ad(dict_index_is_unique(node->index)); - - if (trx->isolation_level - >= TRX_ISO_REPEATABLE_READ - && trx->duplicates - && !node->table->is_temporary() - && thd_rpl_stmt_based(trx->mysql_thd)) { - - /* When we are in REPLACE statement or - INSERT .. ON DUPLICATE UPDATE - statement, we process all the - unique secondary indexes, even after we - encounter a duplicate error. This is - done to take necessary gap locks in - secondary indexes to block concurrent - transactions from inserting the - searched records. */ - if (err == DB_NO_REFERENCED_ROW - && node->duplicate) { - /* A foreign key check on a - unique index may fail to - find the record. - - Consider as a example - following: - create table child(a int not null - primary key, b int not null, - c int, - unique key (b), - foreign key (b) references - parent (id)) engine=innodb; - - insert into child values - (1,1,2); - - insert into child(a) values - (1) on duplicate key update - c = 3; - - Now primary key value 1 - naturally causes duplicate - key error that will be - stored on node->duplicate. - If there was no duplicate - key error, we should return - the actual no referenced - row error. - - As value for - column b used in both unique - key and foreign key is not - provided, server uses 0 as a - search value. This is - naturally, not found leading - to DB_NO_REFERENCED_ROW. - But, we should update the - row with primay key value 1 - anyway. - - Return the - original DB_DUPLICATE_KEY - error after - placing all gaplocks. */ - err = DB_DUPLICATE_KEY; - break; - } else if (!node->duplicate) { - /* Save 1st dup error. Ignore - subsequent dup errors. */ - node->duplicate = node->index; - trx->error_state - = DB_DUPLICATE_KEY; - } - break; - } - // fall through - default: + if (err != DB_SUCCESS) { DBUG_RETURN(err); } } @@ -3711,31 +3572,13 @@ row_ins( node->index = dict_table_get_next_index(node->index); node->entry = UT_LIST_GET_NEXT(tuple_list, node->entry); } - - /* After encountering a duplicate key error, we process - remaining indexes just to place gap locks and no actual - insertion will take place. These gap locks are needed - only for unique indexes. So skipping non-unique indexes. */ - if (node->duplicate) { - ut_ad(thd_rpl_stmt_based(trx->mysql_thd)); - while (node->index - && !dict_index_is_unique(node->index)) { - - node->index = dict_table_get_next_index( - node->index); - node->entry = UT_LIST_GET_NEXT(tuple_list, - node->entry); - } - trx->error_state = DB_DUPLICATE_KEY; - } } ut_ad(node->entry == NULL); - trx->error_info = node->duplicate; node->state = INS_NODE_ALLOC_ROW_ID; - DBUG_RETURN(node->duplicate ? DB_DUPLICATE_KEY : DB_SUCCESS); + DBUG_RETURN(DB_SUCCESS); } /***********************************************************//** diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index fd87f8d03bc..f3d48edfb80 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1435,7 +1435,6 @@ error_exit: goto run_again; } - node->duplicate = NULL; trx->op_info = ""; if (blob_heap != NULL) { @@ -1445,8 +1444,6 @@ error_exit: return(err); } - node->duplicate = NULL; - if (dict_table_has_fts_index(table)) { doc_id_t doc_id;