Bug #59410 read uncommitted: unlock row could not find a 3 mode lock on the record
Submitted: 11 Jan 2011 5:52 Modified: 5 Apr 2011 9:00
Reporter: Shane Bester (Platinum Quality Contributor) Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S2 (Serious)
Version:5.6.2 OS:Any
Assigned to: Vasil Dimov CPU Architecture:Any
Tags: regression

[11 Jan 2011 5:52] Shane Bester
Description:
with subquery and 'for update' clause with read uncommitted, innodb prints this in the error log:

unlock row could not find a 3 mode lock on the record.

How to repeat:
start server with --transaction-isolation=read-uncommitted and run:

drop table if exists `t1`;
create table `t1`(`a` int)engine=innodb;
insert into `t1` values (1),(2),(3);
select 1 from `t1` where `a` <> any (
select 1 from `t1` where `a` <> 1 for update)
for update;

Suggested fix:
Fix the root cause.

Please print the SQL statement that caused the error in future - this is not easy to debug on customer machines.  on own machine i had to breakpoint in gdb.
[11 Jan 2011 5:52] MySQL Verification Team
doesn't happen in 5.5.9 so this is a new regression in mysql-trunk
[11 Jan 2011 7:12] Valeriy Kravchuk
Verified on 32-bit Ubuntu 10.04:

openxs@ubuntu:~/dbs/trunk$ bin/mysql --no-defaults -uroot testReading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.2-m5-valgrind-max-debug Source distribution

Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show variables like '%isolation%';
+---------------+------------------+
| Variable_name | Value            |
+---------------+------------------+
| tx_isolation  | READ-UNCOMMITTED |
+---------------+------------------+
1 row in set (0.00 sec)

mysql> drop table if exists `t1`;
Query OK, 0 rows affected (0.03 sec)

mysql> create table `t1`(`a` int)engine=innodb;
Query OK, 0 rows affected (0.37 sec)

mysql> insert into `t1` values (1),(2),(3);
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select 1 from `t1` where `a` <> any (
    -> select 1 from `t1` where `a` <> 1 for update)
    -> for update;
+---+
| 1 |
+---+
| 1 |
| 1 |
+---+
2 rows in set (0.11 sec)

mysql> exit
Bye
openxs@ubuntu:~/dbs/trunk$ tail data/ubuntu.err 
110111  9:09:45 InnoDB: Mutexes and rw_locks use GCC atomic builtins
110111  9:09:45 InnoDB: Compressed tables use zlib 1.2.3
110111  9:09:46 InnoDB: Initializing buffer pool, size = 128.0M
110111  9:09:47 InnoDB: Completed initialization of buffer pool
110111  9:09:47 InnoDB: highest supported file format is Barracuda.
110111  9:09:48 InnoDB: 1.2.1 started; log sequence number 2177805
110111  9:09:49 [Note] Event Scheduler: Loaded 0 events
110111  9:09:49 [Note] /home/openxs/dbs/trunk/bin/mysqld: ready for connections.
Version: '5.6.2-m5-valgrind-max-debug'  socket: '/tmp/mysql.sock'  port: 3306  Source distribution
110111  9:10:44  InnoDB: Error: unlock row could not find a 3 mode lock on the record
[11 Jan 2011 12:53] Marko Mäkelä
Yes, we can easily print the current statement in lock_rec_unlock(), with something like this:

	size_t	stmt_len;
…
	/* Ignore stmt_len, because it should not hurt to see more
	context (the tail of a multi-statement) in the error message. */
	ut_print_timestamp(stderr);
	fprintf(stderr,
		"  InnoDB: Error: unlock row could not"
		" find a %lu mode lock on the record\n"
		"InnoDB: current statement: %s\n",
		(ulong) lock_mode,
		innobase_get_stmt(trx->mysql_thd, &stmt_len));

I did not succeed in tracking down the cause yet. There apparently are two or three row_prebuilt_t structs at play. I have to work backwards, try to figure out what causes the prebuilt->new_rec_locks to be set and where the lock is disappearing. This should be a simple bug, because the table is constant during the query.
[14 Jan 2011 23:11] MySQL Verification Team
Another testcase with different lock mode error in mysql-trunk:

drop table if exists  g67;
create table g67(`a` char(1),`b` int)engine=innodb;
insert into g67 values('0',0);
set transaction isolation level read uncommitted;
start transaction;
set @a=(select b from g67 where 
(select 1 from g67 where a group by @a=b) 
group by @a:=b);

110115  1:10:36  InnoDB: Error: unlock row could not find a 2 mode lock on the record
[16 Jan 2011 21:22] Marko Mäkelä
I tried the latter test case with and without

set optimizer_switch='index_condition_pushdown=off,mrr=off,mrr_cost_based=off';

and it triggered the warning in both cases.
[30 Jan 2011 16:59] Bugs System
Pushed into mysql-trunk 5.6.2 (revid:vasil.dimov@oracle.com-20110130165639-1pr3opz839b98q5j) (version source revid:vasil.dimov@oracle.com-20110130165522-m0o6al0pn5ig9kv3) (merge vers: 5.6.2) (pib:24)
[28 Mar 2011 18:01] Vasil Dimov
During

select 1 from `t2` where `a` <> any (
select 1 from `t2` where `a` <> 1 for update)
for update;

on unpatched mysql-trunk-innodb as of vasil.dimov@oracle.com-20110328091558-szb1uh03isp9fhxv from all places where DB_SUCCESS_LOCKED_REC is returned only this one is executed:

2162 static
2163 enum db_err
2164 lock_rec_lock(
...
2190         /* We try a simplified and faster subroutine for the most
2191         common cases */
2192         switch (lock_rec_lock_fast(impl, mode, block, heap_no, index, thr)) {
2193         case LOCK_REC_SUCCESS:
2194                 return(DB_SUCCESS);
2195         case LOCK_REC_SUCCESS_CREATED:
2196                 return(DB_SUCCESS_LOCKED_REC);

the breakpoint gets hit 10 times, will attach gdb output shortly
[28 Mar 2011 18:01] Vasil Dimov
gdb output

Attachment: gdboutput.txt (text/plain), 18.57 KiB.

[5 Apr 2011 9:00] Vasil Dimov
Fixed in vasil.dimov@oracle.com-20110405083646-6rfog7rdnp3r5efc
[5 Jan 2012 21:02] John Russell
Added to changelog:

Corrected a condition that produced an InnoDB message in the error
log, unlock row could not find a 3 mode lock on the record. This
situation could occur with a combination of a subquery and a FOR
UPDATE clause under the READ UNCOMMITTED isolation level. The fix
also improves the debuggability of such messages by including the
original SQL statements that caused them.