From 1ed09cfdcbb66807d2e3804884545b405f0f7ad1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@mariadb.com>
Date: Fri, 28 Feb 2025 08:55:16 +0200
Subject: [PATCH] MDEV-35000 preparation: Clean up dict_table_t::stat

innodb_stats_transient_sample_pages, innodb_stats_persistent_sample_pages:
Change the type to UNSIGNED, because the number of pages in a table
is limited to 32 bits by the InnoDB file format.

btr_get_size_and_reserved(), fseg_get_n_frag_pages(),
fseg_n_reserved_pages_low(), fseg_n_reserved_pages(): Return uint32_t.
The file format limits page numbers to 32 bits.

dict_table_t::stat: An Atomic_relaxed<uint32_t> that combines a
number of metadata fields.

innodb_copy_stat_flags(): Copy the statistics flags from
TABLE_SHARE or HA_CREATE_INFO.

dict_table_t::stats_initialized(), dict_table_t::stats_is_persistent():
Accessors to dict_table_t::stat.

Reviewed by: Thirunarayanan Balathandayuthapani
---
 .../suite/sys_vars/r/sysvars_innodb.result    |   8 +-
 storage/innobase/dict/dict0defrag_bg.cc       |  24 ++--
 storage/innobase/dict/dict0dict.cc            |   5 +-
 storage/innobase/dict/dict0stats.cc           |  88 ++++++------
 storage/innobase/dict/dict0stats_bg.cc        |  14 +-
 storage/innobase/fsp/fsp0fsp.cc               |  33 ++---
 storage/innobase/handler/ha_innodb.cc         | 123 ++++++++---------
 storage/innobase/handler/handler0alter.cc     |   5 +-
 storage/innobase/handler/i_s.cc               |   4 +-
 storage/innobase/ibuf/ibuf0ibuf.cc            |  29 ++--
 storage/innobase/include/dict0dict.h          |   2 +-
 storage/innobase/include/dict0mem.h           | 100 +++++++-------
 storage/innobase/include/dict0stats.h         |  38 ------
 storage/innobase/include/dict0stats.inl       | 125 +-----------------
 storage/innobase/include/fsp0fsp.h            |   6 +-
 storage/innobase/include/ibuf0ibuf.h          |  10 +-
 storage/innobase/include/srv0srv.h            |   4 +-
 storage/innobase/row/row0mysql.cc             |   2 +-
 storage/innobase/row/row0purge.cc             |   2 +-
 storage/innobase/row/row0uins.cc              |   2 +-
 storage/innobase/row/row0umod.cc              |   2 +-
 storage/innobase/srv/srv0srv.cc               |   4 +-
 22 files changed, 229 insertions(+), 401 deletions(-)

diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result
index 2d8e1573db84d..a82eab3331347 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result
@@ -1571,10 +1571,10 @@ VARIABLE_NAME	INNODB_STATS_PERSISTENT_SAMPLE_PAGES
 SESSION_VALUE	NULL
 DEFAULT_VALUE	20
 VARIABLE_SCOPE	GLOBAL
-VARIABLE_TYPE	BIGINT UNSIGNED
+VARIABLE_TYPE	INT UNSIGNED
 VARIABLE_COMMENT	The number of leaf index pages to sample when calculating persistent statistics (by ANALYZE, default 20)
 NUMERIC_MIN_VALUE	1
-NUMERIC_MAX_VALUE	18446744073709551615
+NUMERIC_MAX_VALUE	4294967295
 NUMERIC_BLOCK_SIZE	0
 ENUM_VALUE_LIST	NULL
 READ_ONLY	NO
@@ -1595,10 +1595,10 @@ VARIABLE_NAME	INNODB_STATS_TRANSIENT_SAMPLE_PAGES
 SESSION_VALUE	NULL
 DEFAULT_VALUE	8
 VARIABLE_SCOPE	GLOBAL
-VARIABLE_TYPE	BIGINT UNSIGNED
+VARIABLE_TYPE	INT UNSIGNED
 VARIABLE_COMMENT	The number of leaf index pages to sample when calculating transient statistics (if persistent statistics are not used, default 8)
 NUMERIC_MIN_VALUE	1
-NUMERIC_MAX_VALUE	18446744073709551615
+NUMERIC_MAX_VALUE	4294967295
 NUMERIC_BLOCK_SIZE	0
 ENUM_VALUE_LIST	NULL
 READ_ONLY	NO
diff --git a/storage/innobase/dict/dict0defrag_bg.cc b/storage/innobase/dict/dict0defrag_bg.cc
index bec6da8e6af67..81b8dbf077d78 100644
--- a/storage/innobase/dict/dict0defrag_bg.cc
+++ b/storage/innobase/dict/dict0defrag_bg.cc
@@ -284,18 +284,18 @@ dberr_t dict_stats_save_defrag_summary(dict_index_t *index, THD *thd)
 
 /**************************************************************//**
 Gets the number of reserved and used pages in a B-tree.
-@return	number of pages reserved, or ULINT_UNDEFINED if the index
-is unavailable */
+@return	number of pages reserved
+@retval 0 if the index is unavailable */
 static
