Bug #60049 Clarify the comments in row_ins_must_modify

Submitted: 9 Feb 2011 13:46 Modified: 15 Oct 2012 14:03
Reporter: Marko Mäkelä Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S3 (Non-critical)
Version:3.23.53,4.0,4.1,5.0,5.1,5.5 OS:Any
Assigned to: Marko Mäkelä CPU Architecture:Any
Tags: innodb, purge

[9 Feb 2011 13:46] Marko Mäkelä
Description:
This is forked from Bug #59622.

The function row_rename_table_for_mysql() leaves un-purgeable garbage records in DICT_TABLE_IDS_ID, the unique index on SYS_TABLES.ID. This could affect all updates of unique secondary indexes.

How to repeat:
Create a database from the scratch.

CREATE TABLE t(a INT)ENGINE=InnoDB;
RENAME TABLE t TO u;
DROP TABLE u;
SET GLOBAL innodb_fast_shutdown=0;
-- shut down (ctrl-\ or SIGQUIT)

dd ibs=16384 skip=9 count=1 if=ibdata1 of=page.bin

See that the record chain on the page is as follows:

infimum, SYS_FOREIGN, SYS_FOREIGN_COLS, test/t, test/u, supremum

The delete-marked test/t and test/u should not be linked there. The next-record pointer of SYS_FOREIGN_COLS (at byte offset 158) should point to the supremum (0x0074) instead of the delete-marked test/t (0x00c0).

Suggested fix:
=== modified file 'storage/innobase/row/row0ins.c'
--- storage/innobase/row/row0ins.c	revid:marko.makela@oracle.com-20110208121614-jk53j7052eb7t9rf
+++ storage/innobase/row/row0ins.c	2011-02-09 12:26:05 +0000
@@ -1935,14 +1935,13 @@ record so that the intended insert of th
 the existing record. In the case of a clustered index, the prefix must be
 n_unique fields long, and in the case of a secondary index, all fields must be
 equal.
-@return 0 if no update, ROW_INS_PREV if previous should be updated;
-currently we do the search so that only the low_match record can match
-enough to the search tuple, not the next record */
+@return 0 if no update; ROW_INS_PREV if previous should be updated,
+ROW_INS_NEXT if next should be updated */
 UNIV_INLINE
 ulint
 row_ins_must_modify(
 /*================*/
-	btr_cur_t*	cursor)	/*!< in: B-tree cursor */
+	btr_cur_t*	cursor)	/*!< in/out: B-tree cursor */
 {
 	ulint	enough_match;
 	rec_t*	rec;
@@ -1966,6 +1965,16 @@ row_ins_must_modify(
 		}
 	}
 
+	if (cursor->up_match >= enough_match) {
+
+		rec = page_rec_get_next(btr_cur_get_rec(cursor));
+
+		if (!page_rec_is_supremum(rec)) {
+
+			return(ROW_INS_NEXT);
+		}
+	}
+
 	return(0);
 }
[9 Feb 2011 15:16] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/130887
[9 Feb 2011 15:16] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/130888
[10 Feb 2011 11:52] Marko Mäkelä
I was uncertain if the shutdown_server 10 in my test case works as I intended. Andrei Elkin enlightened me that the directive is implemented in client/mysqltest.cc, do_shutdown_server(). It does ensure that the server is killed, but it can fail (kill -KILL the server) if the specified timeout is exceeded. Because other tests in mysql-test seem to be using 10 as the timeout value, I guess that it is safe enough. If it starts failing nondeterministically on a busy server, the timeout can be increased.
[10 Feb 2011 13:29] Marko Mäkelä
Heikki says that there is a general rule not to update secondary index records in place, other than clearing or setting the delete-mark flag.

This is a special case where we would be updating a non-indexed column in a unique secondary index. There could be some record locking considerations.

There is a small possibility that there is a bug in the purge that only affects system tables. I will check if this bug is repeatable for user tables.
[10 Feb 2011 13:48] Marko Mäkelä
The issue does not seem to affect user tables. I just tested with this in the built-in InnoDB of MySQL 5.1:

CREATE TABLE t(a INT PRIMARY KEY,b INT NOT NULL UNIQUE)ENGINE=InnoDB;
INSERT INTO t VALUES(1,2);
UPDATE t SET a=2;
COMMIT;
SET GLOBAL innodb_fast_shutdown=0;

-- shutdown, and inspect the file t.ibd

The unique secondary index page contained infimum, supremum, (b=2,a=1) [deleted], (b=2,a=2). They were linked like this: infimum, (b=2,a=2), supremum. The record (b=2,a=1) was in the free list. That is, the record (1,2) that was delete-marked by the UPDATE was purged.

To be on the safe side, we should change the updates of non-key columns of unique secondary index records to be in place, but instead find out why the purge is not processing the records in DICT_TABLE_IDS_ID a.k.a. ID_IND on SYS_TABLES(ID). This is the only unique index in the system tables.
[10 Feb 2011 14:40] Marko Mäkelä
Sorry, there is no bug after all. It looks like I did not wait long enough for the purge to run.

I set a breakpoint on row_purge_remove_sec_if_poss(), and it succeeded for both test/t and test/u. The test case in my commit does pass even without the patch. If I change the parameter innodb_fast_shutdown=0 to 1 or 2, the test will fail.

Anyway, I would like to fix the comments in row_ins_must_modify() and remove the unused constant ROW_INS_NEXT and the dead code that checks for it. That would be a non-functional change. The test case could be used for regression testing (in case bugs appear in the purge system).
[15 Oct 2012 14:03] Erlend Dahl
Fixed in 5.6.2

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.044 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.