From a7316cba00b420afc4de1f4769f161621e7ef71a Mon Sep 17 00:00:00 2001
From: inaam <Unknown>
Date: Thu, 21 Jan 2010 17:58:36 +0000
Subject: [PATCH] branches/innodb+: Merge revisions 6448:6504 from
 branches/zip:

  ------------------------------------------------------------------------
  r6449 | marko | 2010-01-13 15:38:53 -0500 (Wed, 13 Jan 2010) | 18 lines

  branches/zip: lock_rec_validate_page(): Only validate the record
  queues when the thread is not holding a space->latch.

  When UNIV_DEBUG is defined while UNIV_SYNC_DEBUG is not,
  latching order violations will still occur and deadlocks will be possible.

  sync_thread_levels_nonempty_gen(): Renamed from
  sync_thread_levels_empty_gen().  Return the violating latch or NULL
  instead of FALSE or TRUE, except that there will be a ut_error before
  the non-NULL return.

  sync_thread_levels_empty_gen(): A macro that negates the return value of
  sync_thread_levels_nonempty_gen().

  sync_thread_levels_contains(): New function, based on
  sync_thread_levels_nonempty_gen().

  This should fix Issue #441.
  ------------------------------------------------------------------------
  r6463 | marko | 2010-01-14 08:43:37 -0500 (Thu, 14 Jan 2010) | 5 lines

  branches/zip: page_copy_rec_list_end(), page_copy_rec_list_start():
  Update PAGE_MAX_TRX_ID before attempting to compress the page.  This
  fixes Issue #382 (a debug assertion failure in page_zip_reorganize())
  and reduces the generated redo log.  There was no bug or crash in
  non-debug builds.
  ------------------------------------------------------------------------
  r6467 | inaam | 2010-01-14 13:46:00 -0500 (Thu, 14 Jan 2010) | 10 lines

  branches/zip rb://226

  log_sys->written_to_all_lsn does not accurately represent the LSN
  upto which write and flush has taken place. Under a race condition
  it can fall behind log_sys->flushed_to_disk_lsn which is accurate.
  Besides written_to_all_lsn is redundant as currently InnoDB supports
  only one log group.

  Approved by: Heikki

  ------------------------------------------------------------------------
  r6472 | calvin | 2010-01-15 18:53:47 -0500 (Fri, 15 Jan 2010) | 12 lines

  branches/zip: Merge revisions 6425:6471 from branches/5.1
  to pick up the first part fix of bug49396.

      ------------------------------------------------------------------------
      r6471 | calvin | 2010-01-15 17:43:27 -0600 (Fri, 15 Jan 2010) | 4 lines

      branches/5.1: fix bug#49396: main.innodb test fails in embedded mode

      Change replace_result by using $MYSQLD_DATADIR. Tested in both embedded
      mode and normal server mode.
      ------------------------------------------------------------------------

  ------------------------------------------------------------------------
  r6473 | calvin | 2010-01-15 18:58:16 -0500 (Fri, 15 Jan 2010) | 6 lines

  branches/zip: fix bug#49396: innodb.innodb-index test fails in
  embedded mode

  This is 2nd part of the fix for bug#49396. The 1st part is
  innodb.test. Tested in both embedded mode and normal server mode.

  ------------------------------------------------------------------------
  r6498 | marko | 2010-01-21 04:22:52 -0500 (Thu, 21 Jan 2010) | 15 lines

  branches/zip: buf_page_get_gen(): Obey recv_no_ibuf_operations
  and do not call ibuf_merge_or_delete_for_page() in crash recovery,
  before the redo log has been applied.
  This could cure some hard-to-repeat, hard-to-explain bugs
  related to secondary indexes.

  A possible recipe to repeat the bug:

  1. update a secondary index leaf page on a compressed table
  2. evict the page from the buffer pool while it is still dirty
  3. ibuf_insert() something for the page
  4. crash
  5. crash recovery; ibuf merge would be done too early,
  before applying redo log to the sec index page or the ibuf pages

  ------------------------------------------------------------------------
---
 ChangeLog                    |  6 ++++
 buf/buf0buf.c                |  2 +-
 include/log0log.h            | 12 ++++++-
 include/sync0sync.h          | 21 +++++++++---
 lock/lock0lock.c             |  7 ++++
 log/log0log.c                |  2 +-
 mysql-test/innodb-index.test |  6 +++-
 mysql-test/innodb.test       |  6 ++--
 page/page0page.c             | 34 +++++++++++--------
 sync/sync0sync.c             | 66 +++++++++++++++++++++++++++++++-----
 10 files changed, 129 insertions(+), 33 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index c0ad21cd13..45f99504b6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2010-01-21	The InnoDB Team