-ulint
+uint32_t
 btr_get_size_and_reserved(
 	dict_index_t*	index,	/*!< in: index */
 	ulint		flag,	/*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */
-	ulint*		used,	/*!< out: number of pages used (<= reserved) */
+	uint32_t*	used,	/*!< out: number of pages used (<= reserved) */
 	mtr_t*		mtr)	/*!< in/out: mini-transaction where index
 				is s-latched */
 {
-	ulint		dummy;
+	uint32_t	dummy;
 
 	ut_ad(mtr->memo_contains(index->lock, MTR_MEMO_SX_LOCK));
 	ut_a(flag == BTR_N_LEAF_PAGES || flag == BTR_TOTAL_SIZE);
@@ -304,19 +304,19 @@ btr_get_size_and_reserved(
 	    || dict_index_is_online_ddl(index)
 	    || !index->is_committed()
 	    || !index->table->space) {
-		return(ULINT_UNDEFINED);
+		return 0;
 	}
 
 	dberr_t err;
 	buf_block_t* root = btr_root_block_get(index, RW_SX_LATCH, mtr, &err);
 	*used = 0;
 	if (!root) {
-		return ULINT_UNDEFINED;
+		return 0;
 	}
 
 	mtr->x_lock_space(index->table->space);
 
-	ulint n = fseg_n_reserved_pages(*root, PAGE_HEADER + PAGE_BTR_SEG_LEAF
+	auto n = fseg_n_reserved_pages(*root, PAGE_HEADER + PAGE_BTR_SEG_LEAF
 					+ root->page.frame, used, mtr);
 	if (flag == BTR_TOTAL_SIZE) {
 		n += fseg_n_reserved_pages(*root,
@@ -343,14 +343,14 @@ dict_stats_save_defrag_stats(
 
   const time_t now= time(nullptr);
   mtr_t mtr;
-  ulint n_leaf_pages;
+  uint32_t n_leaf_pages;
   mtr.start();
   mtr_sx_lock_index(index, &mtr);
-  ulint n_leaf_reserved= btr_get_size_and_reserved(index, BTR_N_LEAF_PAGES,
-                                                   &n_leaf_pages, &mtr);
+  uint32_t n_leaf_reserved= btr_get_size_and_reserved(index, BTR_N_LEAF_PAGES,
+                                                      &n_leaf_pages, &mtr);
   mtr.commit();
 
-  if (n_leaf_reserved == ULINT_UNDEFINED)
+  if (!n_leaf_reserved)
     return DB_SUCCESS;
 
   THD *thd= current_thd;
diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
index 2e6a6d896e149..38e95282f2686 100644
--- a/storage/innobase/dict/dict0dict.cc
+++ b/storage/innobase/dict/dict0dict.cc
@@ -198,8 +198,7 @@ dict_tables_have_same_db(
 /** Decrement the count of open handles */
 void dict_table_close(dict_table_t *table)
 {
-  if (table->get_ref_count() == 1 &&
-      dict_stats_is_persistent_enabled(table) &&
+  if (table->get_ref_count() == 1 && table->stats_is_persistent() &&
       strchr(table->name.m_name, '/'))
   {
     /* It looks like we are closing the last handle. The user could
@@ -238,7 +237,7 @@ dict_table_close(
     dict_table_close(table);
   else
   {
-    if (table->release() && dict_stats_is_persistent_enabled(table) &&
+    if (table->release() && table->stats_is_persistent() &&
 	strchr(table->name.m_name, '/'))
     {
       /* Force persistent stats re-read upon next open of the table so
diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc
index 34c2306feb5eb..b9bb3645dd59f 100644
--- a/storage/innobase/dict/dict0stats.cc
+++ b/storage/innobase/dict/dict0stats.cc
@@ -599,7 +599,7 @@ void dict_stats_empty_table(
 	table->stat_clustered_index_size = 1;
 	/* 1 page for each index, not counting the clustered */
 	table->stat_sum_of_other_index_sizes
-		= UT_LIST_GET_LEN(table->indexes) - 1;
+		= uint32_t(UT_LIST_GET_LEN(table->indexes) - 1);
 	table->stat_modified_counter = 0;
 
 	dict_index_t*	index;
@@ -617,7 +617,7 @@ void dict_stats_empty_table(
 		dict_stats_empty_index(index, empty_defrag_stats);
 	}
 
-	table->stat_initialized = TRUE;
+	table->stat = table->stat | dict_table_t::STATS_INITIALIZED;
 	table->stats_mutex_unlock();
 }
 
@@ -658,16 +658,10 @@ dict_stats_assert_initialized(
 /*==========================*/
 	const dict_table_t*	table)	/*!< in: table */
 {
-	ut_a(table->stat_initialized);
-
 	MEM_CHECK_DEFINED(&table->stats_last_recalc,
 			  sizeof table->stats_last_recalc);
 
-	MEM_CHECK_DEFINED(&table->stat_persistent,
-			  sizeof table->stat_persistent);
-
-	MEM_CHECK_DEFINED(&table->stats_auto_recalc,
-			  sizeof table->stats_auto_recalc);
+	MEM_CHECK_DEFINED(&table->stat, sizeof table->stat);
 
 	MEM_CHECK_DEFINED(&table->stats_sample_pages,
 			  sizeof table->stats_sample_pages);
@@ -844,8 +838,8 @@ btr_estimate_number_of_different_key_vals(dict_index_t* index,
 	ulint		n_cols;
 	ib_uint64_t*	n_diff;
 	ib_uint64_t*	n_not_null;
-	ibool		stats_null_not_equal;
-	uintmax_t	n_sample_pages=1; /* number of pages to sample */
+	bool		stats_null_not_equal;
+	uint32_t	n_sample_pages=1; /* number of pages to sample */
 	ulint		not_empty_flag	= 0;
 	ulint		total_external_size = 0;
 	uintmax_t	add_on;
@@ -883,11 +877,11 @@ btr_estimate_number_of_different_key_vals(dict_index_t* index,
 	case SRV_STATS_NULLS_UNEQUAL:
 		/* for both SRV_STATS_NULLS_IGNORED and SRV_STATS_NULLS_UNEQUAL
 		case, we will treat NULLs as unequal value */
-		stats_null_not_equal = TRUE;
+		stats_null_not_equal = true;
 		break;
 
 	case SRV_STATS_NULLS_EQUAL:
-		stats_null_not_equal = FALSE;
+		stats_null_not_equal = false;
 		break;
 
 	default:
@@ -938,19 +932,21 @@ btr_estimate_number_of_different_key_vals(dict_index_t* index,
 
 		so taking all case2 paths is I, our expression is:
 		n_pages = S < I? min(I,L) : I
-                */
-		if (index->stat_index_size > 1) {
-			n_sample_pages = (srv_stats_transient_sample_pages < index->stat_index_size)
-				? ut_min(index->stat_index_size,
-					 static_cast<ulint>(
-						 log2(double(index->stat_index_size))
-						 * double(srv_stats_transient_sample_pages)))
-				: index->stat_index_size;
+		*/
+		if (uint32_t I = index->stat_index_size) {
+			const uint32_t S{srv_stats_transient_sample_pages};
+			n_sample_pages = S < I
+				? std::min(I,
+					   uint32_t(log2(double(I))
+						    * double(S)))
+				: I;
 		}
 	}
 
 	/* Sanity check */
-	ut_ad(n_sample_pages > 0 && n_sample_pages <= (index->stat_index_size <= 1 ? 1 : index->stat_index_size));
+	ut_ad(n_sample_pages);
+	ut_ad(n_sample_pages <= (index->stat_index_size <= 1
+				 ? 1 : index->stat_index_size));
 
 	/* We sample some pages in the index to get an estimate */
 	btr_cur_t cursor;
@@ -1169,7 +1165,7 @@ dict_stats_update_transient_for_index(
 
 		mtr.x_lock_space(index->table->space);
 
-		ulint dummy, size;
+		uint32_t dummy, size;
 		index->stat_index_size
 			= fseg_n_reserved_pages(*root, PAGE_HEADER
 						+ PAGE_BTR_SEG_LEAF
@@ -1226,7 +1222,7 @@ dict_stats_update_transient(
 	ut_ad(!table->stats_mutex_is_owner());
 
 	dict_index_t*	index;
-	ulint		sum_of_index_sizes	= 0;
+	uint32_t	sum_of_index_sizes	= 0;
 	dberr_t		err = DB_SUCCESS;
 
 	/* Find out the sizes of the indexes and how many different values
@@ -1285,7 +1281,7 @@ dict_stats_update_transient(
 
 	table->stat_modified_counter = 0;
 
-	table->stat_initialized = TRUE;
+	table->stat = table->stat | dict_table_t::STATS_INITIALIZED;
 
 	table->stats_mutex_unlock();
 
@@ -2225,8 +2221,8 @@ dict_stats_analyze_index_for_n_prefix(
 struct index_stats_t
 {
   std::vector<index_field_stats_t> stats;
-  ulint index_size;
-  ulint n_leaf_pages;
+  uint32_t index_size;
+  uint32_t n_leaf_pages;
 
   index_stats_t(ulint n_uniq) : index_size(1), n_leaf_pages(1)
   {
@@ -2365,7 +2361,7 @@ static index_stats_t dict_stats_analyze_index(dict_index_t* index)
 
 	uint16_t root_level = btr_page_get_level(root->page.frame);
 	mtr.x_lock_space(index->table->space);
-	ulint dummy, size;
+	uint32_t dummy, size;
 	result.index_size
 		= fseg_n_reserved_pages(*root, PAGE_HEADER + PAGE_BTR_SEG_LEAF
 					+ root->page.frame, &size, &mtr)
@@ -2742,7 +2738,7 @@ dict_stats_update_persistent(
 
 	table->stat_modified_counter = 0;
 
-	table->stat_initialized = TRUE;
+	table->stat = table->stat | dict_table_t::STATS_INITIALIZED;
 
 	dict_stats_assert_initialized(table);
 
@@ -3164,8 +3160,7 @@ dict_stats_fetch_table_stats_step(
 			ut_a(len == 8);
 
 			table->stat_clustered_index_size
-				= std::max<ulint>(
-				      (ulint) mach_read_from_8(data), 1);
+				= std::max(mach_read_from_4(data + 4), 1U);
 			break;
 		}
 
@@ -3174,18 +3169,9 @@ dict_stats_fetch_table_stats_step(
 			ut_a(dtype_get_mtype(type) == DATA_INT);
 			ut_a(len == 8);
 
-			ulint stat_other_idx_size
-				= (ulint) mach_read_from_8(data);
-			if (!stat_other_idx_size
-			    && UT_LIST_GET_LEN(table->indexes) > 1) {
-				stat_other_idx_size
-					= UT_LIST_GET_LEN(table->indexes) - 1;
-			}
-			table->stat_sum_of_other_index_sizes
-				= std::max<ulint>(
-				    (ulint) mach_read_from_8(data),
-				    UT_LIST_GET_LEN(table->indexes) - 1);
-
+			table->stat_sum_of_other_index_sizes = std::max(
+				mach_read_from_4(data + 4),
+				uint32_t(UT_LIST_GET_LEN(table->indexes) - 1));
 			break;
 		}
 		default:
@@ -3370,14 +3356,12 @@ dict_stats_fetch_index_stats_step(
 
 	if (stat_name_len == 4 /* strlen("size") */
 	    && strncasecmp("size", stat_name, stat_name_len) == 0) {
-		index->stat_index_size
-			= std::max<ulint>((ulint) stat_value, 1);
+		index->stat_index_size = std::max(uint32_t(stat_value), 1U);
 		arg->stats_were_modified = true;
 	} else if (stat_name_len == 12 /* strlen("n_leaf_pages") */
 		   && strncasecmp("n_leaf_pages", stat_name, stat_name_len)
 		   == 0) {
-		index->stat_n_leaf_pages
-			= std::max<ulint>((ulint) stat_value, 1);
+		index->stat_n_leaf_pages = std::max(uint32_t(stat_value), 1U);
 		arg->stats_were_modified = true;
 	} else if (stat_name_len == 12 /* strlen("n_page_split") */
 		   && strncasecmp("n_page_split", stat_name, stat_name_len)
@@ -3648,7 +3632,9 @@ dict_stats_update_for_index(
 {
 	DBUG_ENTER("dict_stats_update_for_index");
 
-	if (dict_stats_is_persistent_enabled(index->table)) {
+	ut_ad(index->table->stat_initialized());
+
+	if (index->table->stats_is_persistent()) {
 
 		if (dict_stats_persistent_storage_check(false)) {
 			index_stats_t stats = dict_stats_analyze_index(index);
@@ -3789,7 +3775,7 @@ dict_stats_update(
 		/* If table is using persistent stats,
 		then save the stats on disk */
 
-		if (dict_stats_is_persistent_enabled(table)) {
+		if (table->stats_is_persistent()) {
 
 			if (dict_stats_persistent_storage_check(false)) {
 
@@ -3806,7 +3792,7 @@ dict_stats_update(
 		/* fetch requested, either fetch from persistent statistics
 		storage or use the old method */
 
-		if (table->stat_initialized) {
+		if (table->stat_initialized()) {
 			return(DB_SUCCESS);
 		}
 
@@ -3850,7 +3836,7 @@ dict_stats_update(
 				goto transient;
 			}
 #endif
-			if (dict_stats_auto_recalc_is_enabled(table)) {
+			if (table->stats_is_auto_recalc()) {
 				return(dict_stats_update(
 						table,
 						DICT_STATS_RECALC_PERSISTENT));
diff --git a/storage/innobase/dict/dict0stats_bg.cc b/storage/innobase/dict/dict0stats_bg.cc
index b0c34dc6d3040..e44e97e2b8eb9 100644
--- a/storage/innobase/dict/dict0stats_bg.cc
+++ b/storage/innobase/dict/dict0stats_bg.cc
@@ -135,7 +135,9 @@ schedule new estimates for table and index statistics to be calculated.
 void dict_stats_update_if_needed_func(dict_table_t *table)
 #endif
 {
-	if (UNIV_UNLIKELY(!table->stat_initialized)) {
+        uint32_t stat{table->stat};
+
+	if (UNIV_UNLIKELY(!table->stat_initialized(stat))) {
 		/* The table may have been evicted from dict_sys
 		and reloaded internally by InnoDB for FOREIGN KEY
 		processing, but not reloaded by the SQL layer.
@@ -154,13 +156,9 @@ void dict_stats_update_if_needed_func(dict_table_t *table)
 	ulonglong	counter = table->stat_modified_counter++;
 	ulonglong	n_rows = dict_table_get_n_rows(table);
 
-	if (dict_stats_is_persistent_enabled(table)) {
-		if (table->name.is_temporary()) {
-			return;
-		}
-		if (counter > n_rows / 10 /* 10% */
-		    && dict_stats_auto_recalc_is_enabled(table)) {
-
+	if (table->stats_is_persistent(stat)) {
+		if (table->stats_is_auto_recalc(stat)
+		    && counter > n_rows / 10 && !table->name.is_temporary()) {
 #ifdef WITH_WSREP
 			/* Do not add table to background
 			statistic calculation if this thread is not a
diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc
index 677e9b68662fe..0fb03a0cd41d2 100644
--- a/storage/innobase/fsp/fsp0fsp.cc
+++ b/storage/innobase/fsp/fsp0fsp.cc
@@ -1644,12 +1644,11 @@ fseg_find_last_used_frag_page_slot(
 /** Calculate reserved fragment page slots.
 @param inode  file segment index
 @return number of fragment pages */
-static ulint fseg_get_n_frag_pages(const fseg_inode_t *inode)
+static uint32_t fseg_get_n_frag_pages(const fseg_inode_t *inode) noexcept
 {
-	ulint	i;
-	ulint	count	= 0;
+	uint32_t count = 0;
 
-	for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
+	for (ulint i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
 		if (FIL_NULL != fseg_get_nth_frag_page_no(inode, i)) {
 			count++;
 		}
@@ -1794,21 +1793,24 @@ Calculates the number of pages reserved by a segment, and how many pages are
 currently used.
 @return number of reserved pages */
 static
-ulint
+uint32_t
 fseg_n_reserved_pages_low(
 /*======================*/
 	const fseg_inode_t*	inode,	/*!< in: segment inode */
-	ulint*		used)	/*!< out: number of pages used (not
+	uint32_t*		used)	/*!< out: number of pages used (not
 				more than reserved) */
+	noexcept
 {
+	const uint32_t extent_size = FSP_EXTENT_SIZE;
+
 	*used = mach_read_from_4(inode + FSEG_NOT_FULL_N_USED)
-		+ FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL)
+		+ extent_size * flst_get_len(inode + FSEG_FULL)
 		+ fseg_get_n_frag_pages(inode);
 
 	return fseg_get_n_frag_pages(inode)
-		+ FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FREE)
-		+ FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_NOT_FULL)
-		+ FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL);
+		+ extent_size * flst_get_len(inode + FSEG_FREE)
+		+ extent_size * flst_get_len(inode + FSEG_NOT_FULL)
+		+ extent_size * flst_get_len(inode + FSEG_FULL);
 }
 
 /** Calculate the number of pages reserved by a segment,
@@ -1818,9 +1820,9 @@ and how many pages are currently used.
 @param[out]     used    number of pages that are used (not more than reserved)
 @param[in,out]  mtr     mini-transaction
 @return number of reserved pages */
-ulint fseg_n_reserved_pages(const buf_block_t &block,
-                            const fseg_header_t *header, ulint *used,
-                            mtr_t *mtr)
+uint32_t fseg_n_reserved_pages(const buf_block_t &block,
+                               const fseg_header_t *header, uint32_t *used,
+                               mtr_t *mtr) noexcept
 {
   ut_ad(page_align(header) == block.page.frame);
   buf_block_t *iblock;
@@ -1845,7 +1847,7 @@ static dberr_t fseg_fill_free_list(const fseg_inode_t *inode,
                                    buf_block_t *iblock, fil_space_t *space,
                                    uint32_t hint, mtr_t *mtr)
 {
-  ulint	used;
+  uint32_t used;
 
   ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE));
   ut_d(space->modify_check(*mtr));
@@ -1996,8 +1998,7 @@ fseg_alloc_free_page_low(
 	dberr_t*		err)
 {
 	ib_id_t		seg_id;
-	ulint		used;
-	ulint		reserved;
+	uint32_t	used, reserved;
 	xdes_t*		descr;		/*!< extent of the hinted page */
 	uint32_t	ret_page;	/*!< the allocated page offset, FIL_NULL
 					if could not be allocated */
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 7e9439a760c2c..faec14102362c 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -1622,7 +1622,7 @@ inline void ha_innobase::reload_statistics()
     if (table->is_readable())
       dict_stats_init(table);
     else
-      table->stat_initialized= 1;
+      table->stat.fetch_or(dict_table_t::STATS_INITIALIZED);
   }
 }
 
@@ -2978,6 +2978,44 @@ static int innobase_rollback_by_xid(handlerton*, XID *xid) noexcept
   return XAER_NOTA;
 }
 
+/** Initialize the InnoDB persistent statistics attributes.
+@param table           InnoDB table
+@param table_options   MariaDB table options
+@param sar             the value of STATS_AUTO_RECALC
+@param initialized     whether the InnoDB statistics were already initialized
+@return whether table->stats_sample_pages needs to be initialized */
+static bool innodb_copy_stat_flags(dict_table_t *table,
+                                   ulong table_options,
+                                   enum_stats_auto_recalc sar,
+                                   bool initialized) noexcept
+{
+  if (table->is_temporary() || table->no_rollback())
+  {
+    table->stat= dict_table_t::STATS_INITIALIZED |
+      dict_table_t::STATS_PERSISTENT_OFF | dict_table_t::STATS_AUTO_RECALC_OFF;
+    table->stats_sample_pages= 1;
+    return false;
+  }
+
+  static_assert(HA_OPTION_STATS_PERSISTENT ==
+                dict_table_t::STATS_PERSISTENT_ON << 11, "");
+  static_assert(HA_OPTION_NO_STATS_PERSISTENT ==
+                dict_table_t::STATS_PERSISTENT_OFF << 11, "");
+  uint32_t stat=
+    (table_options &
+     (HA_OPTION_STATS_PERSISTENT | HA_OPTION_NO_STATS_PERSISTENT)) >> 11;
+  static_assert(uint32_t{HA_STATS_AUTO_RECALC_ON} << 3 ==
+                dict_table_t::STATS_AUTO_RECALC_ON, "");
+  static_assert(uint32_t{HA_STATS_AUTO_RECALC_OFF} << 3 ==
+                dict_table_t::STATS_AUTO_RECALC_OFF, "");
+  static_assert(true == dict_table_t::STATS_INITIALIZED, "");
+  stat|= (sar & (HA_STATS_AUTO_RECALC_ON | HA_STATS_AUTO_RECALC_OFF)) << 3 |
+    initialized;
+
+  table->stat= stat;
+  return true;
+}
+
 /*********************************************************************//**
 Copy table flags from MySQL's HA_CREATE_INFO into an InnoDB table object.
 Those flags are stored in .frm file and end up in the MySQL table object,
@@ -2990,29 +3028,9 @@ innobase_copy_frm_flags_from_create_info(
 	dict_table_t*		innodb_table,	/*!< in/out: InnoDB table */
 	const HA_CREATE_INFO*	create_info)	/*!< in: create info */
 {
-	ibool	ps_on;
-	ibool	ps_off;
-
-	if (innodb_table->is_temporary()
-	    || innodb_table->no_rollback()) {
-		/* Temp tables do not use persistent stats. */
-		ps_on = FALSE;
-		ps_off = TRUE;
-	} else {
-		ps_on = create_info->table_options
-			& HA_OPTION_STATS_PERSISTENT;
-		ps_off = create_info->table_options
-			& HA_OPTION_NO_STATS_PERSISTENT;
-	}
-
-	dict_stats_set_persistent(innodb_table, ps_on, ps_off);
-
-	dict_stats_auto_recalc_set(
-		innodb_table,
-		create_info->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON,
-		create_info->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF);
-
-	innodb_table->stats_sample_pages = create_info->stats_sample_pages;
+  if (innodb_copy_stat_flags(innodb_table, create_info->table_options,
+                             create_info->stats_auto_recalc, false))
+    innodb_table->stats_sample_pages= create_info->stats_sample_pages;
 }
 
 /*********************************************************************//**
@@ -3026,28 +3044,10 @@ innobase_copy_frm_flags_from_table_share(
 	dict_table_t*		innodb_table,	/*!< in/out: InnoDB table */
 	const TABLE_SHARE*	table_share)	/*!< in: table share */
 {
-	ibool	ps_on;
-	ibool	ps_off;
-
-	if (innodb_table->is_temporary()) {
-		/* Temp tables do not use persistent stats */
-		ps_on = FALSE;
-		ps_off = TRUE;
-	} else {
-		ps_on = table_share->db_create_options
-			& HA_OPTION_STATS_PERSISTENT;
-		ps_off = table_share->db_create_options
-			& HA_OPTION_NO_STATS_PERSISTENT;
-	}
-
-	dict_stats_set_persistent(innodb_table, ps_on, ps_off);
-
-	dict_stats_auto_recalc_set(
-		innodb_table,
-		table_share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON,
-		table_share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF);
-
-	innodb_table->stats_sample_pages = table_share->stats_sample_pages;
+  if (innodb_copy_stat_flags(innodb_table, table_share->db_create_options,
+                             table_share->stats_auto_recalc,
+                             innodb_table->stat_initialized()))
+    innodb_table->stats_sample_pages= table_share->stats_sample_pages;
 }
 
 /*********************************************************************//**
@@ -13387,6 +13387,8 @@ ha_innobase::discard_or_import_tablespace(
 		DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
 	}
 
+	ut_ad(m_prebuilt->table->stat_initialized());
+
 	if (m_prebuilt->table->space == fil_system.sys_space) {
 		ib_senderrf(
 			m_prebuilt->trx->mysql_thd, IB_LOG_LEVEL_ERROR,
@@ -13460,7 +13462,7 @@ ha_innobase::discard_or_import_tablespace(
 				    err, m_prebuilt->table->flags, NULL));
 	}
 
-	if (dict_stats_is_persistent_enabled(m_prebuilt->table)) {
+	if (m_prebuilt->table->stats_is_persistent()) {
 		dberr_t		ret;
 
 		/* Adjust the persistent statistics. */
@@ -13645,7 +13647,7 @@ int ha_innobase::delete_table(const char *name)
     /* This looks like the rollback of ALTER TABLE...ADD PARTITION
     that was caused by MDL timeout. We could have written undo log
     for inserting the data into the new partitions. */
-    if (table->stat_persistent != DICT_STATS_PERSISTENT_OFF)
+    if (!(table->stat & dict_table_t::STATS_PERSISTENT_OFF))
     {
       /* We do not really know if we are holding MDL_EXCLUSIVE. Even
       though this code is handling the case that we are not holding
@@ -13660,7 +13662,7 @@ int ha_innobase::delete_table(const char *name)
 
   DEBUG_SYNC(thd, "before_delete_table_stats");
 
-  if (err == DB_SUCCESS && dict_stats_is_persistent_enabled(table) &&
+  if (err == DB_SUCCESS && table->stats_is_persistent() &&
       !table->is_stats_table())
   {
     table_stats= dict_table_open_on_name(TABLE_STATS_NAME, false,
@@ -14033,7 +14035,7 @@ int ha_innobase::truncate()
     std::this_thread::sleep_for(std::chrono::milliseconds(50));
   }
 
-  if (error == DB_SUCCESS && dict_stats_is_persistent_enabled(ib_table) &&
+  if (error == DB_SUCCESS && ib_table->stats_is_persistent() &&
       !ib_table->is_stats_table())
   {
     table_stats= dict_table_open_on_name(TABLE_STATS_NAME, false,
@@ -14529,7 +14531,7 @@ ha_innobase::scan_time()
 
 	ulint	stat_clustered_index_size;
 
-	ut_a(m_prebuilt->table->stat_initialized);
+	ut_ad(m_prebuilt->table->stat_initialized());
 
 	stat_clustered_index_size =
 		m_prebuilt->table->stat_clustered_index_size;
@@ -14656,7 +14658,7 @@ innodb_rec_per_key(
 	rec_per_key_t	rec_per_key;
 	ib_uint64_t	n_diff;
 
-	ut_a(index->table->stat_initialized);
+	ut_ad(index->table->stat_initialized());
 
 	ut_ad(i < dict_index_get_n_unique(index));
 	ut_ad(!dict_index_is_spatial(index));
@@ -14806,7 +14808,7 @@ ha_innobase::info_low(
 
 			m_prebuilt->trx->op_info = "updating table statistics";
 
-			if (dict_stats_is_persistent_enabled(ib_table)) {
+			if (ib_table->stats_is_persistent()) {
 				if (is_analyze) {
 					if (!srv_read_only_mode) {
 						dict_stats_recalc_pool_del(
@@ -14844,7 +14846,7 @@ ha_innobase::info_low(
 		ulint	stat_clustered_index_size;
 		ulint	stat_sum_of_other_index_sizes;
 
-		ut_a(ib_table->stat_initialized);
+		ut_ad(ib_table->stat_initialized());
 
 #if !defined NO_ELISION && !defined SUX_LOCK_GENERIC
 		if (xbegin()) {
@@ -14998,7 +15000,7 @@ ha_innobase::info_low(
 		auto _ = make_scope_exit([ib_table]() {
 			ib_table->stats_shared_unlock(); });
 
-		ut_a(ib_table->stat_initialized);
+		ut_ad(ib_table->stat_initialized());
 
 		for (uint i = 0; i < table->s->keys; i++) {
 			ulong	j;
@@ -15925,8 +15927,7 @@ ha_innobase::extra(
 			/* During copy alter operation, InnoDB
 			updates the stats only for non-persistent
 			tables. */
-			if (!dict_stats_is_persistent_enabled(
-					m_prebuilt->table)) {
+			if (!m_prebuilt->table->stats_is_persistent()) {
 				dict_stats_update_if_needed(
 					m_prebuilt->table, *trx);
 			}
@@ -19156,12 +19157,12 @@ static MYSQL_SYSVAR_BOOL(stats_on_metadata, innobase_stats_on_metadata,
   " SHOW TABLE STATUS for tables that use transient statistics (off by default)",
   NULL, NULL, FALSE);
 
-static MYSQL_SYSVAR_ULONGLONG(stats_transient_sample_pages,
+static MYSQL_SYSVAR_UINT(stats_transient_sample_pages,
   srv_stats_transient_sample_pages,
   PLUGIN_VAR_RQCMDARG,
   "The number of leaf index pages to sample when calculating transient"
   " statistics (if persistent statistics are not used, default 8)",
-  NULL, NULL, 8, 1, ~0ULL, 0);
+  NULL, NULL, 8, 1, ~0U, 0);
 
 static MYSQL_SYSVAR_BOOL(stats_persistent, srv_stats_persistent,
   PLUGIN_VAR_OPCMDARG,
@@ -19177,12 +19178,12 @@ static MYSQL_SYSVAR_BOOL(stats_auto_recalc, srv_stats_auto_recalc,
   " new statistics)",
   NULL, NULL, TRUE);
 
-static MYSQL_SYSVAR_ULONGLONG(stats_persistent_sample_pages,
+static MYSQL_SYSVAR_UINT(stats_persistent_sample_pages,
   srv_stats_persistent_sample_pages,
   PLUGIN_VAR_RQCMDARG,
   "The number of leaf index pages to sample when calculating persistent"
   " statistics (by ANALYZE, default 20)",
-  NULL, NULL, 20, 1, ~0ULL, 0);
+  NULL, NULL, 20, 1, ~0U, 0);
 
 static MYSQL_SYSVAR_ULONGLONG(stats_modified_counter, srv_stats_modified_counter,
   PLUGIN_VAR_RQCMDARG,
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index 798c90133d331..b808c74b2622e 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -11180,7 +11180,7 @@ alter_stats_norebuild(
 	DBUG_ENTER("alter_stats_norebuild");
 	DBUG_ASSERT(!ctx->need_rebuild());
 
-	if (!dict_stats_is_persistent_enabled(ctx->new_table)) {
+	if (!ctx->new_table->stats_is_persistent()) {
 		DBUG_VOID_RETURN;
 	}
 
@@ -11214,8 +11214,7 @@ alter_stats_rebuild(
 {
 	DBUG_ENTER("alter_stats_rebuild");
 
-	if (!table->space
-	    || !dict_stats_is_persistent_enabled(table)) {
+	if (!table->space || !table->stats_is_persistent()) {
 		DBUG_VOID_RETURN;
 	}
 
diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc
index 0f00a0d39f931..6f0987f153e52 100644
--- a/storage/innobase/handler/i_s.cc
+++ b/storage/innobase/handler/i_s.cc
@@ -4777,9 +4777,9 @@ i_s_dict_fill_sys_tablestats(THD* thd, dict_table_t *table,
 
     OK(field_store_string(fields[SYS_TABLESTATS_NAME],
                           table->name.m_name));
-    OK(fields[SYS_TABLESTATS_INIT]->store(table->stat_initialized, true));
+    OK(fields[SYS_TABLESTATS_INIT]->store(table->stat_initialized(), true));
 
-    if (table->stat_initialized)
+    if (table->stat_initialized())
     {
       OK(fields[SYS_TABLESTATS_NROW]->store(table->stat_n_rows, true));
 
diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc
index d90527c0107af..b594b86d1eaf7 100644
--- a/storage/innobase/ibuf/ibuf0ibuf.cc
+++ b/storage/innobase/ibuf/ibuf0ibuf.cc
@@ -375,7 +375,7 @@ ibuf_size_update(
 	ibuf.free_list_len = flst_get_len(root + PAGE_HEADER
 					   + PAGE_BTR_IBUF_FREE_LIST);
 
-	ibuf.height = 1 + btr_page_get_level(root);
+	ibuf.height = uint8_t(1 + btr_page_get_level(root));
 
 	/* the '1 +' is the ibuf header page */
 	ibuf.size = ibuf.seg_size - (1 + ibuf.free_list_len);
@@ -443,18 +443,11 @@ ibuf_init_at_db_start(void)
 		goto err_exit;
 	}
 
-	/* At startup we intialize ibuf to have a maximum of
-	CHANGE_BUFFER_DEFAULT_SIZE in terms of percentage of the
-	buffer pool size. Once ibuf struct is initialized this
-	value is updated with the user supplied size by calling
-	ibuf_max_size_update(). */
-	ibuf.max_size = ((buf_pool_get_curr_size() >> srv_page_size_shift)
-			  * CHANGE_BUFFER_DEFAULT_SIZE) / 100;
-
 	mysql_mutex_init(ibuf_mutex_key, &ibuf_mutex, nullptr);
 	mysql_mutex_init(ibuf_pessimistic_insert_mutex_key,
 			 &ibuf_pessimistic_insert_mutex, nullptr);
 
+	ibuf_max_size_update(CHANGE_BUFFER_DEFAULT_SIZE);
 	mysql_mutex_lock(&ibuf_mutex);
 	ibuf_size_update(root);
 	mysql_mutex_unlock(&ibuf_mutex);
@@ -506,10 +499,12 @@ ibuf_max_size_update(
 				percentage of the buffer pool size */
 {
 	if (UNIV_UNLIKELY(!ibuf.index)) return;
-	ulint	new_size = ((buf_pool_get_curr_size() >> srv_page_size_shift)
-			    * new_val) / 100;
+	ulint	new_size = std::min<ulint>(
+		(buf_pool_get_curr_size() >> srv_page_size_shift) * new_val
+		/ 100, uint32_t(~0U));
+
 	mysql_mutex_lock(&ibuf_mutex);
-	ibuf.max_size = new_size;
+	ibuf.max_size = uint32_t(new_size);
 	mysql_mutex_unlock(&ibuf_mutex);
 }
 
@@ -4483,17 +4478,17 @@ ibuf_print(
     return;
   }
 
-  const ulint size= ibuf.size;
-  const ulint free_list_len= ibuf.free_list_len;
-  const ulint seg_size= ibuf.seg_size;
+  const uint32_t size= ibuf.size;
+  const uint32_t free_list_len= ibuf.free_list_len;
+  const uint32_t seg_size= ibuf.seg_size;
   mysql_mutex_unlock(&ibuf_mutex);
 
   fprintf(file,
           "-------------\n"
           "INSERT BUFFER\n"
           "-------------\n"
-          "size " ULINTPF ", free list len " ULINTPF ","
-          " seg size " ULINTPF ", " ULINTPF " merges\n",
+          "size %" PRIu32 ", free list len %" PRIu32 ","
+          " seg size %" PRIu32 ", " ULINTPF " merges\n",
           size, free_list_len, seg_size, ulint{ibuf.n_merges});
   ibuf_print_ops("merged operations:\n", ibuf.n_merged_ops, file);
   ibuf_print_ops("discarded operations:\n", ibuf.n_discarded_ops, file);
diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h
index 97568c61997f5..842b9fec9cd15 100644
--- a/storage/innobase/include/dict0dict.h
+++ b/storage/innobase/include/dict0dict.h
@@ -674,7 +674,7 @@ TPOOL_SUPPRESS_TSAN
 @return estimated number of rows */
 inline uint64_t dict_table_get_n_rows(const dict_table_t *table)
 {
-  ut_ad(table->stat_initialized);
+  ut_ad(table->stat_initialized());
   return table->stat_n_rows;
 }
 
diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h
index ffc2651067935..bec735537fc2b 100644
--- a/storage/innobase/include/dict0mem.h
+++ b/storage/innobase/include/dict0mem.h
@@ -1106,10 +1106,10 @@ struct dict_index_t {
 				is indexed from 0 to n_uniq-1); This
 				is used when innodb_stats_method is
 				"nulls_ignored". */
-	ulint		stat_index_size;
+	uint32_t	stat_index_size;
 				/*!< approximate index size in
 				database pages */
-	ulint		stat_n_leaf_pages;
+	uint32_t	stat_n_leaf_pages;
 				/*!< approximate number of leaf pages in the
 				index tree */
 	bool		stats_error_printed;
@@ -2358,63 +2358,32 @@ struct dict_table_t {
 	/** Statistics for query optimization. Mostly protected by
 	dict_sys.latch and stats_mutex_lock(). @{ */
 
-	/** TRUE if statistics have been calculated the first time after
-	database startup or table creation. */
-	unsigned				stat_initialized:1;
-
 	/** Timestamp of last recalc of the stats. */
 	time_t					stats_last_recalc;
 
-	/** The two bits below are set in the 'stat_persistent' member. They
-	have the following meaning:
-	1. _ON=0, _OFF=0, no explicit persistent stats setting for this table,
-	the value of the global srv_stats_persistent is used to determine
-	whether the table has persistent stats enabled or not
-	2. _ON=0, _OFF=1, persistent stats are explicitly disabled for this
-	table, regardless of the value of the global srv_stats_persistent
-	3. _ON=1, _OFF=0, persistent stats are explicitly enabled for this
-	table, regardless of the value of the global srv_stats_persistent
-	4. _ON=1, _OFF=1, not allowed, we assert if this ever happens. */
-	#define DICT_STATS_PERSISTENT_ON	(1 << 1)
-	#define DICT_STATS_PERSISTENT_OFF	(1 << 2)
-
-	/** Indicates whether the table uses persistent stats or not. See
-	DICT_STATS_PERSISTENT_ON and DICT_STATS_PERSISTENT_OFF. */
-	ib_uint32_t				stat_persistent;
-
-	/** The two bits below are set in the 'stats_auto_recalc' member. They
-	have the following meaning:
-	1. _ON=0, _OFF=0, no explicit auto recalc setting for this table, the
-	value of the global srv_stats_persistent_auto_recalc is used to
-	determine whether the table has auto recalc enabled or not
-	2. _ON=0, _OFF=1, auto recalc is explicitly disabled for this table,
-	regardless of the value of the global srv_stats_persistent_auto_recalc
-	3. _ON=1, _OFF=0, auto recalc is explicitly enabled for this table,
-	regardless of the value of the global srv_stats_persistent_auto_recalc
-	4. _ON=1, _OFF=1, not allowed, we assert if this ever happens. */
-	#define DICT_STATS_AUTO_RECALC_ON	(1 << 1)
-	#define DICT_STATS_AUTO_RECALC_OFF	(1 << 2)
-
-	/** Indicates whether the table uses automatic recalc for persistent
-	stats or not. See DICT_STATS_AUTO_RECALC_ON and
-	DICT_STATS_AUTO_RECALC_OFF. */
-	ib_uint32_t				stats_auto_recalc;
-
-	/** The number of pages to sample for this table during persistent
-	stats estimation. If this is 0, then the value of the global
-	srv_stats_persistent_sample_pages will be used instead. */
-	ulint					stats_sample_pages;
+  static constexpr uint32_t STATS_INITIALIZED= 1U;
+  static constexpr uint32_t STATS_PERSISTENT_ON= 1U << 1;
+  static constexpr uint32_t STATS_PERSISTENT_OFF= 1U << 2;
+  static constexpr uint32_t STATS_AUTO_RECALC_ON= 1U << 3;
+  static constexpr uint32_t STATS_AUTO_RECALC_OFF= 1U << 4;
+
+  /** flags for index cardinality statistics */
+  Atomic_relaxed<uint32_t> stat;
+  /** Approximate clustered index size in database pages. */
+  uint32_t stat_clustered_index_size;
+  /** Approximate size of other indexes in database pages. */
+  uint32_t stat_sum_of_other_index_sizes;
+
+
+  /** The number of pages to sample for this table during persistent
+  stats estimation. If this is 0, then the value of the global
+  srv_stats_persistent_sample_pages will be used instead. */
+  uint32_t stats_sample_pages;
 
 	/** Approximate number of rows in the table. We periodically calculate
 	new estimates. */
 	ib_uint64_t				stat_n_rows;
 
-	/** Approximate clustered index size in database pages. */
-	ulint					stat_clustered_index_size;
-
-	/** Approximate size of other indexes in database pages. */
-	ulint					stat_sum_of_other_index_sizes;
-
 	/** How many rows are modified since last stats recalc. When a row is
 	inserted, updated, or deleted, we add 1 to this number; we calculate
 	new estimates for the table and the indexes if the table has changed
@@ -2551,6 +2520,35 @@ struct dict_table_t {
   /** @return the index for that starts with a specific column */
   dict_index_t *get_index(const dict_col_t &col) const;
 
+  /** @return whether the statistics are initialized */
+  static bool stat_initialized(uint32_t stat) noexcept
+  { return stat & STATS_INITIALIZED; }
+
+  /** @return whether STATS_PERSISTENT is enabled */
+  static bool stats_is_persistent(uint32_t stat) noexcept
+  {
+    ut_ad(~(stat & (STATS_PERSISTENT_ON | STATS_PERSISTENT_OFF)));
+    if (stat & STATS_PERSISTENT_ON) return true;
+    return !(stat & STATS_PERSISTENT_OFF) && srv_stats_persistent;
+  }
+  /** @return whether STATS_AUTO_RECALC is enabled */
+  static bool stats_is_auto_recalc(uint32_t stat) noexcept
+  {
+    ut_ad(stat_initialized(stat));
+    ut_ad(~(stat & (STATS_AUTO_RECALC_ON | STATS_AUTO_RECALC_OFF)));
+    if (stat & STATS_AUTO_RECALC_ON) return true;
+    return !(stat & STATS_AUTO_RECALC_OFF) && srv_stats_auto_recalc;
+  }
+
+  /** @return whether the statistics are initialized */
+  bool stat_initialized() const noexcept { return stat_initialized(stat); }
+  /** @return whether STATS_PERSISTENT is enabled */
+  bool stats_is_persistent() const noexcept
+  { return stats_is_persistent(stat); }
+  /** @return whether STATS_AUTO_RECALC is enabled */
+  bool stats_is_auto_recalc() const noexcept
+  { return stats_is_auto_recalc(stat); }
+
   /** Create metadata.
   @param name     table name
   @param space    tablespace
diff --git a/storage/innobase/include/dict0stats.h b/storage/innobase/include/dict0stats.h
index 3b006daf53c5c..e1d9893dd0aec 100644
--- a/storage/innobase/include/dict0stats.h
+++ b/storage/innobase/include/dict0stats.h
@@ -52,44 +52,6 @@ enum dict_stats_upd_option_t {
 				otherwise do nothing */
 };
 
-/*********************************************************************//**
-Set the persistent statistics flag for a given table. This is set only
-in the in-memory table object and is not saved on disk. It will be read
-from the .frm file upon first open from MySQL after a server restart. */
-UNIV_INLINE
-void
-dict_stats_set_persistent(
-/*======================*/
-	dict_table_t*	table,	/*!< in/out: table */
-	ibool		ps_on,	/*!< in: persistent stats explicitly enabled */
-	ibool		ps_off)	/*!< in: persistent stats explicitly disabled */
-	MY_ATTRIBUTE((nonnull));
-
-/** @return whether persistent statistics is enabled for a given table */
-UNIV_INLINE
-bool
-dict_stats_is_persistent_enabled(const dict_table_t* table)
-	MY_ATTRIBUTE((nonnull, warn_unused_result));
-
-/*********************************************************************//**
-Set the auto recalc flag for a given table (only honored for a persistent
-stats enabled table). The flag is set only in the in-memory table object
-and is not saved in InnoDB files. It will be read from the .frm file upon
-first open from MySQL after a server restart. */
-UNIV_INLINE
-void
-dict_stats_auto_recalc_set(
-/*=======================*/
-	dict_table_t*	table,			/*!< in/out: table */
-	ibool		auto_recalc_on,		/*!< in: explicitly enabled */
-	ibool		auto_recalc_off);	/*!< in: explicitly disabled */
-
-/** @return whether auto recalc is enabled for a given table*/
-UNIV_INLINE
-bool
-dict_stats_auto_recalc_is_enabled(const dict_table_t* table)
-	MY_ATTRIBUTE((nonnull, warn_unused_result));
-
 /*********************************************************************//**
 Initialize table's stats for the first time when opening a table. */
 UNIV_INLINE
diff --git a/storage/innobase/include/dict0stats.inl b/storage/innobase/include/dict0stats.inl
index dd51627515652..fa5b4cccf1905 100644
--- a/storage/innobase/include/dict0stats.inl
+++ b/storage/innobase/include/dict0stats.inl
@@ -25,120 +25,6 @@ Created Jan 23, 2012 Vasil Dimov
 *******************************************************/
 
 #include "dict0dict.h"
-#include "srv0srv.h"
-
-/*********************************************************************//**
-Set the persistent statistics flag for a given table. This is set only
-in the in-memory table object and is not saved on disk. It will be read
-from the .frm file upon first open from MySQL after a server restart. */
-UNIV_INLINE
-void
-dict_stats_set_persistent(
-/*======================*/
-	dict_table_t*	table,	/*!< in/out: table */
-	ibool		ps_on,	/*!< in: persistent stats explicitly enabled */
-	ibool		ps_off)	/*!< in: persistent stats explicitly disabled */
-{
-	/* Not allowed to have both flags set, but a CREATE or ALTER
-	statement that contains "STATS_PERSISTENT=0 STATS_PERSISTENT=1" would
-	end up having both set. In this case we clear the OFF flag. */
-	if (ps_on && ps_off) {
-		ps_off = FALSE;
-	}
-
-	ib_uint32_t	stat_persistent = 0;
-
-	if (ps_on) {
-		stat_persistent |= DICT_STATS_PERSISTENT_ON;
-	}
-
-	if (ps_off) {
-		stat_persistent |= DICT_STATS_PERSISTENT_OFF;
-	}
-
-	/* we rely on this assignment to be atomic */
-	table->stat_persistent = stat_persistent;
-}
-
-/** @return whether persistent statistics is enabled for a given table */
-UNIV_INLINE
-bool
-dict_stats_is_persistent_enabled(const dict_table_t* table)
-{
-	/* Because of the nature of this check (non-locking) it is possible
-	that a table becomes:
-	* PS-disabled immediately after this function has returned TRUE or
-	* PS-enabled immediately after this function has returned FALSE.
-	This means that it is possible that we do:
-	+ dict_stats_update(DICT_STATS_RECALC_PERSISTENT) on a table that has
-	  just been PS-disabled or
-	+ dict_stats_update(DICT_STATS_RECALC_TRANSIENT) on a table that has
-	  just been PS-enabled.
-	This is acceptable. Avoiding this would mean that we would have to
-	hold dict_sys.latch or stats_mutex_lock() like for accessing the
-	other ::stat_ members which would be too big performance penalty,
-	especially when this function is called from
-	dict_stats_update_if_needed(). */
-
-	/* we rely on this read to be atomic */
-	ib_uint32_t	stat_persistent = table->stat_persistent;
-
-	if (stat_persistent & DICT_STATS_PERSISTENT_ON) {
-		ut_ad(!(stat_persistent & DICT_STATS_PERSISTENT_OFF));
-		return(true);
-	} else if (stat_persistent & DICT_STATS_PERSISTENT_OFF) {
-		return(false);
-	} else {
-		return(srv_stats_persistent);
-	}
-}
-
-/*********************************************************************//**
-Set the auto recalc flag for a given table (only honored for a persistent
-stats enabled table). The flag is set only in the in-memory table object
-and is not saved in InnoDB files. It will be read from the .frm file upon
-first open from MySQL after a server restart. */
-UNIV_INLINE
-void
-dict_stats_auto_recalc_set(
-/*=======================*/
-	dict_table_t*	table,			/*!< in/out: table */
-	ibool		auto_recalc_on,		/*!< in: explicitly enabled */
-	ibool		auto_recalc_off)	/*!< in: explicitly disabled */
-{
-	ut_ad(!auto_recalc_on || !auto_recalc_off);
-
-	ib_uint32_t	stats_auto_recalc = 0;
-
-	if (auto_recalc_on) {
-		stats_auto_recalc |= DICT_STATS_AUTO_RECALC_ON;
-	}
-
-	if (auto_recalc_off) {
-		stats_auto_recalc |= DICT_STATS_AUTO_RECALC_OFF;
-	}
-
-	/* we rely on this assignment to be atomic */
-	table->stats_auto_recalc = stats_auto_recalc;
-}
-
-/** @return whether auto recalc is enabled for a given table*/
-UNIV_INLINE
-bool
-dict_stats_auto_recalc_is_enabled(const dict_table_t* table)
-{
-	/* we rely on this read to be atomic */
-	ib_uint32_t	stats_auto_recalc = table->stats_auto_recalc;
-
-	if (stats_auto_recalc & DICT_STATS_AUTO_RECALC_ON) {
-		ut_ad(!(stats_auto_recalc & DICT_STATS_AUTO_RECALC_OFF));
-		return(true);
-	} else if (stats_auto_recalc & DICT_STATS_AUTO_RECALC_OFF) {
-		return(false);
-	} else {
-		return(srv_stats_auto_recalc);
-	}
-}
 
 /*********************************************************************//**
 Initialize table's stats for the first time when opening a table. */
@@ -150,13 +36,15 @@ dict_stats_init(
 {
 	ut_ad(!table->stats_mutex_is_owner());
 
-	if (table->stat_initialized) {
+	uint32_t stat = table->stat;
+
+	if (stat & dict_table_t::STATS_INITIALIZED) {
 		return;
 	}
 
 	dict_stats_upd_option_t	opt;
 
-	if (dict_stats_is_persistent_enabled(table)) {
+	if (table->stats_is_persistent(stat)) {
 		opt = DICT_STATS_FETCH_ONLY_IF_NOT_IN_MEMORY;
 	} else {
 		opt = DICT_STATS_RECALC_TRANSIENT;
@@ -178,7 +66,7 @@ dict_stats_deinit(
 	ut_ad(table->get_ref_count() == 0);
 
 #ifdef HAVE_valgrind
-	if (!table->stat_initialized) {
+	if (!table->stat_initialized()) {
 		return;
 	}
 
@@ -215,5 +103,6 @@ dict_stats_deinit(
 			sizeof(index->stat_n_leaf_pages));
 	}
 #endif /* HAVE_valgrind */
-	table->stat_initialized = FALSE;
+
+	table->stat = table->stat & ~dict_table_t::STATS_INITIALIZED;
 }
diff --git a/storage/innobase/include/fsp0fsp.h b/storage/innobase/include/fsp0fsp.h
index 4b8dd20a22403..fe8d5980ac43e 100644
--- a/storage/innobase/include/fsp0fsp.h
+++ b/storage/innobase/include/fsp0fsp.h
@@ -355,9 +355,9 @@ and how many pages are currently used.
 @param[out]     used    number of pages that are used (not more than reserved)
 @param[in,out]  mtr     mini-transaction
 @return number of reserved pages */
-ulint fseg_n_reserved_pages(const buf_block_t &block,
-                            const fseg_header_t *header, ulint *used,
-                            mtr_t *mtr)
+uint32_t fseg_n_reserved_pages(const buf_block_t &block,
+                               const fseg_header_t *header, uint32_t *used,
+                               mtr_t *mtr) noexcept
   MY_ATTRIBUTE((nonnull));
 /**********************************************************************//**
 Allocates a single free page from a segment. This function implements
diff --git a/storage/innobase/include/ibuf0ibuf.h b/storage/innobase/include/ibuf0ibuf.h
index c246b2ef513ec..b00532f9a0251 100644
--- a/storage/innobase/include/ibuf0ibuf.h
+++ b/storage/innobase/include/ibuf0ibuf.h
@@ -62,11 +62,11 @@ extern ulong		innodb_change_buffering;
 
 /** Insert buffer struct */
 struct ibuf_t{
-	Atomic_relaxed<ulint> size;	/*!< current size of the ibuf index
+	Atomic_relaxed<uint32_t> size;	/*!< current size of the ibuf index
 					tree, in pages */
-	Atomic_relaxed<ulint> max_size;	/*!< recommended maximum size of the
+	Atomic_relaxed<uint32_t> max_size;/*!< recommended maximum size of the
 					ibuf index tree, in pages */
-	ulint		seg_size;	/*!< allocated pages of the file
+	uint32_t	seg_size;	/*!< allocated pages of the file
 					segment containing ibuf header and
 					tree */
 	bool		empty;		/*!< Protected by the page
@@ -75,8 +75,8 @@ struct ibuf_t{
 					(FSP_IBUF_TREE_ROOT_PAGE_NO). true
 					if and only if the insert
 					buffer tree is empty. */
-	ulint		free_list_len;	/*!< length of the free list */
-	ulint		height;		/*!< tree height */
+	uint8_t		height;		/*!< tree height */
+	uint32_t	free_list_len;	/*!< length of the free list */
 	dict_index_t*	index;		/*!< insert buffer index */
 
 	/** number of pages merged */
diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h
index bca858ca409c0..374b15f0d6615 100644
--- a/storage/innobase/include/srv0srv.h
+++ b/storage/innobase/include/srv0srv.h
@@ -294,9 +294,9 @@ extern uint	srv_fast_shutdown;
 
 extern ibool	srv_innodb_status;
 
-extern unsigned long long	srv_stats_transient_sample_pages;
+extern uint32_t			srv_stats_transient_sample_pages;
 extern my_bool			srv_stats_persistent;
-extern unsigned long long	srv_stats_persistent_sample_pages;
+extern uint32_t			srv_stats_persistent_sample_pages;
 extern my_bool			srv_stats_auto_recalc;
 extern my_bool			srv_stats_include_delete_marked;
 extern unsigned long long	srv_stats_modified_counter;
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index 4c8468dbc41f0..8b20d70fb157c 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -1599,7 +1599,7 @@ row_update_for_mysql(row_prebuilt_t* prebuilt)
 	ut_a(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED);
 	ut_a(prebuilt->magic_n2 == ROW_PREBUILT_ALLOCATED);
 	ut_a(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
-	ut_ad(table->stat_initialized);
+	ut_ad(table->stat_initialized());
 
 	if (!table->is_readable()) {
 		return row_mysql_get_table_error(trx, table);
diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc
index 442dd4f6e1d75..547936e34c1a4 100644
--- a/storage/innobase/row/row0purge.cc
+++ b/storage/innobase/row/row0purge.cc
@@ -1564,7 +1564,7 @@ row_purge_record_func(
 	case TRX_UNDO_DEL_MARK_REC:
 		purged = row_purge_del_mark(node);
 		if (purged) {
-			if (node->table->stat_initialized
+			if (node->table->stat_initialized()
 			    && srv_stats_include_delete_marked) {
 				dict_stats_update_if_needed(
 					node->table, *thr->graph->trx);
diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc
index 23255cc98f90b..b02cbec56ea5c 100644
--- a/storage/innobase/row/row0uins.cc
+++ b/storage/innobase/row/row0uins.cc
@@ -614,7 +614,7 @@ row_undo_ins(
 			err = row_undo_ins_remove_clust_rec(node);
 		}
 
-		if (err == DB_SUCCESS && node->table->stat_initialized) {
+		if (err == DB_SUCCESS && node->table->stat_initialized()) {
 			/* Not protected by dict_sys.latch
 			or table->stats_mutex_lock() for
 			performance reasons, we would rather get garbage
diff --git a/storage/innobase/row/row0umod.cc b/storage/innobase/row/row0umod.cc
index d8a3b29b6304e..a1bc1df3dd2a7 100644
--- a/storage/innobase/row/row0umod.cc
+++ b/storage/innobase/row/row0umod.cc
@@ -1388,7 +1388,7 @@ row_undo_mod(
 		bool update_statistics
 			= !(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE);
 
-		if (err == DB_SUCCESS && node->table->stat_initialized) {
+		if (err == DB_SUCCESS && node->table->stat_initialized()) {
 			switch (node->rec_type) {
 			case TRX_UNDO_UPD_EXIST_REC:
 				break;
diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc
index b3bdfef9c6613..f73a95d522647 100644
--- a/storage/innobase/srv/srv0srv.cc
+++ b/storage/innobase/srv/srv0srv.cc
@@ -291,13 +291,13 @@ this many index pages, there are 2 ways to calculate statistics:
   in the innodb database.
 * quick transient stats, that are used if persistent stats for the given
   table/index are not found in the innodb database */
-unsigned long long	srv_stats_transient_sample_pages;
+uint32_t	srv_stats_transient_sample_pages;
 /** innodb_stats_persistent */
 my_bool		srv_stats_persistent;
 /** innodb_stats_include_delete_marked */
 my_bool		srv_stats_include_delete_marked;
 /** innodb_stats_persistent_sample_pages */
-unsigned long long	srv_stats_persistent_sample_pages;
+uint32_t	srv_stats_persistent_sample_pages;
 /** innodb_stats_auto_recalc */
 my_bool		srv_stats_auto_recalc;
 
