From 2337d577ea813cc87a342a469fd9a00dafaa326c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@mariadb.com>
Date: Sun, 21 Jan 2018 17:37:39 +0200
Subject: [PATCH 4/4] Follow-up to MDEV-14941

FIXME: Use proper locking for btr_scrub!!!

dict_stats_wait_bg_to_stop_using_table(), dict_stats_stop_bg(),
dict_table_t::stats_bg_flag: Remove. Most conflicts between
statistics and DDL operations can be prevented by transactional
table locks. A record lock conflict on the persistent statistics
tables remains possible and cannot be entirely avoided.

ha_innobase::commit_inplace_alter_table(): When rebuilding a table,
acquire an exclusive table lock on both the old and the new copy
of the table.

row_drop_database_for_mysql(): Simplify the code. Call
row_drop_table_for_mysql() to separately drop each table.

row_truncate_table_for_mysql(): Acquire an exclusive transactional
lock on the table.
---
 storage/innobase/btr/btr0scrub.cc         |  12 +--
 storage/innobase/dict/dict0stats.cc       |   7 +-
 storage/innobase/dict/dict0stats_bg.cc    |  27 +-----
 storage/innobase/handler/ha_innodb.cc     |   5 +-
 storage/innobase/handler/handler0alter.cc |  50 +++--------
 storage/innobase/include/dict0mem.h       |  36 +-------
 storage/innobase/include/dict0stats_bg.h  |  36 --------
 storage/innobase/include/row0merge.h      |   7 +-
 storage/innobase/row/row0merge.cc         |  10 +--
 storage/innobase/row/row0mysql.cc         | 142 ++++--------------------------
 storage/innobase/row/row0trunc.cc         |  22 ++---
 11 files changed, 46 insertions(+), 308 deletions(-)