+
+	* buf/buf0buf.c:
+	Do not merge buffered inserts to compressed pages before
+	the redo log has been applied in crash recovery.
+
 2010-01-13	The InnoDB Team
 
 	* row/row0sel.c:
diff --git a/buf/buf0buf.c b/buf/buf0buf.c
index 2e44bc89ca..100f7ed20a 100644
--- a/buf/buf0buf.c
+++ b/buf/buf0buf.c
@@ -2311,7 +2311,7 @@ wait_until_unfixed:
 		while not holding buf_pool_mutex or block->mutex. */
 		success = buf_zip_decompress(block, srv_use_checksums);
 
-		if (UNIV_LIKELY(success)) {
+		if (UNIV_LIKELY(success && !recv_no_ibuf_operations)) {
 			ibuf_merge_or_delete_for_page(block, space, offset,
 						      zip_size, TRUE);
 		}
diff --git a/include/log0log.h b/include/log0log.h
index 135aeb69e2..233714eb63 100644
--- a/include/log0log.h
+++ b/include/log0log.h
@@ -825,7 +825,17 @@ struct log_struct{
 					written to some log group; for this to
 					be advanced, it is enough that the
 					write i/o has been completed for all
-					log groups */
+					log groups.
+					Note that since InnoDB currently
+					has only one log group therefore
+					this value is redundant. Also it
+					is possible that this value
+					falls behind the
+					flushed_to_disk_lsn transiently.
+					It is appropriate to use either
+					flushed_to_disk_lsn or
+					write_lsn which are always
+					up-to-date and accurate. */
 	ib_uint64_t	write_lsn;	/*!< end lsn for the current running
 					write */
 	ulint		write_end_offset;/*!< the data in buffer has
diff --git a/include/sync0sync.h b/include/sync0sync.h
index 92f9415f15..7b39e08d6f 100644
--- a/include/sync0sync.h
+++ b/include/sync0sync.h
@@ -238,16 +238,27 @@ ibool
 sync_thread_levels_empty(void);
 /*==========================*/
 /******************************************************************//**
-Checks that the level array for the current thread is empty.
-@return	TRUE if empty except the exceptions specified below */
+Checks if the level array for the current thread contains a
+mutex or rw-latch at the specified level.
+@return	a matching latch, or NULL if not found */
 UNIV_INTERN
-ibool
-sync_thread_levels_empty_gen(
-/*=========================*/
+void*
+sync_thread_levels_contains(
+/*========================*/
+	ulint	level);			/*!< in: latching order level
+					(SYNC_DICT, ...)*/
+/******************************************************************//**
+Checks if the level array for the current thread is empty.
+@return	a latch, or NULL if empty except the exceptions specified below */
+UNIV_INTERN
+void*
+sync_thread_levels_nonempty_gen(
+/*============================*/
 	ibool	dict_mutex_allowed);	/*!< in: TRUE if dictionary mutex is
 					allowed to be owned by the thread,
 					also purge_is_running mutex is
 					allowed */
+#define sync_thread_levels_empty_gen(d) (!sync_thread_levels_nonempty_gen(d))
 /******************************************************************//**
 Gets the debug information for a reserved mutex. */
 UNIV_INTERN
diff --git a/lock/lock0lock.c b/lock/lock0lock.c
index 3db7c2b36b..0fa67d3171 100644
--- a/lock/lock0lock.c
+++ b/lock/lock0lock.c
@@ -4766,6 +4766,13 @@ loop:
 	     || lock->trx->conc_state == TRX_PREPARED
 	     || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY);
 
+# ifdef UNIV_SYNC_DEBUG
+	/* Only validate the record queues when this thread is not
+	holding a space->latch.  Deadlocks are possible due to
+	latching order violation when UNIV_DEBUG is defined while
+	UNIV_SYNC_DEBUG is not. */
+	if (!sync_thread_levels_contains(SYNC_FSP))
+# endif /* UNIV_SYNC_DEBUG */
 	for (i = nth_bit; i < lock_rec_get_n_bits(lock); i++) {
 
 		if (i == 1 || lock_rec_get_nth_bit(lock, i)) {
diff --git a/log/log0log.c b/log/log0log.c
index 86c9f9b413..063581055e 100644
--- a/log/log0log.c
+++ b/log/log0log.c
@@ -2013,7 +2013,7 @@ log_checkpoint(
 		return(TRUE);
 	}
 
-	ut_ad(log_sys->written_to_all_lsn >= oldest_lsn);
+	ut_ad(log_sys->flushed_to_disk_lsn >= oldest_lsn);
 
 	if (log_sys->n_pending_checkpoint_writes > 0) {
 		/* A checkpoint write is running */
diff --git a/mysql-test/innodb-index.test b/mysql-test/innodb-index.test
index 5b229f83bc..b0477e2f54 100644
--- a/mysql-test/innodb-index.test
+++ b/mysql-test/innodb-index.test
@@ -1,5 +1,7 @@
 -- source include/have_innodb.inc
 
+let $MYSQLD_DATADIR= `select @@datadir`;
+
 let $innodb_file_format_check_orig=`select @@innodb_file_format_check`;
 
 create table t1(a int not null, b int, c char(10) not null, d varchar(20)) engine = innodb;
@@ -136,7 +138,9 @@ delete from t1;
 --error ER_CANT_DROP_FIELD_OR_KEY
 drop index dc on t4;
 # there is no foreign key dc on t3
---replace_regex /'\.\/test\/#sql2-[0-9a-f-]*'/'#sql2-temporary'/
+--replace_regex /'[^']*test\/#sql2-[0-9a-f-]*'/'#sql2-temporary'/
+# Embedded server doesn't chdir to data directory
+--replace_result $MYSQLD_DATADIR ./ master-data/ ''
 --error ER_ERROR_ON_RENAME
 alter table t3 drop foreign key dc;
 alter table t4 drop foreign key dc;
diff --git a/mysql-test/innodb.test b/mysql-test/innodb.test
index fe58831653..aa824685b1 100644
--- a/mysql-test/innodb.test
+++ b/mysql-test/innodb.test
@@ -15,6 +15,8 @@
 
 -- source include/have_innodb.inc
 
+let $MYSQLD_DATADIR= `select @@datadir`;
+
 # Save the original values of some variables in order to be able to
 # estimate how much they have changed during the tests. Previously this
 # test assumed that e.g. rows_deleted is 0 here and after deleting 23
@@ -1700,7 +1702,7 @@ set foreign_key_checks=0;
 create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1;
 create table t3(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=utf8;
 # Embedded server doesn't chdir to data directory
---replace_result $MYSQLTEST_VARDIR . master-data/ ''
+--replace_result $MYSQLD_DATADIR ./ master-data/ ''
 -- error 1025
 rename table t3 to t1;
 set foreign_key_checks=1;
@@ -2340,7 +2342,7 @@ ALTER TABLE t2 ADD FOREIGN KEY (a) REFERENCES t1 (a) ON DELETE SET NULL;
 # mysqltest first does replace_regex, then replace_result
 --replace_regex /'[^']*test\/#sql-[0-9a-f_]*'/'#sql-temporary'/
 # Embedded server doesn't chdir to data directory
---replace_result $MYSQLTEST_VARDIR . master-data/ ''
+--replace_result $MYSQLD_DATADIR ./ master-data/ ''
 --error 1025
 ALTER TABLE t2 MODIFY a INT NOT NULL;
 DELETE FROM t1;
diff --git a/page/page0page.c b/page/page0page.c
index ab2ba60570..17c40170e1 100644
--- a/page/page0page.c
+++ b/page/page0page.c
@@ -658,6 +658,14 @@ page_copy_rec_list_end(
 						index, mtr);
 	}
 
+	/* Update PAGE_MAX_TRX_ID on the uncompressed page.
+	Modifications will be redo logged and copied to the compressed
+	page in page_zip_compress() or page_zip_reorganize() below. */
+	if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) {
+		page_update_max_trx_id(new_block, NULL,
+				       page_get_max_trx_id(page), mtr);
+	}
+
 	if (UNIV_LIKELY_NULL(new_page_zip)) {
 		mtr_set_log_mode(mtr, log_mode);
 
@@ -696,15 +704,10 @@ page_copy_rec_list_end(
 		}
 	}
 
-	/* Update the lock table, MAX_TRX_ID, and possible hash index */
+	/* Update the lock table and possible hash index */
 
 	lock_move_rec_list_end(new_block, block, rec);
 
-	if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) {
-		page_update_max_trx_id(new_block, new_page_zip,
-				       page_get_max_trx_id(page), mtr);
-	}
-
 	btr_search_move_or_delete_hash_entries(new_block, block, index);
 
 	return(ret);
@@ -772,6 +775,16 @@ page_copy_rec_list_start(
 		mem_heap_free(heap);
 	}
 
+	/* Update PAGE_MAX_TRX_ID on the uncompressed page.
+	Modifications will be redo logged and copied to the compressed
+	page in page_zip_compress() or page_zip_reorganize() below. */
+	if (dict_index_is_sec_or_ibuf(index)
+	    && page_is_leaf(page_align(rec))) {
+		page_update_max_trx_id(new_block, NULL,
+				       page_get_max_trx_id(page_align(rec)),
+				       mtr);
+	}
+
 	if (UNIV_LIKELY_NULL(new_page_zip)) {
 		mtr_set_log_mode(mtr, log_mode);
 
@@ -809,14 +822,7 @@ page_copy_rec_list_start(
 		}
 	}
 
-	/* Update MAX_TRX_ID, the lock table, and possible hash index */
-
-	if (dict_index_is_sec_or_ibuf(index)
-	    && page_is_leaf(page_align(rec))) {
-		page_update_max_trx_id(new_block, new_page_zip,
-				       page_get_max_trx_id(page_align(rec)),
-				       mtr);
-	}
+	/* Update the lock table and possible hash index */
 
 	lock_move_rec_list_start(new_block, block, rec, ret);
 
diff --git a/sync/sync0sync.c b/sync/sync0sync.c
index 01c809ec1f..44f1cba216 100644
--- a/sync/sync0sync.c
+++ b/sync/sync0sync.c
@@ -957,13 +957,63 @@ sync_thread_levels_contain(
 	return(FALSE);
 }
 
+/******************************************************************//**
+Checks if the level array for the current thread contains a
+mutex or rw-latch at the specified level.
+@return	a matching latch, or NULL if not found */
+UNIV_INTERN
+void*
+sync_thread_levels_contains(
+/*========================*/
+	ulint	level)			/*!< in: latching order level
+					(SYNC_DICT, ...)*/
+{
+	sync_level_t*	arr;
+	sync_thread_t*	thread_slot;
+	sync_level_t*	slot;
+	ulint		i;
+
+	if (!sync_order_checks_on) {
+
+		return(NULL);
+	}
+
+	mutex_enter(&sync_thread_mutex);
+
+	thread_slot = sync_thread_level_arrays_find_slot();
+
+	if (thread_slot == NULL) {
+
+		mutex_exit(&sync_thread_mutex);
+
+		return(NULL);
+	}
+
+	arr = thread_slot->levels;
+
+	for (i = 0; i < SYNC_THREAD_N_LEVELS; i++) {
+
+		slot = sync_thread_levels_get_nth(arr, i);
+
+		if (slot->latch != NULL && slot->level == level) {
+
+			mutex_exit(&sync_thread_mutex);
+			return(slot->latch);
+		}
+	}
+
+	mutex_exit(&sync_thread_mutex);
+
+	return(NULL);
+}
+
 /******************************************************************//**
 Checks that the level array for the current thread is empty.
-@return	TRUE if empty except the exceptions specified below */
+@return	a latch, or NULL if empty except the exceptions specified below */
 UNIV_INTERN
-ibool
-sync_thread_levels_empty_gen(
-/*=========================*/
+void*
+sync_thread_levels_nonempty_gen(
+/*============================*/
 	ibool	dict_mutex_allowed)	/*!< in: TRUE if dictionary mutex is
 					allowed to be owned by the thread,
 					also purge_is_running mutex is
@@ -976,7 +1026,7 @@ sync_thread_levels_empty_gen(
 
 	if (!sync_order_checks_on) {
 
-		return(TRUE);
+		return(NULL);
 	}
 
 	mutex_enter(&sync_thread_mutex);
@@ -987,7 +1037,7 @@ sync_thread_levels_empty_gen(
 
 		mutex_exit(&sync_thread_mutex);
 
-		return(TRUE);
+		return(NULL);
 	}
 
 	arr = thread_slot->levels;
@@ -1004,13 +1054,13 @@ sync_thread_levels_empty_gen(
 			mutex_exit(&sync_thread_mutex);
 			ut_error;
 
-			return(FALSE);
+			return(slot->latch);
 		}
 	}
 
 	mutex_exit(&sync_thread_mutex);
 
-	return(TRUE);
+	return(NULL);
 }
 
 /******************************************************************//**
-- 
2.30.9