diff --git a/handler/ha_innodb.cc b/handler/ha_innodb.cc index e6d07859d4e..616c6c509ad 100644 --- a/handler/ha_innodb.cc +++ b/handler/ha_innodb.cc @@ -3875,7 +3875,8 @@ ha_innobase::unlock_row(void) switch (prebuilt->row_read_type) { case ROW_READ_WITH_LOCKS: - if (!srv_locks_unsafe_for_binlog) { + if (!srv_locks_unsafe_for_binlog + || prebuilt->trx->isolation_level == TRX_ISO_READ_COMMITTED) { break; } /* fall through */ @@ -3907,7 +3908,13 @@ ha_innobase::try_semi_consistent_read(bool yes) { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - if (yes && srv_locks_unsafe_for_binlog) { + /* Row read type is set to semi consistent read if this was + requested by the MySQL and either innodb_locks_unsafe_for_binlog + option is used or this session is using READ COMMITTED isolation + level. */ + + if (yes && (srv_locks_unsafe_for_binlog + || prebuilt->trx->isolation_level == TRX_ISO_READ_COMMITTED)) { prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT; } else { prebuilt->row_read_type = ROW_READ_WITH_LOCKS; @@ -6390,12 +6397,6 @@ ha_innobase::external_lock( trx->n_mysql_tables_in_use++; prebuilt->mysql_has_locked = TRUE; - if (trx->n_mysql_tables_in_use == 1) { - trx->isolation_level = innobase_map_isolation_level( - (enum_tx_isolation) - thd->variables.tx_isolation); - } - if (trx->isolation_level == TRX_ISO_SERIALIZABLE && prebuilt->select_lock_type == LOCK_NONE && (thd->options @@ -6869,11 +6870,22 @@ ha_innobase::store_lock( TL_IGNORE */ { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + trx_t* trx = prebuilt->trx; /* NOTE: MySQL can call this function with lock 'type' TL_IGNORE! Be careful to ignore TL_IGNORE if we are going to do something with only 'real' locks! */ + /* If no MySQL tables is use we need to set isolation level + of the transaction. */ + + if (lock_type != TL_IGNORE + && trx->n_mysql_tables_in_use == 0) { + trx->isolation_level = innobase_map_isolation_level( + (enum_tx_isolation) + thd->variables.tx_isolation); + } + if ((lock_type == TL_READ && thd->in_lock_tables) || (lock_type == TL_READ_HIGH_PRIORITY && thd->in_lock_tables) || lock_type == TL_READ_WITH_SHARED_LOCKS || @@ -6898,15 +6910,21 @@ ha_innobase::store_lock( unexpected if an obsolete consistent read view would be used. */ - if (srv_locks_unsafe_for_binlog - && prebuilt->trx->isolation_level != TRX_ISO_SERIALIZABLE + ulint isolation_level; + + isolation_level = trx->isolation_level; + + if ((srv_locks_unsafe_for_binlog + || isolation_level == TRX_ISO_READ_COMMITTED) + && isolation_level != TRX_ISO_SERIALIZABLE && (lock_type == TL_READ || lock_type == TL_READ_NO_INSERT) && (thd->lex->sql_command == SQLCOM_INSERT_SELECT || thd->lex->sql_command == SQLCOM_UPDATE || thd->lex->sql_command == SQLCOM_CREATE_TABLE)) { - /* In case we have innobase_locks_unsafe_for_binlog - option set and isolation level of the transaction + /* If we either have innobase_locks_unsafe_for_binlog + option set or this session is using READ COMMITTED + isolation level and isolation level of the transaction is not set to serializable and MySQL is doing INSERT INTO...SELECT or UPDATE ... = (SELECT ...) or CREATE ... SELECT... without FOR UPDATE or diff --git a/include/row0mysql.h b/include/row0mysql.h index 8ff8a0476a1..48fb7432b54 100644 --- a/include/row0mysql.h +++ b/include/row0mysql.h @@ -244,7 +244,8 @@ row_update_for_mysql( row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL handle */ /************************************************************************* -This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before +This can only be used when srv_locks_unsafe_for_binlog is TRUE or +session is using a READ COMMITTED isolation level. Before calling this function we must use trx_reset_new_rec_lock_info() and trx_register_new_rec_lock() to store the information which new record locks really were set. This function removes a newly set lock under prebuilt->pcur, diff --git a/include/trx0trx.h b/include/trx0trx.h index 4278d602675..838b5b546c7 100644 --- a/include/trx0trx.h +++ b/include/trx0trx.h @@ -544,7 +544,9 @@ struct trx_struct{ the transaction; note that it is also in the lock list trx_locks */ dict_index_t* new_rec_locks[2];/* these are normally NULL; if - srv_locks_unsafe_for_binlog is TRUE, + srv_locks_unsafe_for_binlog is TRUE + or session is using READ COMMITTED + isolation level, in a cursor search, if we set a new record lock on an index, this is set to point to the index; this is diff --git a/lock/lock0lock.c b/lock/lock0lock.c index 1152e0c89ea..8cb32eaa997 100644 --- a/lock/lock0lock.c +++ b/lock/lock0lock.c @@ -2001,7 +2001,8 @@ lock_rec_lock_fast( if (!impl) { lock_rec_create(mode, rec, index, trx); - if (srv_locks_unsafe_for_binlog) { + if (srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) { trx_register_new_rec_lock(trx, index); } } @@ -2027,7 +2028,8 @@ lock_rec_lock_fast( if (!lock_rec_get_nth_bit(lock, heap_no)) { lock_rec_set_nth_bit(lock, heap_no); - if (srv_locks_unsafe_for_binlog) { + if (srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) { trx_register_new_rec_lock(trx, index); } } @@ -2087,7 +2089,8 @@ lock_rec_lock_slow( err = lock_rec_enqueue_waiting(mode, rec, index, thr); - if (srv_locks_unsafe_for_binlog) { + if (srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) { trx_register_new_rec_lock(trx, index); } } else { @@ -2096,7 +2099,8 @@ lock_rec_lock_slow( lock_rec_add_to_queue(LOCK_REC | mode, rec, index, trx); - if (srv_locks_unsafe_for_binlog) { + if (srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) { trx_register_new_rec_lock(trx, index); } } @@ -2436,15 +2440,18 @@ lock_rec_inherit_to_gap( lock = lock_rec_get_first(rec); - /* If srv_locks_unsafe_for_binlog is TRUE, we do not want locks set + /* If srv_locks_unsafe_for_binlog is TRUE or session is using + READ COMMITTED isolation level, we do not want locks set by an UPDATE or a DELETE to be inherited as gap type locks. But we DO want S-locks set by a consistency constraint to be inherited also then. */ while (lock != NULL) { if (!lock_rec_get_insert_intention(lock) - && !(srv_locks_unsafe_for_binlog - && lock_get_mode(lock) == LOCK_X)) { + && !((srv_locks_unsafe_for_binlog + || lock->trx->isolation_level == + TRX_ISO_READ_COMMITTED) + && lock_get_mode(lock) == LOCK_X)) { lock_rec_add_to_queue(LOCK_REC | lock_get_mode(lock) | LOCK_GAP, diff --git a/mysql-test/innodb-master.opt b/mysql-test/innodb-master.opt index 4cb927540bf..de2dc4f9ed4 100644 --- a/mysql-test/innodb-master.opt +++ b/mysql-test/innodb-master.opt @@ -1 +1 @@ ---binlog_cache_size=32768 +--binlog_cache_size=32768 --innodb_lock_wait_timeout=5 --loose_innodb_lock_wait_timeout=5 diff --git a/mysql-test/innodb.result b/mysql-test/innodb.result index de2fbb3c295..e86e945a8ac 100644 --- a/mysql-test/innodb.result +++ b/mysql-test/innodb.result @@ -3214,3 +3214,260 @@ UPDATE t1 SET field1 = 'other' WHERE field2 = 'somevalu'; ERROR 23000: Upholding foreign key constraints for table 't1', entry 'other-somevalu', key 1 would lead to a duplicate entry DROP TABLE t2; DROP TABLE t1; +create table t1 (id int not null, f_id int not null, f int not null, +primary key(f_id, id)) engine=innodb; +create table t2 (id int not null,s_id int not null,s varchar(200), +primary key(id)) engine=innodb; +INSERT INTO t1 VALUES (8, 1, 3); +INSERT INTO t1 VALUES (1, 2, 1); +INSERT INTO t2 VALUES (1, 0, ''); +INSERT INTO t2 VALUES (8, 1, ''); +commit; +DELETE ml.* FROM t1 AS ml LEFT JOIN t2 AS mm ON (mm.id=ml.id) +WHERE mm.id IS NULL; +select ml.* from t1 as ml left join t2 as mm on (mm.id=ml.id) +where mm.id is null lock in share mode; +id f_id f +drop table t1,t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2),(7,3); +commit; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +update t1 set b = 5 where b = 1; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +select * from t1 where a = 7 and b = 3 for update; +a b +7 3 +commit; +commit; +drop table t1; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2); +commit; +set autocommit = 0; +select * from t1 lock in share mode; +a b +1 1 +2 2 +3 1 +4 2 +5 1 +6 2 +update t1 set b = 5 where b = 1; +set autocommit = 0; +select * from t1 where a = 2 and b = 2 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +select * from t1 where b = 1 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +3 1 +8 6 +12 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +insert into t1 select * from t2; +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +d e +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +update t1 set b = (select e from t2 where a = d); +commit; +commit; +drop table t1, t2; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +create table t1(a int not null, b int, primary key(a)) engine=innodb +select * from t2; +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +3 1 +8 6 +12 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +insert into t1 select * from t2; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +d e +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +update t1 set b = (select e from t2 where a = d); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +create table t1(a int not null, b int, primary key(a)) engine=innodb +select * from t2; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +3 1 +8 6 +12 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +insert into t1 select * from t2 lock in share mode; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +d e +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +update t1 set b = (select e from t2 where a = d lock in share mode); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +create table t1(a int not null, b int, primary key(a)) engine=innodb select * from t2 lock in share mode; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +3 1 +8 6 +12 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +insert into t1 select * from t2 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +d e +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +update t1 set b = (select e from t2 where a = d for update); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +create table t1(a int not null, b int, primary key(a)) engine=innodb select * from t2 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t2; diff --git a/mysql-test/innodb.test b/mysql-test/innodb.test index 27d9814404d..851a93f48fe 100644 --- a/mysql-test/innodb.test +++ b/mysql-test/innodb.test @@ -2113,3 +2113,407 @@ UPDATE t1 SET field1 = 'other' WHERE field2 = 'somevalu'; DROP TABLE t2; DROP TABLE t1; + +create table t1 (id int not null, f_id int not null, f int not null, +primary key(f_id, id)) engine=innodb; +create table t2 (id int not null,s_id int not null,s varchar(200), +primary key(id)) engine=innodb; +INSERT INTO t1 VALUES (8, 1, 3); +INSERT INTO t1 VALUES (1, 2, 1); +INSERT INTO t2 VALUES (1, 0, ''); +INSERT INTO t2 VALUES (8, 1, ''); +commit; +DELETE ml.* FROM t1 AS ml LEFT JOIN t2 AS mm ON (mm.id=ml.id) +WHERE mm.id IS NULL; +select ml.* from t1 as ml left join t2 as mm on (mm.id=ml.id) +where mm.id is null lock in share mode; +drop table t1,t2; + +# +# Test case where X-locks on unused rows should be released in a +# update (because READ COMMITTED isolation level) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2),(7,3); +commit; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +update t1 set b = 5 where b = 1; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +# +# X-lock to record (7,3) should be released in a update +# +select * from t1 where a = 7 and b = 3 for update; +connection a; +commit; +connection b; +commit; +drop table t1; +connection default; +disconnect a; +disconnect b; + +# +# Test case where no locks should be released (because we are not +# using READ COMMITTED isolation level) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2); +commit; +set autocommit = 0; +select * from t1 lock in share mode; +update t1 set b = 5 where b = 1; +connection b; +set autocommit = 0; +# +# S-lock to records (2,2),(4,2), and (6,2) should not be released in a update +# +--error 1205 +select * from t1 where a = 2 and b = 2 for update; +# +# X-lock to record (1,1),(3,1),(5,1) should not be released in a update +# +--error 1205 +select * from t1 where b = 1 for update; +connection a; +commit; +connection b; +commit; +drop table t1; +connection default; +disconnect a; +disconnect b; + +# +# Consistent read should be used in following selects +# +# 1) INSERT INTO ... SELECT + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +insert into t1 select * from t2; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 2) UPDATE ... = ( SELECT ...) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +update t1 set b = (select e from t2 where a = d); +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 3) CREATE ... SELECT +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +create table t1(a int not null, b int, primary key(a)) engine=innodb +select * from t2; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# Consistent read should not be used if isolation level is serializable +# + +# 1) INSERT INTO ... SELECT + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +--error 1205 +insert into t1 select * from t2; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 2) UPDATE ... = ( SELECT ...) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +--error 1205 +update t1 set b = (select e from t2 where a = d); +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 3) CREATE ... SELECT +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +--error 1205 +create table t1(a int not null, b int, primary key(a)) engine=innodb +select * from t2; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t2; + +# +# Consistent read should not be used if locking read is used case +# (a) lock in share mode +# + +# 1) INSERT INTO ... SELECT + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +--error 1205 +insert into t1 select * from t2 lock in share mode; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 2) UPDATE ... = ( SELECT ...) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +--error 1205 +update t1 set b = (select e from t2 where a = d lock in share mode); +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 3) CREATE ... SELECT +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +--error 1205 +create table t1(a int not null, b int, primary key(a)) engine=innodb select * from t2 lock in share mode; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t2; + +# +# Consistent read should not be used if locking read is used case +# (b) for update +# + +# 1) INSERT INTO ... SELECT + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +--error 1205 +insert into t1 select * from t2 for update; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 2) UPDATE ... = ( SELECT ...) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +--error 1205 +update t1 set b = (select e from t2 where a = d for update); +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 3) CREATE ... SELECT +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +--error 1205 +create table t1(a int not null, b int, primary key(a)) engine=innodb select * from t2 for update; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t2; diff --git a/mysql-test/innodb_unsafe_binlog-master.opt b/mysql-test/innodb_unsafe_binlog-master.opt index 503c8457b2c..87af4c58c64 100644 --- a/mysql-test/innodb_unsafe_binlog-master.opt +++ b/mysql-test/innodb_unsafe_binlog-master.opt @@ -1 +1 @@ ---innodb_locks_unsafe_for_binlog=true +--innodb_locks_unsafe_for_binlog=true --innodb_lock_wait_timeout=2 --loose_innodb_lock_wait_timeout=2 \ No newline at end of file diff --git a/mysql-test/innodb_unsafe_binlog.result b/mysql-test/innodb_unsafe_binlog.result index e741fbb75a4..cb0e187ddd7 100644 --- a/mysql-test/innodb_unsafe_binlog.result +++ b/mysql-test/innodb_unsafe_binlog.result @@ -15,7 +15,7 @@ where mm.id is null lock in share mode; id f_id f drop table t1,t2; create table t1(a int not null, b int, primary key(a)) engine=innodb; -insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2); +insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2),(7,3); commit; set autocommit = 0; select * from t1 lock in share mode; @@ -26,6 +26,7 @@ a b 4 2 5 1 6 2 +7 3 update t1 set b = 5 where b = 1; set autocommit = 0; select * from t1 where a = 2 and b = 2 for update; @@ -33,3 +34,213 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction commit; commit; drop table t1; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2),(7,3); +commit; +set autocommit = 0; +update t1 set b = 5 where b = 1; +set autocommit = 0; +select * from t1 where a = 7 and b = 3 for update; +a b +7 3 +commit; +commit; +drop table t1; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +3 1 +8 6 +12 1 +set autocommit = 0; +insert into t1 select * from t2; +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +d e +1 6 +4 1 +5 1 +set autocommit = 0; +update t1 set b = (select e from t2 where a = d); +commit; +commit; +drop table t1, t2; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +1 6 +4 1 +5 1 +set autocommit = 0; +create table t1(a int not null, b int, primary key(a)) engine=innodb +select * from t2; +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +3 1 +8 6 +12 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +insert into t1 select * from t2; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +d e +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +update t1 set b = (select e from t2 where a = d); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +1 6 +4 1 +5 1 +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +create table t1(a int not null, b int, primary key(a)) engine=innodb +select * from t2; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +3 1 +8 6 +12 1 +set autocommit = 0; +insert into t1 select * from t2 lock in share mode; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +d e +1 6 +4 1 +5 1 +set autocommit = 0; +update t1 set b = (select e from t2 where a = d lock in share mode); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +1 6 +4 1 +5 1 +set autocommit = 0; +create table t1(a int not null, b int, primary key(a)) engine=innodb select * from t2 lock in share mode; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +3 1 +8 6 +12 1 +set autocommit = 0; +insert into t1 select * from t2 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +d e +1 6 +4 1 +5 1 +set autocommit = 0; +update t1 set b = (select e from t2 where a = d for update); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t1, t2; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +a b +1 6 +4 1 +5 1 +set autocommit = 0; +create table t1(a int not null, b int, primary key(a)) engine=innodb select * from t2 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +commit; +commit; +drop table t2; diff --git a/mysql-test/innodb_unsafe_binlog.test b/mysql-test/innodb_unsafe_binlog.test index e4cb683e59d..4a8f6891630 100644 --- a/mysql-test/innodb_unsafe_binlog.test +++ b/mysql-test/innodb_unsafe_binlog.test @@ -1,7 +1,9 @@ -- source include/have_innodb.inc # -# Note that these tests uses a innodb_locks_unsafe_for_binlog option. -# +# Note that these tests uses options +# innodb_locks_unsafe_for_binlog = true +# innodb_lock_timeout = 5 + # # Test cases for a bug #15650 # @@ -33,7 +35,7 @@ connect (a,localhost,root,,); connect (b,localhost,root,,); connection a; create table t1(a int not null, b int, primary key(a)) engine=innodb; -insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2); +insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2),(7,3); commit; set autocommit = 0; select * from t1 lock in share mode; @@ -50,6 +52,348 @@ commit; connection b; commit; drop table t1; +connection default; disconnect a; disconnect b; +# +# unlock row test +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values(1,1),(2,2),(3,1),(4,2),(5,1),(6,2),(7,3); +commit; +set autocommit = 0; +update t1 set b = 5 where b = 1; +connection b; +set autocommit = 0; +# +# X-lock to record (7,3) should be released in a update +# +select * from t1 where a = 7 and b = 3 for update; +commit; +connection a; +commit; +drop table t1; +connection default; +disconnect a; +disconnect b; + + +# +# Consistent read should be used in following selects +# +# 1) INSERT INTO ... SELECT + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +insert into t1 select * from t2; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 2) UPDATE ... = ( SELECT ...) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +update t1 set b = (select e from t2 where a = d); +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 3) CREATE ... SELECT +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +create table t1(a int not null, b int, primary key(a)) engine=innodb +select * from t2; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# Consistent read should not be used if isolation level is serializable +# + +# 1) INSERT INTO ... SELECT + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +--error 1205 +insert into t1 select * from t2; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 2) UPDATE ... = ( SELECT ...) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +--error 1205 +update t1 set b = (select e from t2 where a = d); +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 3) CREATE ... SELECT +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; +--error 1205 +create table t1(a int not null, b int, primary key(a)) engine=innodb +select * from t2; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t2; + +# +# Consistent read should not be used if locking read is used case +# (a) lock in share mode +# + +# 1) INSERT INTO ... SELECT + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +--error 1205 +insert into t1 select * from t2 lock in share mode; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 2) UPDATE ... = ( SELECT ...) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +--error 1205 +update t1 set b = (select e from t2 where a = d lock in share mode); +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 3) CREATE ... SELECT +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +--error 1205 +create table t1(a int not null, b int, primary key(a)) engine=innodb select * from t2 lock in share mode; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t2; + +# +# Consistent read should not be used if locking read is used case +# (b) for update +# + +# 1) INSERT INTO ... SELECT + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (8,6),(12,1),(3,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +--error 1205 +insert into t1 select * from t2 for update; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 2) UPDATE ... = ( SELECT ...) +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t1(a int not null, b int, primary key(a)) engine=innodb; +insert into t1 values (1,2),(5,3),(4,2); +create table t2(d int not null, e int, primary key(d)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +--error 1205 +update t1 set b = (select e from t2 where a = d for update); +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t1, t2; + +# +# 3) CREATE ... SELECT +# + +connect (a,localhost,root,,); +connect (b,localhost,root,,); +connection a; +create table t2(a int not null, b int, primary key(a)) engine=innodb; +insert into t2 values (1,6),(5,1),(4,1); +commit; +set autocommit = 0; +select * from t2 for update; +connection b; +set autocommit = 0; +--error 1205 +create table t1(a int not null, b int, primary key(a)) engine=innodb select * from t2 for update; +commit; +connection a; +commit; +connection default; +disconnect a; +disconnect b; +drop table t2; diff --git a/row/row0mysql.c b/row/row0mysql.c index 4dbe5128974..4b2954c715b 100644 --- a/row/row0mysql.c +++ b/row/row0mysql.c @@ -1435,7 +1435,8 @@ run_again: } /************************************************************************* -This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before +This can only be used when srv_locks_unsafe_for_binlog is TRUE or +this session is using a READ COMMITTED isolation level. Before calling this function we must use trx_reset_new_rec_lock_info() and trx_register_new_rec_lock() to store the information which new record locks really were set. This function removes a newly set lock under prebuilt->pcur, @@ -1466,11 +1467,13 @@ row_unlock_for_mysql( ut_ad(prebuilt && trx); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); - if (!srv_locks_unsafe_for_binlog) { + if (!(srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED)) { fprintf(stderr, "InnoDB: Error: calling row_unlock_for_mysql though\n" -"InnoDB: srv_locks_unsafe_for_binlog is FALSE.\n"); +"InnoDB: srv_locks_unsafe_for_binlog is FALSE and\n" +"InnoDB: this session is not using READ COMMITTED isolation level.\n"); return(DB_SUCCESS); } diff --git a/row/row0sel.c b/row/row0sel.c index c5f4df68de6..e044ba2f9a8 100644 --- a/row/row0sel.c +++ b/row/row0sel.c @@ -710,12 +710,17 @@ row_sel_get_clust_rec( if (!node->read_view) { /* Try to place a lock on the index record */ - /* If innodb_locks_unsafe_for_binlog option is used, + /* If innodb_locks_unsafe_for_binlog option is used + or this session is using READ COMMITTED isolation level we lock only the record, i.e., next-key locking is not used. */ ulint lock_type; + trx_t* trx; - if (srv_locks_unsafe_for_binlog) { + trx = thr_get_trx(thr); + + if (srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) { lock_type = LOCK_REC_NOT_GAP; } else { lock_type = LOCK_ORDINARY; @@ -1307,16 +1312,22 @@ rec_loop: if (!consistent_read) { - /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e., next-key locking is - not used. */ + /* If innodb_locks_unsafe_for_binlog option is used + or this session is using READ COMMITTED isolation + level, we lock only the record, i.e., next-key + locking is not used. */ rec_t* next_rec = page_rec_get_next(rec); ulint lock_type; + trx_t* trx; + + trx = thr_get_trx(thr); + offsets = rec_get_offsets(next_rec, index, offsets, ULINT_UNDEFINED, &heap); - if (srv_locks_unsafe_for_binlog) { + if (srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) { lock_type = LOCK_REC_NOT_GAP; } else { lock_type = LOCK_ORDINARY; @@ -1350,15 +1361,21 @@ rec_loop: if (!consistent_read) { /* Try to place a lock on the index record */ - /* If innodb_locks_unsafe_for_binlog option is used, + /* If innodb_locks_unsafe_for_binlog option is used + or this session is using READ COMMITTED isolation level, we lock only the record, i.e., next-key locking is not used. */ ulint lock_type; + trx_t* trx; + offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); - if (srv_locks_unsafe_for_binlog) { + trx = thr_get_trx(thr); + + if (srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) { lock_type = LOCK_REC_NOT_GAP; } else { lock_type = LOCK_ORDINARY; @@ -3206,11 +3223,13 @@ stderr); } /* Reset the new record lock info if srv_locks_unsafe_for_binlog - is set. Then we are able to remove the record locks set here on an - individual row. */ + is set or session is using a READ COMMITED isolation level. Then + we are able to remove the record locks set here on an individual + row. */ - if (srv_locks_unsafe_for_binlog - && prebuilt->select_lock_type != LOCK_NONE) { + if ((srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) + && prebuilt->select_lock_type != LOCK_NONE) { trx_reset_new_rec_lock_info(trx); } @@ -3578,13 +3597,15 @@ rec_loop: if (page_rec_is_supremum(rec)) { if (set_also_gap_locks - && !srv_locks_unsafe_for_binlog - && prebuilt->select_lock_type != LOCK_NONE) { + && !(srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) + && prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a lock on the index record */ - /* If innodb_locks_unsafe_for_binlog option is used, - we do not lock gaps. Supremum record is really + /* If innodb_locks_unsafe_for_binlog option is used + or this session is using a READ COMMITTED isolation + level we do not lock gaps. Supremum record is really a gap and therefore we do not set locks there. */ offsets = rec_get_offsets(rec, index, offsets, @@ -3704,12 +3725,14 @@ wrong_offs: if (0 != cmp_dtuple_rec(search_tuple, rec, offsets)) { if (set_also_gap_locks - && !srv_locks_unsafe_for_binlog - && prebuilt->select_lock_type != LOCK_NONE) { + && !(srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) + && prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a gap lock on the index record only if innodb_locks_unsafe_for_binlog - option is not set */ + option is not set or this session is not + using a READ COMMITTED isolation level. */ err = sel_set_rec_lock(rec, index, offsets, prebuilt->select_lock_type, @@ -3735,12 +3758,14 @@ wrong_offs: if (!cmp_dtuple_is_prefix_of_rec(search_tuple, rec, offsets)) { if (set_also_gap_locks - && !srv_locks_unsafe_for_binlog - && prebuilt->select_lock_type != LOCK_NONE) { + && !(srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) + && prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a gap lock on the index record only if innodb_locks_unsafe_for_binlog - option is not set */ + option is not set or this session is not + using a READ COMMITTED isolation level. */ err = sel_set_rec_lock(rec, index, offsets, prebuilt->select_lock_type, @@ -3771,16 +3796,18 @@ wrong_offs: is a non-delete marked record, then it is enough to lock its existence with LOCK_REC_NOT_GAP. */ - /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e., next-key locking is + /* If innodb_locks_unsafe_for_binlog option is used + or this session is using a READ COMMITED isolation + level we lock only the record, i.e., next-key locking is not used. */ ulint lock_type; if (!set_also_gap_locks - || srv_locks_unsafe_for_binlog - || (unique_search && !UNIV_UNLIKELY( - rec_get_deleted_flag(rec, comp)))) { + || srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED + || (unique_search + && !UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp)))) { goto no_gap_lock; } else { @@ -3939,9 +3966,10 @@ no_gap_lock: /* The record is delete-marked: we can skip it */ - if (srv_locks_unsafe_for_binlog - && prebuilt->select_lock_type != LOCK_NONE - && !did_semi_consistent_read) { + if ((srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) + && prebuilt->select_lock_type != LOCK_NONE + && !did_semi_consistent_read) { /* No need to keep a lock on a delete-marked record if we do not want to use next-key locking. */ @@ -3992,8 +4020,9 @@ requires_clust_rec: /* The record is delete marked: we can skip it */ - if (srv_locks_unsafe_for_binlog - && prebuilt->select_lock_type != LOCK_NONE) { + if ((srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) + && prebuilt->select_lock_type != LOCK_NONE) { /* No need to keep a lock on a delete-marked record if we do not want to use next-key @@ -4113,8 +4142,9 @@ next_rec: } did_semi_consistent_read = FALSE; - if (UNIV_UNLIKELY(srv_locks_unsafe_for_binlog) - && prebuilt->select_lock_type != LOCK_NONE) { + if (UNIV_UNLIKELY(srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) + && prebuilt->select_lock_type != LOCK_NONE) { trx_reset_new_rec_lock_info(trx); } @@ -4202,7 +4232,11 @@ lock_wait_or_error: sel_restore_position_for_mysql(&same_user_rec, BTR_SEARCH_LEAF, pcur, moves_up, &mtr); - if (srv_locks_unsafe_for_binlog && !same_user_rec) { + + if ((srv_locks_unsafe_for_binlog + || trx->isolation_level == TRX_ISO_READ_COMMITTED) + && !same_user_rec) { + /* Since we were not able to restore the cursor on the same user record, we cannot use row_unlock_for_mysql() to unlock any records, and