[Commits] 7a4281d1b1b: MDEV-13424: InnoDB: Assertion failure in thread 139986168802048 in file row0mysql.cc line 691

jan jan.lindstrom at mariadb.com
Fri Aug 11 08:04:30 EEST 2017


revision-id: 7a4281d1b1b30c1ea76ec557a4a7f0c2e8d64e82 (mariadb-10.1.26-3-g7a4281d1b1b)
parent(s): 1ff65c271ff3edef5ccba72141047fda19170163
author: Jan Lindström
committer: Jan Lindström
timestamp: 2017-08-11 07:57:03 +0300
message:

MDEV-13424: InnoDB: Assertion failure in thread 139986168802048 in file row0mysql.cc line 691

row_mysql_handle_errors missed error handling for DB_MISSING_HISTORY
that is possible if index is not yet usable or transaction failed
to find version of the row needed for query.

row_mysql_handle_errors
	Add prebuilt to function parameters and add error handling
	for DB_MISSING_HISTORY error.

row_lock_table_autoinc_for_mysql
row_insert_for_mysql
row_update_for_mysql
row_drop_table_for_mysql
row_search_for_mysql
	Update row_mysql_handle_errors call to contain prebuilt
	if available and process return value.

row_vers_build_for_consistent_read
	If looked row version is not found (i.e. DB_MISSING_HISTORY)
	print error message and records.

No test case found.

---
 storage/innobase/include/row0mysql.h | 30 ++++++++++------
 storage/innobase/row/row0mysql.cc    | 66 +++++++++++++++++++++++++----------
 storage/innobase/row/row0sel.cc      |  9 ++++-
 storage/innobase/row/row0vers.cc     | 24 +++++++++++++
 storage/xtradb/include/row0mysql.h   | 30 ++++++++++------
 storage/xtradb/row/row0mysql.cc      | 67 ++++++++++++++++++++++++++----------
 storage/xtradb/row/row0sel.cc        |  9 ++++-
 storage/xtradb/row/row0vers.cc       | 24 +++++++++++++
 8 files changed, 199 insertions(+), 60 deletions(-)

diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h
index 71e3b9bb19e..bc1744038bd 100644
--- a/storage/innobase/include/row0mysql.h
+++ b/storage/innobase/include/row0mysql.h
@@ -154,21 +154,29 @@ row_mysql_store_col_in_innobase_format(
 					payload data; if the column is a true
 					VARCHAR then this is irrelevant */
 	ulint		comp);		/*!< in: nonzero=compact format */
-/****************************************************************//**
+/**
 Handles user errors and lock waits detected by the database engine.
- at return true if it was a lock wait and we should continue running the
-query thread */
+return true if it was a lock wait and we should continue running the
+query thread and in that case the thr is ALREADY in the running state.
+ at param[out]	err	possible new error encountered in
+			lock wait, or if no new error, the value
+			of trx->error_state at the entry of this
+			function
+ at param[in]	trx	Transaction
+ at param[in]	thr	Query thread, or NULL
+ at param[in]	savept	Savepoint or NULL
+ at param[in]	prebuilt Prebuild used or NULL
+ at retval true if it was a lock wait, false if not */
 UNIV_INTERN
 bool
 row_mysql_handle_errors(
-/*====================*/
-	dberr_t*	new_err,/*!< out: possible new error encountered in
-				rollback, or the old error which was
-				during the function entry */
-	trx_t*		trx,	/*!< in: transaction */
-	que_thr_t*	thr,	/*!< in: query thread, or NULL */
-	trx_savept_t*	savept)	/*!< in: savepoint, or NULL */
-	MY_ATTRIBUTE((nonnull(1,2)));
+	dberr_t*	new_err,
+	trx_t*		trx,
+	que_thr_t*	thr,
+	trx_savept_t*	savept,
+	const row_prebuilt_t* prebuilt = NULL)
+	MY_ATTRIBUTE((warn_unused_result));
+
 /********************************************************************//**
 Create a prebuilt struct for a MySQL table handle.
 @return	own: a prebuilt struct */
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index 1d64d66cd49..d55e76b1b67 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -578,21 +578,27 @@ row_mysql_convert_row_to_innobase(
 	}
 }
 