diff --git a/storage/innobase/btr/btr0scrub.cc b/storage/innobase/btr/btr0scrub.cc
index 376a106bf8a..93a9a0543b1 100644
--- a/storage/innobase/btr/btr0scrub.cc
+++ b/storage/innobase/btr/btr0scrub.cc
@@ -182,10 +182,7 @@ btr_scrub_table_close(
 /*==================*/
 	dict_table_t* table)  /*!< in: table */
 {
-	bool dict_locked = true;
-	bool try_drop = false;
-	table->stats_bg_flag &= ~BG_SCRUB_IN_PROGRESS;
-	dict_table_close(table, dict_locked, try_drop);
+	dict_table_close(table, true, false);
 }
 
 /****************************************************************
@@ -564,10 +561,6 @@ btr_scrub_table_needs_scrubbing(
 	if (table == NULL)
 		return false;
 
-	if (table->stats_bg_flag & BG_STAT_SHOULD_QUIT) {
-		return false;
-	}
-
 	if (table->to_be_dropped) {
 		return false;
 	}
@@ -641,9 +634,6 @@ btr_scrub_get_table_and_index(
 		dict_locked);
 
 	if (table != NULL) {
-		/* mark table as being scrubbed */
-		table->stats_bg_flag |= BG_SCRUB_IN_PROGRESS;
-
 		if (!btr_scrub_table_needs_scrubbing(table)) {
 			btr_scrub_table_close(table);
 			btr_scrub_unlock_dict();
diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc
index e589ee4ae39..40044a37c04 100644
--- a/storage/innobase/dict/dict0stats.cc
+++ b/storage/innobase/dict/dict0stats.cc
@@ -601,9 +601,6 @@ dict_stats_assert_initialized(
 	UNIV_MEM_ASSERT_RW_ABORT(&table->stat_modified_counter,
 			   sizeof(table->stat_modified_counter));
 
-	UNIV_MEM_ASSERT_RW_ABORT(&table->stats_bg_flag,
-			   sizeof(table->stats_bg_flag));
-
 	for (dict_index_t* index = dict_table_get_first_index(table);
 	     index != NULL;
 	     index = dict_table_get_next_index(index)) {
@@ -2165,9 +2162,7 @@ dict_stats_update_persistent(dict_table_t* table, trx_t* trx)
 			continue;
 		}
 
-		if (!(table->stats_bg_flag & BG_STAT_SHOULD_QUIT)) {
-			dict_stats_analyze_index(index);
-		}
+		dict_stats_analyze_index(index);
 
 		table->stat_sum_of_other_index_sizes
 			+= index->stat_index_size;
diff --git a/storage/innobase/dict/dict0stats_bg.cc b/storage/innobase/dict/dict0stats_bg.cc
index 3f5419a0751..d70113b4166 100644
--- a/storage/innobase/dict/dict0stats_bg.cc
+++ b/storage/innobase/dict/dict0stats_bg.cc
@@ -243,27 +243,6 @@ dict_stats_recalc_pool_del(
 	mutex_exit(&recalc_pool_mutex);
 }
 
-/*****************************************************************//**
-Wait until background stats thread has stopped using the specified table.
-The caller must have locked the data dictionary using
-row_mysql_lock_data_dictionary() and this function may unlock it temporarily
-and restore the lock before it exits.
-The background stats thread is guaranteed not to start using the specified
-table after this function returns and before the caller unlocks the data
-dictionary because it sets the BG_STAT_IN_PROGRESS bit in table->stats_bg_flag
-under dict_sys->mutex. */
-void
-dict_stats_wait_bg_to_stop_using_table(
-/*===================================*/
-	dict_table_t*	table,	/*!< in/out: table */
-	trx_t*		trx)	/*!< in/out: transaction to use for
-				unlocking/locking the data dict */
-{
-	while (!dict_stats_stop_bg(table)) {
-		DICT_BG_YIELD(trx);
-	}
-}
-
 /*****************************************************************//**
 Initialize global variables needed for the operation of dict_stats_thread()
 Must be called before dict_stats_thread() is started. */
@@ -357,8 +336,6 @@ dict_stats_process_entry_from_recalc_pool(trx_t* trx)
 		return;
 	}
 
-	table->stats_bg_flag |= BG_STAT_IN_PROGRESS;
-
 	mutex_exit(&dict_sys->mutex);
 
 	/* ut_time() could be expensive, the current function
@@ -381,15 +358,13 @@ dict_stats_process_entry_from_recalc_pool(trx_t* trx)
 		trx->error_state = DB_SUCCESS;
 		++trx->will_lock;
 		dict_stats_update(table, DICT_STATS_RECALC_PERSISTENT, trx);
-		if (trx->state != TRX_STATE_NOT_STARTED) {
+		if (trx->lock.trx_locks.count) {
 			trx_commit_for_mysql(trx);
 		}
 	}
 
 	mutex_enter(&dict_sys->mutex);
 
-	table->stats_bg_flag = BG_STAT_NONE;
-
 	dict_table_close(table, TRUE, FALSE);
 
 	mutex_exit(&dict_sys->mutex);
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index dd397bb7b20..747b0ff768f 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -13582,10 +13582,9 @@ ha_innobase::truncate()
 		++m_prebuilt->trx->will_lock;
 	}
 
-	dberr_t	err;
-
 	/* Truncate the table in InnoDB */
-	err = row_truncate_table_for_mysql(m_prebuilt->table, m_prebuilt->trx);
+	dberr_t err = row_truncate_table_for_mysql(m_prebuilt->table,
+						   m_prebuilt->trx);
 
 	int	error;
 
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index ad75a5e8846..96289d31858 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -4497,14 +4497,6 @@ prepare_inplace_alter_table_dict(
 	row_mysql_lock_data_dictionary(ctx->trx);
 	dict_locked = true;
 
-	/* Wait for background stats processing to stop using the table that
-	we are going to alter. We know bg stats will not start using it again
-	until we are holding the data dict locked and we are holding it here
-	at least until checking ut_ad(user_table->n_ref_count == 1) below.
-	XXX what may happen if bg stats opens the table after we
-	have unlocked data dictionary below? */
-	dict_stats_wait_bg_to_stop_using_table(user_table, ctx->trx);
-
 	online_retry_drop_indexes_low(ctx->new_table, ctx->trx);
 
 	ut_d(dict_table_check_for_dup_indexes(
@@ -4760,7 +4752,11 @@ prepare_inplace_alter_table_dict(
 			DBUG_ASSERT(ctx->trx != ctx->prebuilt->trx);
 			trx_rollback_to_savepoint(ctx->trx, NULL);
 
-			ut_ad(user_table->get_ref_count() == 1);
+			/* Even though purge should not be able to
+			execute while we are holding an exclusive
+			dict_operation_lock, background statistics
+			update may be holding a table handle. */
+			ut_ad(user_table->get_ref_count() >= 1);
 
 			online_retry_drop_indexes_with_trx(
 				user_table, ctx->trx);
@@ -8443,11 +8439,13 @@ ha_innobase::commit_inplace_alter_table(
 	}
 
 	trx_start_if_not_started_xa(m_prebuilt->trx, true);
+	const bool	new_clustered	= ctx0->need_rebuild();
 
 	for (inplace_alter_handler_ctx** pctx = ctx_array; *pctx; pctx++) {
 		ha_innobase_inplace_ctx*	ctx
 			= static_cast<ha_innobase_inplace_ctx*>(*pctx);
 		DBUG_ASSERT(ctx->prebuilt->trx == m_prebuilt->trx);
+		DBUG_ASSERT(ctx->need_rebuild() == new_clustered);
 
 		/* If decryption failed for old table or new table
 		fail here. */
@@ -8472,6 +8470,10 @@ ha_innobase::commit_inplace_alter_table(
 
 		error = row_merge_lock_table(
 			m_prebuilt->trx, ctx->old_table, LOCK_X);
+		if (error == DB_SUCCESS && new_clustered) {
+			error = row_merge_lock_table(
+				m_prebuilt->trx, ctx->new_table, LOCK_X);
+		}
 
 		if (error != DB_SUCCESS) {
 			my_error_innodb(
@@ -8482,7 +8484,6 @@ ha_innobase::commit_inplace_alter_table(
 
 	DEBUG_SYNC(m_user_thd, "innodb_alter_commit_after_lock_table");
 
-	const bool	new_clustered	= ctx0->need_rebuild();
 	trx_t*		trx		= ctx0->trx;
 	bool		fail		= false;
 
@@ -8519,35 +8520,6 @@ ha_innobase::commit_inplace_alter_table(
 
 	ut_ad(log_append_on_checkpoint(NULL) == NULL);
 
-	/* Prevent the background statistics collection from accessing
-	the tables. */
-	for (;;) {
-		bool	retry = false;
-
-		for (inplace_alter_handler_ctx** pctx = ctx_array;
-		     *pctx; pctx++) {
-			ha_innobase_inplace_ctx*	ctx
-				= static_cast<ha_innobase_inplace_ctx*>(*pctx);
-
-			DBUG_ASSERT(new_clustered == ctx->need_rebuild());
-
-			if (new_clustered
-			    && !dict_stats_stop_bg(ctx->old_table)) {
-				retry = true;
-			}
-
-			if (!dict_stats_stop_bg(ctx->new_table)) {
-				retry = true;
-			}
-		}
-
-		if (!retry) {
-			break;
-		}
-
-		DICT_BG_YIELD(trx);
-	}
-
 	/* Make a concurrent Drop fts Index to wait until sync of that
 	fts index is happening in the background */
 	for (;;) {
diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h
index 97b34fd53e5..040e46ceaba 100644
--- a/storage/innobase/include/dict0mem.h
+++ b/storage/innobase/include/dict0mem.h
@@ -2,7 +2,7 @@
 
 Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
 Copyright (c) 2012, Facebook Inc.
-Copyright (c) 2013, 2017, MariaDB Corporation.
+Copyright (c) 2013, 2018, MariaDB Corporation.
 
 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
@@ -1586,40 +1586,6 @@ struct dict_table_t {
 	any latch, because this is only used for heuristics. */
 	ib_uint64_t				stat_modified_counter;
 
-	/** Background stats thread is not working on this table. */
-	#define BG_STAT_NONE			0
-
-	/** Set in 'stats_bg_flag' when the background stats code is working
-	on this table. The DROP TABLE code waits for this to be cleared before
-	proceeding. */
-	#define BG_STAT_IN_PROGRESS		(1 << 0)
-
-	/** Set in 'stats_bg_flag' when DROP TABLE starts waiting on
-	BG_STAT_IN_PROGRESS to be cleared. The background stats thread will
-	detect this and will eventually quit sooner. */
-	#define BG_STAT_SHOULD_QUIT		(1 << 1)
-
-	/** The state of the background stats thread wrt this table.
-	See BG_STAT_NONE, BG_STAT_IN_PROGRESS and BG_STAT_SHOULD_QUIT.
-	Writes are covered by dict_sys->mutex. Dirty reads are possible. */
-
-	#define BG_SCRUB_IN_PROGRESS	((byte)(1 << 2))
-				/*!< BG_SCRUB_IN_PROGRESS is set in
-				stats_bg_flag when the background
-				scrub code is working on this table. The DROP
-				TABLE code waits for this to be cleared
-				before proceeding. */
-
-	#define BG_STAT_SHOULD_QUIT		(1 << 1)
-
-	#define BG_IN_PROGRESS (BG_STAT_IN_PROGRESS | BG_SCRUB_IN_PROGRESS)
-
-
-	/** The state of the background stats thread wrt this table.
-	See BG_STAT_NONE, BG_STAT_IN_PROGRESS and BG_STAT_SHOULD_QUIT.
-	Writes are covered by dict_sys->mutex. Dirty reads are possible. */
-	byte					stats_bg_flag;
-
 	bool		stats_error_printed;
 				/*!< Has persistent stats error beein
 				already printed for this table ? */
diff --git a/storage/innobase/include/dict0stats_bg.h b/storage/innobase/include/dict0stats_bg.h
index e66666b66a3..1aa6c996496 100644
--- a/storage/innobase/include/dict0stats_bg.h
+++ b/storage/innobase/include/dict0stats_bg.h
@@ -63,42 +63,6 @@ for the background thread to stop accessing a table.
 	row_mysql_lock_data_dictionary(trx);	\
 } while (0)
 
-/*****************************************************************//**
-Request the background collection of statistics to stop for a table.
-@retval true when no background process is active
-@retval false when it is not safe to modify the table definition */
-UNIV_INLINE
-bool
-dict_stats_stop_bg(
-/*===============*/
-	dict_table_t*	table)	/*!< in/out: table */
-{
-	ut_ad(!srv_read_only_mode);
-	ut_ad(mutex_own(&dict_sys->mutex));
-
-	if (!(table->stats_bg_flag & BG_STAT_IN_PROGRESS)) {
-		return(true);
-	}
-
-	table->stats_bg_flag |= BG_STAT_SHOULD_QUIT;
-	return(false);
-}
-
-/*****************************************************************//**
-Wait until background stats thread has stopped using the specified table.
-The caller must have locked the data dictionary using
-row_mysql_lock_data_dictionary() and this function may unlock it temporarily
-and restore the lock before it exits.
-The background stats thread is guaranteed not to start using the specified
-table after this function returns and before the caller unlocks the data
-dictionary because it sets the BG_STAT_IN_PROGRESS bit in table->stats_bg_flag
-under dict_sys->mutex. */
-void
-dict_stats_wait_bg_to_stop_using_table(
-/*===================================*/
-	dict_table_t*	table,	/*!< in/out: table */
-	trx_t*		trx);	/*!< in/out: transaction to use for
-				unlocking/locking the data dict */
 /*****************************************************************//**
 Initialize global variables needed for the operation of dict_stats_thread().
 Must be called before dict_stats_thread() is started. */
diff --git a/storage/innobase/include/row0merge.h b/storage/innobase/include/row0merge.h
index 51ad5cc5cd7..e48b3f46d87 100644
--- a/storage/innobase/include/row0merge.h
+++ b/storage/innobase/include/row0merge.h
@@ -1,7 +1,7 @@
 /*****************************************************************************
 
 Copyright (c) 2005, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2015, 2017, MariaDB Corporation.
+Copyright (c) 2015, 2018, MariaDB Corporation.
 
 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
@@ -285,10 +285,7 @@ row_merge_is_index_usable(
 	MY_ATTRIBUTE((nonnull, warn_unused_result));
 
 /*********************************************************************//**
-Drop a table. The caller must have ensured that the background stats
-thread is not processing the table. This can be done by calling
-dict_stats_wait_bg_to_stop_using_table() after locking the dictionary and
-before calling this function.
+Drop a table.
 @return DB_SUCCESS or error code */
 dberr_t
 row_merge_drop_table(
diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc
index 017821fdf38..9db250f68f3 100644
--- a/storage/innobase/row/row0merge.cc
+++ b/storage/innobase/row/row0merge.cc
@@ -1,7 +1,7 @@
 /*****************************************************************************
 
 Copyright (c) 2005, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2014, 2017, MariaDB Corporation.
+Copyright (c) 2014, 2018, MariaDB Corporation.
 
 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
@@ -4462,10 +4462,7 @@ row_merge_is_index_usable(
 }
 
 /*********************************************************************//**
-Drop a table. The caller must have ensured that the background stats
-thread is not processing the table. This can be done by calling
-dict_stats_wait_bg_to_stop_using_table() after locking the dictionary and
-before calling this function.
+Drop a table.
 @return DB_SUCCESS or error code */
 dberr_t
 row_merge_drop_table(
@@ -4475,9 +4472,6 @@ row_merge_drop_table(
 {
 	ut_ad(!srv_read_only_mode);
 
-	/* There must be no open transactions on the table. */
-	ut_a(table->get_ref_count() == 0);
-
 	return(row_drop_table_for_mysql(table->name.m_name,
 			trx, false, false, false));
 }
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index 8aaff448f9e..5c2b060fbed 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -2964,13 +2964,10 @@ row_discard_tablespace_begin(
 
 	row_mysql_lock_data_dictionary(trx);
 
-	dict_table_t*	table;
-
-	table = dict_table_open_on_name(
-		name, TRUE, FALSE, DICT_ERR_IGNORE_NONE);
+	dict_table_t*	table = dict_table_open_on_name(
+		name, FALSE, FALSE, DICT_ERR_IGNORE_NONE);
 
 	if (table) {
-		dict_stats_wait_bg_to_stop_using_table(table, trx);
 		ut_a(!is_system_tablespace(table->space));
 		ut_a(table->n_foreign_key_checks_running == 0);
 	}
@@ -3552,19 +3549,8 @@ row_drop_table_for_mysql(
 			fts_optimize_remove_table(table);
 			row_mysql_lock_data_dictionary(trx);
 		}
-
-		/* Do not bother to deal with persistent stats for temp
-		tables since we know temp tables do not use persistent
-		stats. */
-		if (!dict_table_is_temporary(table)) {
-			dict_stats_wait_bg_to_stop_using_table(
-				table, trx);
-		}
 	}
 
-	/* make sure background stats thread is not running on the table */
-	ut_ad(!(table->stats_bg_flag & BG_STAT_IN_PROGRESS));
-
 	/* Delete the link file if used. */
 	if (DICT_TF_HAS_DATA_DIR(table->flags)) {
 		RemoteDatafile::delete_link_file(name);
@@ -3684,11 +3670,8 @@ row_drop_table_for_mysql(
 		ut_a(table->n_rec_locks == 0);
 	} else if (table->get_ref_count() > 0 || table->n_rec_locks > 0) {
 		if (row_add_table_to_background_drop_list(table->id)) {
-			ib::info() << "MySQL is trying to drop table "
-				<< table->name
-				<< " though there are still open handles to"
-				" it. Adding the table to the background drop"
-				" queue.";
+			ib::info() << "Going to drop table "
+				<< table->name << " in the background.";
 
 			/* We return DB_SUCCESS to MySQL though the drop will
 			happen lazily later */
@@ -4093,11 +4076,7 @@ row_drop_database_for_mysql(
 	trx_t*		trx,
 	ulint*		found)
 {
-	dict_table_t*	table;
-	char*		table_name;
-	dberr_t		err	= DB_SUCCESS;
 	ulint		namelen	= strlen(name);
-	bool		is_partition = false;
 
 	ut_ad(found != NULL);
 
@@ -4106,15 +4085,6 @@ row_drop_database_for_mysql(
 	DBUG_PRINT("row_drop_database_for_mysql", ("db: '%s'", name));
 
 	ut_a(name != NULL);
-	/* Assert DB name or partition name. */
-	if (name[namelen - 1] == '#') {
-		ut_ad(name[namelen - 2] != '/');
-		is_partition = true;
-		trx->op_info = "dropping partitions";
-	} else {
-		ut_a(name[namelen - 1] == '/');
-		trx->op_info = "dropping database";
-	}
 
 	*found = 0;
 
@@ -4122,10 +4092,7 @@ row_drop_database_for_mysql(
 
 	trx_start_if_not_started_xa(trx, true);
 
-loop:
-	row_mysql_lock_data_dictionary(trx);
-
-	while ((table_name = dict_get_first_table_name_in_db(name))) {
+	while (char* table_name = dict_get_first_table_name_in_db(name)) {
 		/* Drop parent table if it is a fts aux table, to
 		avoid accessing dropped fts aux tables in information
 		scheam when parent table still exists.
@@ -4141,77 +4108,8 @@ row_drop_database_for_mysql(
 
 		ut_a(memcmp(table_name, name, namelen) == 0);
 
-		table = dict_table_open_on_name(
-			table_name, TRUE, FALSE, static_cast<dict_err_ignore_t>(
-				DICT_ERR_IGNORE_INDEX_ROOT
-				| DICT_ERR_IGNORE_CORRUPT));
-
-		if (!table) {
-			ib::error() << "Cannot load table " << table_name
-				<< " from InnoDB internal data dictionary"
-				" during drop database";
-			ut_free(table_name);
-			err = DB_TABLE_NOT_FOUND;
-			break;
-
-		}
-
-		if (!row_is_mysql_tmp_table_name(table->name.m_name)) {
-			/* There could be orphan temp tables left from
-			interrupted alter table. Leave them, and handle
-			the rest.*/
-			if (table->can_be_evicted
-			    && (name[namelen - 1] != '#')) {
-				ib::warn() << "Orphan table encountered during"
-					" DROP DATABASE. This is possible if '"
-					<< table->name << ".frm' was lost.";
-			}
-
-			if (!table->is_readable()
-			    && !fil_space_get(table->space)) {
-				ib::warn() << "Missing .ibd file for table "
-					<< table->name << ".";
-			}
-		}
-
-		dict_table_close(table, TRUE, FALSE);
-
-		/* The dict_table_t object must not be accessed before
-		dict_table_open() or after dict_table_close(). But this is OK
-		if we are holding, the dict_sys->mutex. */
-		ut_ad(mutex_own(&dict_sys->mutex));
-
-		/* Disable statistics on the found table. */
-		if (!dict_stats_stop_bg(table)) {
-			row_mysql_unlock_data_dictionary(trx);
-
-			os_thread_sleep(250000);
-
-			ut_free(table_name);
-
-			goto loop;
-		}
-
-		/* Wait until MySQL does not have any queries running on
-		the table */
-
-		if (table->get_ref_count() > 0) {
-			row_mysql_unlock_data_dictionary(trx);
-
-			ib::warn() << "MySQL is trying to drop database "
-				<< ut_get_name(trx, name) << " though"
-				" there are still open handles to table "
-				<< table->name << ".";
-
-			os_thread_sleep(1000000);
-
-			ut_free(table_name);
-
-			goto loop;
-		}
-
-		err = row_drop_table_for_mysql(table_name, trx, TRUE, FALSE);
-		trx_commit_for_mysql(trx);
+		dberr_t err = row_drop_table_for_mysql(
+			table_name, trx, TRUE, FALSE);
 
 		if (err != DB_SUCCESS) {
 			ib::error() << "DROP DATABASE "
@@ -4219,31 +4117,24 @@ row_drop_database_for_mysql(
 				" with error (" << ut_strerr(err) << ") for"
 				" table " << ut_get_name(trx, table_name);
 			ut_free(table_name);
-			break;
+			return err;
 		}
 
 		ut_free(table_name);
 		(*found)++;
 	}
 
-	/* Partitioning does not yet support foreign keys. */
-	if (err == DB_SUCCESS && !is_partition) {
-		/* after dropping all tables try to drop all leftover
-		foreign keys in case orphaned ones exist */
-		err = drop_all_foreign_keys_in_db(name, trx);
+	/* after dropping all tables try to drop all leftover foreign
+	keys in case orphaned ones exist */
+	dberr_t err = drop_all_foreign_keys_in_db(name, trx);
 
-		if (err != DB_SUCCESS) {
-			const std::string&	db = ut_get_name(trx, name);
-			ib::error() << "DROP DATABASE " << db << " failed with"
-				" error " << err << " while dropping all"
-				" foreign keys";
-		}
+	if (err != DB_SUCCESS) {
+		const std::string&	db = ut_get_name(trx, name);
+		ib::error() << "DROP DATABASE " << db
+			    << " failed with error " << ut_strerr(err)
+			    << " while dropping all foreign keys";
 	}
 
-	trx_commit_for_mysql(trx);
-
-	row_mysql_unlock_data_dictionary(trx);
-
 	trx->op_info = "";
 
 	DBUG_RETURN(err);
@@ -4445,7 +4336,6 @@ row_rename_table_for_mysql(
 	}
 
 	ut_ad(!my_atomic_loadlint(&table->n_foreign_key_checks_running));
-	ut_ad(!(table->stats_bg_flag & BG_STAT_IN_PROGRESS));
 
 	if (commit) {
 		row_mysql_lock_data_dictionary(trx);
diff --git a/storage/innobase/row/row0trunc.cc b/storage/innobase/row/row0trunc.cc
index ce470544430..2e2eb01676e 100644
--- a/storage/innobase/row/row0trunc.cc
+++ b/storage/innobase/row/row0trunc.cc
@@ -1,7 +1,7 @@
 /*****************************************************************************
 
 Copyright (c) 2013, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, MariaDB Corporation.
+Copyright (c) 2017, 2018, MariaDB Corporation.
 
 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
@@ -1703,12 +1703,17 @@ row_truncate_table_for_mysql(
 	trx_t* trx)
 {
 	bool	is_file_per_table = dict_table_is_file_per_table(table);
-	dberr_t		err;
 #ifdef UNIV_DEBUG
 	ulint		old_space = table->space;
 #endif /* UNIV_DEBUG */
 	TruncateLogger*	logger = NULL;
 
+	dberr_t err = row_mysql_lock_table(trx, table, LOCK_X,
+					   "locking for TRUNCATE");
+	if (err != DB_SUCCESS) {
+		return err;
+	}
+
 	/* Understanding the truncate flow.
 
 	Step-1: Perform intiial sanity check to ensure table can be truncated.
@@ -1718,11 +1723,7 @@ row_truncate_table_for_mysql(
 	Step-2: Start transaction (only for non-temp table as temp-table don't
 	modify any data on disk doesn't need transaction object).
 
-	Step-3: Validate ownership of needed locks (Exclusive lock).
-	Ownership will also ensure there is no active SQL queries, INSERT,
-	SELECT, .....
-
-	Step-4: Stop all the background process associated with table.
+	Step-3: Exclusively lock the data dictionary cache.
 
 	Step-5: There are few foreign key related constraint under which
 	we can't truncate table (due to referential integrity unless it is
@@ -1805,18 +1806,13 @@ row_truncate_table_for_mysql(
 		trx_start_for_ddl(trx, TRX_DICT_OP_TABLE);
 	}
 
-	/* Step-3: Validate ownership of needed locks (Exclusive lock).
-	Ownership will also ensure there is no active SQL queries, INSERT,
-	SELECT, .....*/
+	/* Step-3: Exclusively lock the data dictionary cache. */
 	trx->op_info = "truncating table";
 	ut_a(trx->dict_operation_lock_mode == 0);
 	row_mysql_lock_data_dictionary(trx);
 	ut_ad(mutex_own(&dict_sys->mutex));
 	ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
 
-	/* Step-4: Stop all the background process associated with table. */
-	dict_stats_wait_bg_to_stop_using_table(table, trx);
-
 	/* Step-5: There are few foreign key related constraint under which
 	we can't truncate table (due to referential integrity unless it is
 	turned off). Ensure this condition is satisfied. */
-- 
2.15.1