-/****************************************************************//**
+/**
 Handles user errors and lock waits detected by the database engine.
- at return true if it was a lock wait and we should continue running the
-query thread and in that case the thr is ALREADY in the running state. */
+return true if it was a lock wait and we should continue running the
+query thread and in that case the thr is ALREADY in the running state.
+ at param[out]	err	possible new error encountered in
+			lock wait, or if no new error, the value
+			of trx->error_state at the entry of this
+			function
+ at param[in]	trx	Transaction
+ at param[in]	thr	Query thread, or NULL
+ at param[in]	savept	Savepoint or NULL
+ at param[in]	prebuilt Prebuild
+ at retval true if it was a lock wait, false if not */
 UNIV_INTERN
 bool
 row_mysql_handle_errors(
-/*====================*/
-	dberr_t*	new_err,/*!< out: possible new error encountered in
-				lock wait, or if no new error, the value
-				of trx->error_state at the entry of this
-				function */
-	trx_t*		trx,	/*!< in: transaction */
-	que_thr_t*	thr,	/*!< in: query thread, or NULL */
-	trx_savept_t*	savept)	/*!< in: savepoint, or NULL */
+	dberr_t*	new_err,
+	trx_t*		trx,
+	que_thr_t*	thr,
+	trx_savept_t*	savept,
+	const row_prebuilt_t* prebuilt)
 {
 	dberr_t	err;
 
@@ -686,6 +692,28 @@ row_mysql_handle_errors(
 			"Please drop excessive foreign constraints"
 			" and try again\n", (ulong) DICT_FK_MAX_RECURSIVE_LOAD);
 		break;
+	case DB_MISSING_HISTORY: {
+		ut_a(prebuilt && prebuilt->index);
+
+		if(!prebuilt->index_usable) {
+			ib_logf(IB_LOG_LEVEL_ERROR,
+				"Index %s for table %s used by query is not yet usable.",
+				prebuilt->index->name, prebuilt->index->table->name);
+
+			ib_push_warning(trx, HA_ERR_INDEX_CORRUPT,
+				"Index %s for table %s used by query is not yet usable.",
+				prebuilt->index->name, prebuilt->index->table->name);
+		} else {
+			ib_logf(IB_LOG_LEVEL_ERROR,
+				"Can't find history for index %s for table %s.",
+				prebuilt->index->name, prebuilt->index->table->name);
+
+			ib_push_warning(trx, HA_ERR_INDEX_CORRUPT,
+				"Can't find history for index %s for table %s.",
+				prebuilt->index->name, prebuilt->index->table->name);
+		}
+		break;
+	}
 	default:
 		fprintf(stderr, "InnoDB: unknown error code %lu\n",
 			(ulong) err);
@@ -1182,7 +1210,7 @@ row_lock_table_autoinc_for_mysql(
 	if (err != DB_SUCCESS) {
 		que_thr_stop_for_mysql(thr);
 
-		was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
+		was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL, prebuilt);
 
 		if (was_lock_wait) {
 			goto run_again;
@@ -1263,7 +1291,7 @@ row_lock_table_for_mysql(
 	if (err != DB_SUCCESS) {
 		que_thr_stop_for_mysql(thr);
 
-		was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
+		was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL, prebuilt);
 
 		if (was_lock_wait) {
 			goto run_again;
@@ -1435,7 +1463,7 @@ row_insert_for_mysql(
 		thr->lock_state = QUE_THR_LOCK_ROW;
 
 		was_lock_wait = row_mysql_handle_errors(
-			&err, trx, thr, &savept);
+			&err, trx, thr, &savept, prebuilt);
 
 		thr->lock_state = QUE_THR_LOCK_NOLOCK;
 
@@ -1861,7 +1889,8 @@ row_update_for_mysql(
 		DEBUG_SYNC(trx->mysql_thd, "row_update_for_mysql_error");
 
 		was_lock_wait = row_mysql_handle_errors(&err, trx, thr,
-							&savept);
+			&savept, prebuilt);
+
 		thr->lock_state= QUE_THR_LOCK_NOLOCK;
 
 		if (was_lock_wait) {
@@ -4484,15 +4513,16 @@ row_drop_table_for_mysql(
 
 		break;
 
-	case DB_OUT_OF_FILE_SPACE:
+	case DB_OUT_OF_FILE_SPACE: {
 		err = DB_MUST_GET_MORE_FILE_SPACE;
 		trx->error_state = err;
-		row_mysql_handle_errors(&err, trx, NULL, NULL);
+		bool lock_wait = row_mysql_handle_errors(&err, trx, NULL, NULL);
 
+		ut_a(!lock_wait);
 		/* raise error */
 		ut_error;
 		break;
-
+	}
 	case DB_TOO_MANY_CONCURRENT_TRXS:
 		/* Cannot even find a free slot for the
 		the undo log. We can directly exit here
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc
index 189c7a7a1d2..2c18b5a7b87 100644
--- a/storage/innobase/row/row0sel.cc
+++ b/storage/innobase/row/row0sel.cc
@@ -3760,6 +3760,13 @@ row_search_for_mysql(
 			return(DB_DECRYPTION_FAILED);
 		}
 	} else if (!prebuilt->index_usable) {
+		size_t length = 0;
+		const char* query = trx->mysql_thd ?
+			innobase_get_stmt((THD*)trx->mysql_thd, &length) : NULL;
+
+		ib_logf(IB_LOG_LEVEL_ERROR,
+			"Index %s in table %s not yet usable in query %s",
+			index->name, index->table->name, query ? query : "NULL");
 
 		return(DB_MISSING_HISTORY);
 
@@ -5201,7 +5208,7 @@ row_search_for_mysql(
 
 	thr->lock_state = QUE_THR_LOCK_ROW;
 
-	if (row_mysql_handle_errors(&err, trx, thr, NULL)) {
+	if (row_mysql_handle_errors(&err, trx, thr, NULL, prebuilt)) {
 		/* It was a lock wait, and it ended */
 
 		thr->lock_state = QUE_THR_LOCK_NOLOCK;
diff --git a/storage/innobase/row/row0vers.cc b/storage/innobase/row/row0vers.cc
index 9dd7b63bcab..bac8590e6f6 100644
--- a/storage/innobase/row/row0vers.cc
+++ b/storage/innobase/row/row0vers.cc
@@ -588,6 +588,30 @@ row_vers_build_for_consistent_read(
 						  *offsets, heap,
 						  &prev_version)
 			? DB_SUCCESS : DB_MISSING_HISTORY;
+
+		if (err != DB_SUCCESS) {
+			trx_id_t rec_trx_id = row_get_rec_trx_id(rec, index, *offsets);
+
+			ib_logf(IB_LOG_LEVEL_ERROR,
+				"Failed to find correct version of the record for index %s "
+				" table %s record trx_id %lu.",
+				index->name, index->table->name, rec_trx_id);
+
+			ib_logf(IB_LOG_LEVEL_INFO,
+				"Looking trx_id between %lu-%lu.", view->low_limit_id,
+				view->up_limit_id);
+
+			rec_print(stderr, rec, index);
+
+			if (rec != version) {
+				rec_trx_id = row_get_rec_trx_id(version, index, *offsets);
+				ib_logf(IB_LOG_LEVEL_INFO,
+					"Previous version of this record has trx_id %lu.",
+					rec_trx_id);
+				rec_print(stderr, version, index);
+			}
+		}
+
 		if (heap2) {
 			mem_heap_free(heap2); /* free version */
 		}
diff --git a/storage/xtradb/include/row0mysql.h b/storage/xtradb/include/row0mysql.h
index a8503a5cfda..ac330b505ef 100644
--- a/storage/xtradb/include/row0mysql.h
+++ b/storage/xtradb/include/row0mysql.h
@@ -156,21 +156,29 @@ row_mysql_store_col_in_innobase_format(
 					payload data; if the column is a true
 					VARCHAR then this is irrelevant */
 	ulint		comp);		/*!< in: nonzero=compact format */
-/****************************************************************//**
+/**
 Handles user errors and lock waits detected by the database engine.
- at return true if it was a lock wait and we should continue running the
-query thread */
+return true if it was a lock wait and we should continue running the
+query thread and in that case the thr is ALREADY in the running state.
+ at param[out]	err	possible new error encountered in
+			lock wait, or if no new error, the value
+			of trx->error_state at the entry of this
+			function
+ at param[in]	trx	Transaction
+ at param[in]	thr	Query thread, or NULL
+ at param[in]	savept	Savepoint or NULL
+ at param[in]	prebuilt Prebuild used or NULL
+ at retval true if it was a lock wait, false if not */
 UNIV_INTERN
 bool
 row_mysql_handle_errors(
-/*====================*/
-	dberr_t*	new_err,/*!< out: possible new error encountered in
-				rollback, or the old error which was
-				during the function entry */
-	trx_t*		trx,	/*!< in: transaction */
-	que_thr_t*	thr,	/*!< in: query thread, or NULL */
-	trx_savept_t*	savept)	/*!< in: savepoint, or NULL */
-	MY_ATTRIBUTE((nonnull(1,2)));
+	dberr_t*	new_err,
+	trx_t*		trx,
+	que_thr_t*	thr,
+	trx_savept_t*	savept,
+	const row_prebuilt_t* prebuilt = NULL)
+	MY_ATTRIBUTE((warn_unused_result));
+
 /********************************************************************//**
 Create a prebuilt struct for a MySQL table handle.
 @return	own: a prebuilt struct */
diff --git a/storage/xtradb/row/row0mysql.cc b/storage/xtradb/row/row0mysql.cc
index 0079fc79a0e..13983e08892 100644
--- a/storage/xtradb/row/row0mysql.cc
+++ b/storage/xtradb/row/row0mysql.cc
@@ -577,21 +577,27 @@ row_mysql_convert_row_to_innobase(
 	}
 }
 
-/****************************************************************//**
+/**
 Handles user errors and lock waits detected by the database engine.
- at return true if it was a lock wait and we should continue running the
-query thread and in that case the thr is ALREADY in the running state. */
+return true if it was a lock wait and we should continue running the
+query thread and in that case the thr is ALREADY in the running state.
+ at param[out]	err	possible new error encountered in
+			lock wait, or if no new error, the value
+			of trx->error_state at the entry of this
+			function
+ at param[in]	trx	Transaction
+ at param[in]	thr	Query thread, or NULL
+ at param[in]	savept	Savepoint or NULL
+ at param[in]	prebuilt Prebuild
+ at retval true if it was a lock wait, false if not */
 UNIV_INTERN
 bool
 row_mysql_handle_errors(
-/*====================*/
-	dberr_t*	new_err,/*!< out: possible new error encountered in
-				lock wait, or if no new error, the value
-				of trx->error_state at the entry of this
-				function */
-	trx_t*		trx,	/*!< in: transaction */
-	que_thr_t*	thr,	/*!< in: query thread, or NULL */
-	trx_savept_t*	savept)	/*!< in: savepoint, or NULL */
+	dberr_t*	new_err,
+	trx_t*		trx,
+	que_thr_t*	thr,
+	trx_savept_t*	savept,
+	const row_prebuilt_t* prebuilt)
 {
 	dberr_t	err;
 
@@ -685,6 +691,28 @@ row_mysql_handle_errors(
 			"Please drop excessive foreign constraints"
 			" and try again\n", (ulong) DICT_FK_MAX_RECURSIVE_LOAD);
 		break;
+	case DB_MISSING_HISTORY: {
+		ut_a(prebuilt && prebuilt->index);
+
+		if(!prebuilt->index_usable) {
+			ib_logf(IB_LOG_LEVEL_ERROR,
+				"Index %s for table %s used by query is not yet usable.",
+				prebuilt->index->name, prebuilt->index->table->name);
+
+			ib_push_warning(trx, HA_ERR_INDEX_CORRUPT,
+				"Index %s for table %s used by query is not yet usable.",
+				prebuilt->index->name, prebuilt->index->table->name);
+		} else {
+			ib_logf(IB_LOG_LEVEL_ERROR,
+				"Can't find history for index %s for table %s.",
+				prebuilt->index->name, prebuilt->index->table->name);
+
+			ib_push_warning(trx, HA_ERR_INDEX_CORRUPT,
+				"Can't find history for index %s for table %s.",
+				prebuilt->index->name, prebuilt->index->table->name);
+		}
+		break;
+	}
 	default:
 		fprintf(stderr, "InnoDB: unknown error code %lu\n",
 			(ulong) err);
@@ -1181,7 +1209,7 @@ row_lock_table_autoinc_for_mysql(
 	if (err != DB_SUCCESS) {
 		que_thr_stop_for_mysql(thr);
 
-		was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
+		was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL, prebuilt);
 
 		if (was_lock_wait) {
 			goto run_again;
@@ -1262,7 +1290,7 @@ row_lock_table_for_mysql(
 	if (err != DB_SUCCESS) {
 		que_thr_stop_for_mysql(thr);
 
-		was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
+		was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL, prebuilt);
 
 		if (was_lock_wait) {
 			goto run_again;
@@ -1439,7 +1467,7 @@ row_insert_for_mysql(
 		thr->lock_state = QUE_THR_LOCK_ROW;
 
 		was_lock_wait = row_mysql_handle_errors(
-			&err, trx, thr, &savept);
+			&err, trx, thr, &savept, prebuilt);
 
 		thr->lock_state = QUE_THR_LOCK_NOLOCK;
 
@@ -1869,7 +1897,8 @@ row_update_for_mysql(
 		DEBUG_SYNC(trx->mysql_thd, "row_update_for_mysql_error");
 
 		was_lock_wait = row_mysql_handle_errors(&err, trx, thr,
-							&savept);
+			&savept, prebuilt);
+
 		thr->lock_state= QUE_THR_LOCK_NOLOCK;
 
 		if (was_lock_wait) {
@@ -4515,16 +4544,18 @@ row_drop_table_for_mysql(
 
 		break;
 
-	case DB_OUT_OF_FILE_SPACE:
+	case DB_OUT_OF_FILE_SPACE: {
 		err = DB_MUST_GET_MORE_FILE_SPACE;
 
 		trx->error_state = err;
-		row_mysql_handle_errors(&err, trx, NULL, NULL);
+		bool lock_wait = row_mysql_handle_errors(&err, trx, NULL, NULL);
+
+		ut_a(!lock_wait);
 
 		/* raise error */
 		ut_error;
 		break;
-
+	}
 	case DB_TOO_MANY_CONCURRENT_TRXS:
 		/* Cannot even find a free slot for the
 		the undo log. We can directly exit here
diff --git a/storage/xtradb/row/row0sel.cc b/storage/xtradb/row/row0sel.cc
index 8e3ed3d1a4e..ccbfc5dd0eb 100644
--- a/storage/xtradb/row/row0sel.cc
+++ b/storage/xtradb/row/row0sel.cc
@@ -3747,6 +3747,13 @@ row_search_for_mysql(
 			return(DB_DECRYPTION_FAILED);
 		}
 	} else if (!prebuilt->index_usable) {
+		size_t length = 0;
+		const char* query = trx->mysql_thd ?
+			innobase_get_stmt((THD*)trx->mysql_thd, &length) : NULL;
+
+		ib_logf(IB_LOG_LEVEL_ERROR,
+			"Index %s in table %s not yet usable in query %s",
+			index->name, index->table->name, query ? query : "NULL");
 
 		return(DB_MISSING_HISTORY);
 
@@ -5189,7 +5196,7 @@ row_search_for_mysql(
 
 	thr->lock_state = QUE_THR_LOCK_ROW;
 
-	if (row_mysql_handle_errors(&err, trx, thr, NULL)) {
+	if (row_mysql_handle_errors(&err, trx, thr, NULL, prebuilt)) {
 		/* It was a lock wait, and it ended */
 
 		thr->lock_state = QUE_THR_LOCK_NOLOCK;
diff --git a/storage/xtradb/row/row0vers.cc b/storage/xtradb/row/row0vers.cc
index 9f1fc13ee09..7df6566b623 100644
--- a/storage/xtradb/row/row0vers.cc
+++ b/storage/xtradb/row/row0vers.cc
@@ -588,6 +588,30 @@ row_vers_build_for_consistent_read(
 						  *offsets, heap,
 						  &prev_version)
 			? DB_SUCCESS : DB_MISSING_HISTORY;
+
+		if (err != DB_SUCCESS) {
+			trx_id_t rec_trx_id = row_get_rec_trx_id(rec, index, *offsets);
+
+			ib_logf(IB_LOG_LEVEL_ERROR,
+				"Failed to find correct version of the record for index %s "
+				" table %s record trx_id %lu.",
+				index->name, index->table->name, rec_trx_id);
+
+			ib_logf(IB_LOG_LEVEL_INFO,
+				"Looking trx_id between %lu-%lu.", view->low_limit_id,
+				view->up_limit_id);
+
+			rec_print(stderr, rec, index);
+
+			if (rec != version) {
+				rec_trx_id = row_get_rec_trx_id(version, index, *offsets);
+				ib_logf(IB_LOG_LEVEL_INFO,
+					"Previous version of this record has trx_id %lu.",
+					rec_trx_id);
+				rec_print(stderr, version, index);
+			}
+		}
+
 		if (heap2) {
 			mem_heap_free(heap2); /* free version */
 		}


More information about the commits mailing list