commit e1b19901d5aae33cc3e5ccbf360a4b134df29807
Author: Thirunarayanan Balathandayuthapani <thiru@mariadb.com>
Date:   Sun Apr 6 13:53:00 2025 +0530

    - Remove parser from statistics usage

diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc
index 93ac060fc53..db6c7b3fb95 100644
--- a/storage/innobase/dict/dict0stats.cc
+++ b/storage/innobase/dict/dict0stats.cc
@@ -44,6 +44,8 @@ Created Jan 06, 2010 Vasil Dimov
 #include <map>
 #include <vector>
 #include <thread>
+#include "row0ins.h"
+#include "row0upd.h"
 
 /* Sampling algorithm description @{
 
@@ -176,6 +178,284 @@ dict_stats_should_ignore_index(
   return !index->is_btree() || index->to_be_dropped || !index->is_committed();
 }
 
+static
+dberr_t construct_table_stats_tuple(dict_field_t *f, dtuple_t *tuple,
+                                    mem_heap_t *heap, const char *db,
+                                    const char *tbl= nullptr,
+                                    const time_t *now= nullptr,
+                                    uint64_t *n_rows= nullptr,
+                                    uint32_t *clust_size= nullptr,
+                                    uint32_t *other_index_size= nullptr)
+{
+  dfield_set_data(dtuple_get_nth_field(tuple, 0), db, strlen(db));
+  if (tbl)
+    dfield_set_data(dtuple_get_nth_field(tuple, 1), tbl, strlen(tbl));
+
+  if (tuple->n_fields < 3) {
+    return DB_SUCCESS;
+  }
+
+  uint32_t col_len= 0;
+  byte *data= nullptr;
+  byte *sys_buf= static_cast<byte*>(
+    mem_heap_zalloc(heap, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN));
+  /* DB_TRX_ID */
+  dfield_set_data(dtuple_get_nth_field(tuple, 2), sys_buf, DATA_TRX_ID_LEN);
+  /* DB_ROLL_PTR */
+  dfield_set_data(dtuple_get_nth_field(tuple, 3), &sys_buf[DATA_TRX_ID_LEN],
+                  DATA_ROLL_PTR_LEN);
+
+  if (now)
+  {
+    col_len= f[4].col->len;
+    if (col_len < 4)
+      return DB_CORRUPTION;
+
+    data= static_cast<byte*>(mem_heap_zalloc(heap, col_len));
+    mach_write_to_4(data, *now);
+    dfield_set_data(dtuple_get_nth_field(tuple, 4), data, col_len);
+  }
+
+  if (n_rows)
+  {
+    col_len= f[5].col->len;
+    if (col_len < 8)
+      return DB_CORRUPTION;
+
+    data= static_cast<byte*>(mem_heap_zalloc(heap, col_len));
+    mach_write_to_8(data, *n_rows);
+    dfield_set_data(dtuple_get_nth_field(tuple, 5), data, col_len);
+  }
+
+  if (clust_size)
+  {
+    col_len= f[6].col->len;
+    if (col_len < 8)
+      return DB_CORRUPTION;
+
+    data= static_cast<byte*>(mem_heap_zalloc(heap, col_len));
+    mach_write_to_8(data, *clust_size);
+    dfield_set_data(dtuple_get_nth_field(tuple, 6), data, col_len);
+  }
+
+  if (other_index_size)
+  {
+    col_len= f[7].col->len;
+    if (col_len < 8)
+      return DB_CORRUPTION;
+
+    data= static_cast<byte*>(mem_heap_zalloc(heap, col_len));
+    mach_write_to_8(data, *other_index_size);
+    dfield_set_data(dtuple_get_nth_field(tuple, 7), data, col_len);
+  }
+
+  return DB_SUCCESS;
+}
+
+static
+dberr_t construct_index_stats_tuple(dict_field_t *f, dtuple_t *tuple,
+                                    mem_heap_t *heap, const char *db,
+                                    const char *tbl= nullptr,
+                                    const char *idx_name= nullptr,
+                                    const time_t *last_update= nullptr,
+                                    const char *stat_name= nullptr,
+                                    ib_uint64_t *stat_value= nullptr,
+                                    ib_uint64_t *sample_size= nullptr,
+                                    const char *stat_descr= nullptr)
+{
+  dfield_set_data(dtuple_get_nth_field(tuple, 0), db, strlen(db));
+  if (tbl)
+    dfield_set_data(dtuple_get_nth_field(tuple, 1), tbl, strlen(tbl));
+
+  if (idx_name)
+    dfield_set_data(dtuple_get_nth_field(tuple, 2), idx_name, strlen(idx_name));
+
+  if (stat_name)
+    dfield_set_data(dtuple_get_nth_field(tuple, 3), stat_name, strlen(stat_name));
+
+  if (tuple->n_fields < 4) {
+    return DB_SUCCESS;
+  }
+
+  uint32_t col_len= 0;
+  byte *data= nullptr;
+  byte *sys_buf= static_cast<byte*>(
+    mem_heap_zalloc(heap, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN));
+  /* DB_TRX_ID */
+  dfield_set_data(dtuple_get_nth_field(tuple, 4), sys_buf, DATA_TRX_ID_LEN);
+  /* DB_ROLL_PTR */
+  dfield_set_data(dtuple_get_nth_field(tuple, 5), &sys_buf[DATA_TRX_ID_LEN],
+                  DATA_ROLL_PTR_LEN);
+  if (last_update)
+  {
+    col_len= f[6].col->len;
+    if (col_len < 4)
+      return DB_CORRUPTION;
+
+    data= static_cast<byte*>(mem_heap_zalloc(heap, col_len));
+    mach_write_to_4(data, *last_update);
+    dfield_set_data(dtuple_get_nth_field(tuple, 6), data, col_len);
+  }
+
+  if (stat_value)
+  {
+    col_len= f[7].col->len;
+    if (col_len < 8)
+      return DB_CORRUPTION;
+
+    data= static_cast<byte*>(mem_heap_zalloc(heap, col_len));
+    mach_write_to_8(data, *stat_value);
+    dfield_set_data(dtuple_get_nth_field(tuple, 7), data, col_len);
+  }
+
+  if (tuple->n_fields < 8) {
+    return DB_SUCCESS;
+  }
+
+  if (sample_size)
+  {
+    col_len= f[8].col->len;
+    if (col_len < 8)
+      return DB_CORRUPTION;
+
+    data= static_cast<byte*>(mem_heap_zalloc(heap, col_len));
+    mach_write_to_8(data, *sample_size);
+    dfield_set_data(dtuple_get_nth_field(tuple, 8), data, col_len);
+  }
+  else
+    dfield_set_data(dtuple_get_nth_field(tuple, 8), nullptr, UNIV_SQL_NULL);
+
+  if (stat_descr)
+    dfield_set_data(dtuple_get_nth_field(tuple, 9), stat_descr, strlen(stat_descr));
+
+  return DB_SUCCESS;
+}
+
+static
+dberr_t insert_into_stats(dict_table_t *table, dtuple_t *tuple, trx_t *trx,
+                          mem_heap_t *heap)
+{
+  que_fork_t *fork= que_fork_create(heap);
+  que_thr_t *thr= que_thr_create(fork, heap, nullptr);
+  fork->trx= trx;
+  return row_ins_clust_index_entry(
+	dict_table_get_first_index(table), tuple, thr, 0);
+}
+
+static
+rec_t* matched_rec(btr_pcur_t *pcur, dtuple_t *tuple, mtr_t *mtr,
+                   rec_offs **offsets, mem_heap_t *heap)
+{
+  dict_index_t *index= pcur->index();
+  rec_t *rec= btr_pcur_get_rec(pcur);
+  while (rec_get_deleted_flag(rec, dict_table_is_comp(index->table)))
+  {
+    btr_pcur_move_to_next_user_rec(pcur, mtr);
+    if (!btr_pcur_is_on_user_rec(pcur)) return nullptr;
+    rec= btr_pcur_get_rec(pcur);
+  }
+
+  *offsets= rec_get_offsets(rec, index, nullptr,
+                            index->n_core_fields, ULINT_UNDEFINED,
+                            &heap);
+  return cmp_dtuple_rec(tuple, rec, index, *offsets) != 0 ? nullptr : rec;
+}
+
+struct upd_stats_t {
+  dtuple_t *update_tuple;
+  std::vector<dtuple_t*> new_tuples;
+};
+
+static
+dberr_t delete_from_stats(dict_table_t *table, dtuple_t *tuple, trx_t *trx,
+                          mem_heap_t *heap, upd_stats_t *upd_stats= nullptr)
+{
+  undo_no_t undo_no= trx->undo_no;
+  que_fork_t *fork= que_fork_create(heap);
+  que_thr_t *thr= que_thr_create(fork, heap, nullptr);
+  fork->trx= trx;
+  dberr_t err= DB_SUCCESS;
+  dict_index_t *index= dict_table_get_first_index(table);
+  btr_pcur_t pcur;
+  rec_t *rec= nullptr;
+  buf_block_t *block= nullptr;
+  rec_offs *offsets= nullptr;
+  mtr_t mtr;
+  mtr.start();
+  mtr.set_named_space(table->space);
+  pcur.btr_cur.page_cur.index= index;
+  err= btr_pcur_open_with_no_init(tuple, PAGE_CUR_GE,
+                                  BTR_MODIFY_LEAF, &pcur, &mtr);
+  if (err)
+    goto func_exit;
+
+  while((rec = matched_rec(&pcur, tuple, &mtr, &offsets, heap)))
+  {
+    block= btr_pcur_get_block(&pcur);
+    err= lock_clust_rec_modify_check_and_lock(block, rec, index, offsets, thr);
+    if (err != DB_SUCCESS)
+      break;
+    if (upd_stats)
+    {
+      dtuple_t *insert_tuple= row_rec_to_index_entry(rec, index, offsets,
+                                                     heap);
+      if (insert_tuple)
+      {
+        ut_ad(insert_tuple->n_fields >= upd_stats->update_tuple->n_fields_cmp);
+        for (uint32_t i= 0; i < upd_stats->update_tuple->n_fields_cmp; i++)
+          dfield_copy(dtuple_get_nth_field(insert_tuple, i),
+                      dtuple_get_nth_field(upd_stats->update_tuple, i));
+      }
+      upd_stats->new_tuples.push_back(insert_tuple);
+    }
+    err= btr_cur_del_mark_set_clust_rec(block, rec, index, offsets, thr,
+                                        tuple, &mtr);
+    if (err != DB_SUCCESS)
+      break;
+    block= btr_pcur_get_block(&pcur);
+    block->page.fix();
+    block->page.lock.x_lock();
+    mtr.commit();
+
+    mtr.start();
+    mtr.set_named_space(table->space);
+    pcur.btr_cur.page_cur.index= index;
+    mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
+    page_cur_set_before_first(block, &pcur.btr_cur.page_cur);
+    if (!page_cur_move_to_next(&pcur.btr_cur.page_cur))
+      break;
+  }
+func_exit:
+  btr_pcur_close(&pcur);
+  mtr.commit();
+  trx->error_state= err;
+  if (err != DB_SUCCESS)
+    row_mysql_handle_errors(&err, trx, thr, &undo_no);
+  return err;
+}
+
+static
+dberr_t update_stats(dict_table_t *table, dtuple_t *old_tuple,
+                     dtuple_t *new_tuple, trx_t *trx,
+                     mem_heap_t *heap)
+{
+  upd_stats_t upd_stats;
+  upd_stats.update_tuple= new_tuple;
+  dberr_t err= delete_from_stats(table, old_tuple, trx, heap, &upd_stats);
+  if (err != DB_SUCCESS)
+    return err;
+  dict_index_t *index= dict_table_get_first_index(table);
+  que_fork_t *fork= que_fork_create(heap);
+  que_thr_t *thr= que_thr_create(fork, heap, nullptr);
+  fork->trx= trx;
+  for (dtuple_t *tuple : upd_stats.new_tuples)
+  {
+    err= row_ins_clust_index_entry(index, tuple, thr, 0);
+    if (err)
+      return err;
+  }
+  return err;
+}
 
 /** expected column definition */
 struct dict_col_meta_t
@@ -528,7 +808,7 @@ dict_stats_persistent_storage_check(bool dict_already_locked) noexcept
 		return SCHEMA_INVALID;
 	}
 }
-
+#if 0
 /** Executes a given SQL statement using the InnoDB internal SQL parser.
 This function will free the pinfo object.
 @param[in,out]	pinfo	pinfo to pass to que_eval_sql() must already
@@ -552,6 +832,7 @@ dberr_t dict_stats_exec_sql(pars_info_t *pinfo, const char* sql, trx_t *trx)
   pars_info_free(pinfo);
   return DB_STATS_DO_NOT_EXIST;
 }
+#endif
 
 /*********************************************************************//**
 Write all zeros (or 1 where it makes sense) into an index
@@ -2720,6 +3001,7 @@ storage.
 @return DB_SUCCESS or error code */
 dberr_t
 dict_stats_save_index_stat(
+	dict_table_t *index_stats,
 	dict_index_t*	index,
 	time_t		last_update,
 	const char*	stat_name,
@@ -2729,60 +3011,25 @@ dict_stats_save_index_stat(
 	trx_t*		trx)
 {
 	dberr_t		ret;
-	pars_info_t*	pinfo;
 	char		db_utf8[MAX_DB_UTF8_LEN];
 	char		table_utf8[MAX_TABLE_UTF8_LEN];
 
-	ut_ad(dict_sys.locked());
-
 	dict_fs2utf8(index->table->name.m_name, db_utf8, sizeof(db_utf8),
 		     table_utf8, sizeof(table_utf8));
 
-	pinfo = pars_info_create();
-	pars_info_add_str_literal(pinfo, "database_name", db_utf8);
-	pars_info_add_str_literal(pinfo, "table_name", table_utf8);
-	pars_info_add_str_literal(pinfo, "index_name", index->name);
-	MEM_CHECK_DEFINED(&last_update, 4);
-	pars_info_add_int4_literal(pinfo, "last_update", uint32(last_update));
-	MEM_CHECK_DEFINED(stat_name, strlen(stat_name));
-	pars_info_add_str_literal(pinfo, "stat_name", stat_name);
-	MEM_CHECK_DEFINED(&stat_value, 8);
-	pars_info_add_ull_literal(pinfo, "stat_value", stat_value);
-	if (sample_size != NULL) {
-		MEM_CHECK_DEFINED(sample_size, 8);
-		pars_info_add_ull_literal(pinfo, "sample_size", *sample_size);
-	} else {
-		pars_info_add_literal(pinfo, "sample_size", NULL,
-				      UNIV_SQL_NULL, DATA_FIXBINARY, 0);
-	}
-	pars_info_add_str_literal(pinfo, "stat_description",
-				  stat_description);
-
-	ret = dict_stats_exec_sql(
-		pinfo,
-		"PROCEDURE INDEX_STATS_SAVE () IS\n"
-		"BEGIN\n"
-
-		"DELETE FROM \"" INDEX_STATS_NAME "\"\n"
-		"WHERE\n"
-		"database_name = :database_name AND\n"
-		"table_name = :table_name AND\n"
-		"index_name = :index_name AND\n"
-		"stat_name = :stat_name;\n"
-
-		"INSERT INTO \"" INDEX_STATS_NAME "\"\n"
-		"VALUES\n"
-		"(\n"
-		":database_name,\n"
-		":table_name,\n"
-		":index_name,\n"
-		":last_update,\n"
-		":stat_name,\n"
-		":stat_value,\n"
-		":sample_size,\n"
-		":stat_description\n"
-		");\n"
-		"END;", trx);
+        mem_heap_t *heap= mem_heap_create(128);
+	dict_index_t *stats_idx= dict_table_get_first_index(index_stats);
+	dtuple_t *tuple= dtuple_create(heap, stats_idx->n_fields);
+	dtuple_set_n_fields_cmp(tuple, stats_idx->n_uniq);
+	dict_index_copy_types(tuple, stats_idx, stats_idx->n_fields);
+	ret= construct_index_stats_tuple(stats_idx->fields, tuple, heap, db_utf8,
+	                                 table_utf8, index->name, &last_update,
+                                     stat_name, &stat_value, sample_size,
+                                     stat_description);
+        if (ret == DB_SUCCESS)
+	  ret= delete_from_stats(index_stats, tuple, trx, heap);
+	if (ret == DB_SUCCESS)
+	  ret= insert_into_stats(index_stats, tuple, trx, heap);
 
 	if (UNIV_UNLIKELY(ret != DB_SUCCESS)) {
 		if (innodb_index_stats_not_found == false
@@ -2795,7 +3042,7 @@ dict_stats_save_index_stat(
 			<< ret;
 		}
 	}
-
+	mem_heap_free(heap);
 	return(ret);
 }
 
@@ -2836,7 +3083,6 @@ static dberr_t dict_stats_report_error(dict_table_t* table)
 @return DB_SUCCESS or error code */
 dberr_t dict_stats_save(dict_table_t* table, index_id_t index_id)
 {
-	pars_info_t*	pinfo;
 	char		db_utf8[MAX_DB_UTF8_LEN];
 	char		table_utf8[MAX_TABLE_UTF8_LEN];
 
@@ -2901,7 +3147,6 @@ dberr_t dict_stats_save(dict_table_t* table, index_id_t index_id)
 
 	dict_fs2utf8(table->name.m_name, db_utf8, sizeof(db_utf8),
 		     table_utf8, sizeof(table_utf8));
-	const time_t now = time(NULL);
 	trx_t*	trx = trx_create();
 	trx->mysql_thd = thd;
 	trx_start_internal(trx);
@@ -2915,45 +3160,30 @@ dberr_t dict_stats_save(dict_table_t* table, index_id_t index_id)
 		if (trx->state != TRX_STATE_NOT_STARTED) {
 			trx->commit();
 		}
-		goto unlocked_free_and_exit;
+unlocked_free_and_exit:
+		trx->free();
+		dict_table_close(table_stats, thd, mdl_table);
+		dict_table_close(index_stats, thd, mdl_index);
+		return ret;
 	}
 
-	pinfo = pars_info_create();
-
-	pars_info_add_str_literal(pinfo, "database_name", db_utf8);
-	pars_info_add_str_literal(pinfo, "table_name", table_utf8);
-	pars_info_add_int4_literal(pinfo, "last_update", uint32(now));
-	pars_info_add_ull_literal(pinfo, "n_rows", table->stat_n_rows);
-	pars_info_add_ull_literal(pinfo, "clustered_index_size",
-		table->stat_clustered_index_size);
-	pars_info_add_ull_literal(pinfo, "sum_of_other_index_sizes",
-		table->stat_sum_of_other_index_sizes);
-
-	dict_sys.lock(SRW_LOCK_CALL);
-	trx->dict_operation_lock_mode = true;
-
-	ret = dict_stats_exec_sql(
-		pinfo,
-		"PROCEDURE TABLE_STATS_SAVE () IS\n"
-		"BEGIN\n"
-
-		"DELETE FROM \"" TABLE_STATS_NAME "\"\n"
-		"WHERE\n"
-		"database_name = :database_name AND\n"
-		"table_name = :table_name;\n"
-
-		"INSERT INTO \"" TABLE_STATS_NAME "\"\n"
-		"VALUES\n"
-		"(\n"
-		":database_name,\n"
-		":table_name,\n"
-		":last_update,\n"
-		":n_rows,\n"
-		":clustered_index_size,\n"
-		":sum_of_other_index_sizes\n"
-		");\n"
-		"END;", trx);
-
+	const time_t now= time(nullptr);
+	dict_index_t *stats_idx= dict_table_get_first_index(table_stats);
+	mem_heap_t *heap= mem_heap_create(128);
+	dtuple_t *tuple= dtuple_create(heap, stats_idx->n_fields);
+	dtuple_set_n_fields_cmp(tuple, stats_idx->n_uniq);
+	dict_index_copy_types(tuple, stats_idx, stats_idx->n_fields);
+        ret= construct_table_stats_tuple(stats_idx->fields, tuple, heap,
+                                         db_utf8, table_utf8, &now,
+                                         &table->stat_n_rows,
+					 &table->stat_clustered_index_size,
+                                         &table->stat_sum_of_other_index_sizes);
+	
+        if (ret == DB_SUCCESS)
+          ret= delete_from_stats(table_stats, tuple, trx, heap);
+	if (ret == DB_SUCCESS)
+          ret= insert_into_stats(table_stats, tuple, trx, heap);
+        mem_heap_free(heap);
 	if (UNIV_UNLIKELY(ret != DB_SUCCESS)) {
 		sql_print_error("InnoDB: Cannot save table statistics for"
 #ifdef EMBEDDED_LIBRARY
@@ -2965,14 +3195,7 @@ dberr_t dict_stats_save(dict_table_t* table, index_id_t index_id)
 				table->name.basename(), ut_strerr(ret));
 rollback_and_exit:
 		trx->rollback();
-free_and_exit:
-		trx->dict_operation_lock_mode = false;
-		dict_sys.unlock();
-unlocked_free_and_exit:
-		trx->free();
-		dict_table_close(table_stats, thd, mdl_table);
-		dict_table_close(index_stats, thd, mdl_index);
-		return ret;
+		goto unlocked_free_and_exit;
 	}
 
 	dict_index_t*	index;
@@ -3035,6 +3258,7 @@ dberr_t dict_stats_save(dict_table_t* table, index_id_t index_id)
 			}
 
 			ret = dict_stats_save_index_stat(
+				index_stats,
 				index, now, stat_name,
 				index->stat_n_diff_key_vals[i],
 				&index->stat_n_sample_sizes[i],
@@ -3045,20 +3269,24 @@ dberr_t dict_stats_save(dict_table_t* table, index_id_t index_id)
 			}
 		}
 
-		ret = dict_stats_save_index_stat(index, now, "n_leaf_pages",
+		ret = dict_stats_save_index_stat(index_stats, index, now,
+                                                 "n_leaf_pages",
 						 index->stat_n_leaf_pages,
 						 NULL,
 						 "Number of leaf pages "
 						 "in the index", trx);
+
 		if (ret != DB_SUCCESS) {
 			goto rollback_and_exit;
 		}
 
-		ret = dict_stats_save_index_stat(index, now, "size",
+		ret = dict_stats_save_index_stat(index_stats, index, now,
+                                                 "size",
 						 index->stat_index_size,
 						 NULL,
 						 "Number of pages "
 						 "in the index", trx);
+
 		if (ret != DB_SUCCESS) {
 			goto rollback_and_exit;
 		}
@@ -3070,7 +3298,7 @@ dberr_t dict_stats_save(dict_table_t* table, index_id_t index_id)
 	}
 
 	trx->commit();
-	goto free_and_exit;
+	goto unlocked_free_and_exit;
 }
 
 void dict_stats_empty_table_and_save(dict_table_t *table)
@@ -3630,26 +3858,20 @@ dict_stats_update_for_index(
 @param table_name     table name
 @param trx            transaction (nullptr=start and commit a new one)
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_delete_from_table_stats(const char *database_name,
+dberr_t dict_stats_delete_from_table_stats(dict_table_t *table_stats,
+					                       const char *database_name,
                                            const char *table_name, trx_t *trx)
 {
-	pars_info_t*	pinfo;
-
-	ut_ad(dict_sys.locked());
-
-	pinfo = pars_info_create();
-
-	pars_info_add_str_literal(pinfo, "database_name", database_name);
-	pars_info_add_str_literal(pinfo, "table_name", table_name);
-
-	return dict_stats_exec_sql(
-		pinfo,
-		"PROCEDURE DELETE_FROM_TABLE_STATS () IS\n"
-		"BEGIN\n"
-		"DELETE FROM \"" TABLE_STATS_NAME "\" WHERE\n"
-		"database_name = :database_name AND\n"
-		"table_name = :table_name;\n"
-		"END;\n", trx);
+  dict_index_t *stats_idx= dict_table_get_first_index(table_stats);
+  mem_heap_t *heap= mem_heap_create(128);
+  dtuple_t *tuple= dtuple_create(heap, 2);
+  dict_index_copy_types(tuple, stats_idx, 2);
+  dberr_t err= construct_table_stats_tuple(stats_idx->fields, tuple, heap,
+                                           database_name, table_name);
+  if (err == DB_SUCCESS)
+    err= delete_from_stats(table_stats, tuple, trx, heap);
+  mem_heap_free(heap);
+  return err;
 }
 
 /** Execute DELETE FROM mysql.innodb_index_stats
@@ -3657,26 +3879,21 @@ dberr_t dict_stats_delete_from_table_stats(const char *database_name,
 @param table_name     table name
 @param trx            transaction
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_delete_from_index_stats(const char *database_name,
-                                           const char *table_name, trx_t *trx)
+dberr_t dict_stats_delete_from_index_stats(
+  dict_table_t *index_stats,
+  const char *database_name,
+  const char *table_name, trx_t *trx)
 {
-	pars_info_t*	pinfo;
-
-	ut_ad(dict_sys.locked());
-
-	pinfo = pars_info_create();
-
-	pars_info_add_str_literal(pinfo, "database_name", database_name);
-	pars_info_add_str_literal(pinfo, "table_name", table_name);
-
-	return dict_stats_exec_sql(
-		pinfo,
-		"PROCEDURE DELETE_FROM_INDEX_STATS () IS\n"
-		"BEGIN\n"
-		"DELETE FROM \"" INDEX_STATS_NAME "\" WHERE\n"
-		"database_name = :database_name AND\n"
-		"table_name = :table_name;\n"
-		"END;\n", trx);
+	dict_index_t *stats_idx= dict_table_get_first_index(index_stats);
+	mem_heap_t *heap= mem_heap_create(128);
+	dtuple_t *tuple= dtuple_create(heap, 2);
+	dict_index_copy_types(tuple, stats_idx, 2);
+	dberr_t err= construct_index_stats_tuple(stats_idx->fields, tuple, heap,
+	                                         database_name, table_name);
+	if (err == DB_SUCCESS)
+	  err= delete_from_stats(index_stats, tuple, trx, heap);
+	mem_heap_free(heap);
+	return err;
 }
 
 /** Execute DELETE FROM mysql.innodb_index_stats
@@ -3685,29 +3902,22 @@ dberr_t dict_stats_delete_from_index_stats(const char *database_name,
 @param index_name     name of the index
 @param trx            transaction
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_delete_from_index_stats(const char *database_name,
+dberr_t dict_stats_delete_from_index_stats(dict_table_t *index_stats,
+                      					   const char *database_name,
                                            const char *table_name,
                                            const char *index_name, trx_t *trx)
 {
-	pars_info_t*	pinfo;
-
-	ut_ad(dict_sys.locked());
-
-	pinfo = pars_info_create();
-
-	pars_info_add_str_literal(pinfo, "database_name", database_name);
-	pars_info_add_str_literal(pinfo, "table_name", table_name);
-	pars_info_add_str_literal(pinfo, "index_name", index_name);
-
-	return dict_stats_exec_sql(
-		pinfo,
-		"PROCEDURE DELETE_FROM_INDEX_STATS () IS\n"
-		"BEGIN\n"
-		"DELETE FROM \"" INDEX_STATS_NAME "\" WHERE\n"
-		"database_name = :database_name AND\n"
-		"table_name = :table_name AND\n"
-		"index_name = :index_name;\n"
-		"END;\n", trx);
+  dict_index_t *stats_idx= dict_table_get_first_index(index_stats);
+  mem_heap_t *heap= mem_heap_create(128);
+  dtuple_t *tuple= dtuple_create(heap, 3);
+  dict_index_copy_types(tuple, stats_idx, 3);
+  dberr_t err= construct_index_stats_tuple(stats_idx->fields, tuple,
+                                           heap, database_name, table_name,
+   									       index_name);
+  if (err == DB_SUCCESS)
+    err= delete_from_stats(index_stats, tuple, trx, heap);
+  mem_heap_free(heap);
+  return err;
 }
 
 /** Rename a table in InnoDB persistent stats storage.
@@ -3715,7 +3925,10 @@ dberr_t dict_stats_delete_from_index_stats(const char *database_name,
 @param new_name  new table name
 @param trx       transaction
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_rename_table(const char *old_name, const char *new_name,
+dberr_t dict_stats_rename_table(dict_table_t *table_stats,
+                                dict_table_t *index_stats,
+                                const char *old_name,
+                                const char *new_name,
                                 trx_t *trx)
 {
   /* skip the statistics tables themselves */
@@ -3736,29 +3949,49 @@ dberr_t dict_stats_rename_table(const char *old_name, const char *new_name,
   if (dict_table_t::is_temporary_name(old_name) ||
       dict_table_t::is_temporary_name(new_name))
   {
-    if (dberr_t e= dict_stats_delete_from_table_stats(old_db, old_table, trx))
+    if (dberr_t e= dict_stats_delete_from_table_stats(table_stats, old_db,
+						                              old_table, trx))
       return e;
-    return dict_stats_delete_from_index_stats(old_db, old_table, trx);
+    return dict_stats_delete_from_index_stats(index_stats, old_db,
+					                          old_table, trx);
+  }
+
+  dict_index_t *stats_idx= dict_table_get_first_index(table_stats);
+  mem_heap_t *heap= mem_heap_create(128);
+  dtuple_t *old_tuple= dtuple_create(heap, 2);
+  dict_index_copy_types(old_tuple, stats_idx, 2);
+  dtuple_t *new_tuple= dtuple_create(heap, 2);
+  dict_index_copy_types(new_tuple, stats_idx, 2);
+  dberr_t err= construct_table_stats_tuple(stats_idx->fields, old_tuple, heap,
+                                           old_db, old_table);
+  if (err == DB_SUCCESS)
+  {
+    dberr_t err= construct_table_stats_tuple(stats_idx->fields, new_tuple, heap,
+                                            new_db, new_table);
+    if (err == DB_SUCCESS)
+      err= update_stats(table_stats, old_tuple, new_tuple, trx, heap);
   }
 
-  pars_info_t *pinfo= pars_info_create();
-  pars_info_add_str_literal(pinfo, "old_db", old_db);
-  pars_info_add_str_literal(pinfo, "old_table", old_table);
-  pars_info_add_str_literal(pinfo, "new_db", new_db);
-  pars_info_add_str_literal(pinfo, "new_table", new_table);
-
-  static const char sql[]=
-    "PROCEDURE RENAME_TABLE_IN_STATS() IS\n"
-    "BEGIN\n"
-    "UPDATE \"" TABLE_STATS_NAME "\" SET\n"
-    "database_name=:new_db, table_name=:new_table\n"
-    "WHERE database_name=:old_db AND table_name=:old_table;\n"
-    "UPDATE \"" INDEX_STATS_NAME "\" SET\n"
-    "database_name=:new_db, table_name=:new_table\n"
-    "WHERE database_name=:old_db AND table_name=:old_table;\n"
-    "END;\n";
-
-  return dict_stats_exec_sql(pinfo, sql, trx);
+  if (err)
+    goto func_exit;
+
+  stats_idx= dict_table_get_first_index(index_stats);
+  old_tuple= dtuple_create(heap, 2);
+  dict_index_copy_types(old_tuple, stats_idx, 2);
+  new_tuple= dtuple_create(heap, 2);
+  dict_index_copy_types(new_tuple, stats_idx, 2);
+  err= construct_index_stats_tuple(stats_idx->fields, old_tuple,
+                                   heap, old_db, old_table);
+  if (err == DB_SUCCESS)
+  {
+    err= construct_index_stats_tuple(stats_idx->fields, new_tuple, heap,
+                                     new_db, new_table);
+    if (err == DB_SUCCESS)
+      err= update_stats(index_stats, old_tuple, new_tuple, trx, heap);
+  }
+ func_exit:
+  mem_heap_free(heap);
+  return err;
 }
 
 /** Rename an index in InnoDB persistent statistics.
@@ -3768,45 +4001,57 @@ dberr_t dict_stats_rename_table(const char *old_name, const char *new_name,
 @param new_name   new table name
 @param trx        transaction
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_rename_index(const char *db, const char *table,
+dberr_t dict_stats_rename_index(dict_table_t *index_stats,
+                                const char *db, const char *table,
                                 const char *old_name, const char *new_name,
                                 trx_t *trx)
 {
   if (dict_stats_persistent_storage_check(true) != SCHEMA_OK)
     return DB_STATS_DO_NOT_EXIST;
-  pars_info_t *pinfo= pars_info_create();
 
-  pars_info_add_str_literal(pinfo, "db", db);
-  pars_info_add_str_literal(pinfo, "table", table);
-  pars_info_add_str_literal(pinfo, "old", old_name);
-  pars_info_add_str_literal(pinfo, "new", new_name);
-
-  static const char sql[]=
-    "PROCEDURE RENAME_INDEX_IN_STATS() IS\n"
-    "BEGIN\n"
-    "UPDATE \"" INDEX_STATS_NAME "\" SET index_name=:new\n"
-    "WHERE database_name=:db AND table_name=:table AND index_name=:old;\n"
-    "END;\n";
-
-  return dict_stats_exec_sql(pinfo, sql, trx);
+  dict_index_t *stats_idx= dict_table_get_first_index(index_stats);
+  mem_heap_t *heap= mem_heap_create(128);
+  dtuple_t *old_tuple= dtuple_create(heap, 3);
+  dict_index_copy_types(old_tuple, stats_idx, 3);
+  dtuple_t *new_tuple= dtuple_create(heap, 3);
+  dict_index_copy_types(new_tuple, stats_idx, 3);
+  dberr_t err= construct_index_stats_tuple(stats_idx->fields, old_tuple,
+                                           heap, db, table, old_name);
+  if (err == DB_SUCCESS)
+  {
+    err= construct_index_stats_tuple(stats_idx->fields, new_tuple, heap,
+                                     db, table, new_name);
+    if (err == DB_SUCCESS)
+      err= update_stats(index_stats, old_tuple, new_tuple, trx, heap);
+  }
+  mem_heap_free(heap);
+  return err;
 }
 
 /** Delete all persistent statistics for a database.
 @param db    database name
 @param trx   transaction
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_delete(const char *db, trx_t *trx)
+dberr_t dict_stats_delete(dict_table_t *table_stats, dict_table_t *index_stats, const char *db, trx_t *trx)
 {
-  static const char sql[] =
-    "PROCEDURE DROP_DATABASE_STATS () IS\n"
-    "BEGIN\n"
-    "DELETE FROM \"" TABLE_STATS_NAME "\" WHERE database_name=:db;\n"
-    "DELETE FROM \"" INDEX_STATS_NAME "\" WHERE database_name=:db;\n"
-    "END;\n";
-
-  pars_info_t *pinfo= pars_info_create();
-  pars_info_add_str_literal(pinfo, "db", db);
-  return dict_stats_exec_sql(pinfo, sql, trx);
+  dict_index_t *stats_idx= dict_table_get_first_index(table_stats);
+  mem_heap_t *heap= mem_heap_create(128);
+  dtuple_t *tuple= dtuple_create(heap, 1);
+  dict_index_copy_types(tuple, stats_idx, 1);
+  dberr_t err= construct_table_stats_tuple(stats_idx->fields, tuple, heap, db);
+  if (err == DB_SUCCESS)
+    err= delete_from_stats(table_stats, tuple, trx, heap);
+  if (err == DB_SUCCESS)
+  {
+    stats_idx= dict_table_get_first_index(index_stats);
+    tuple= dtuple_create(heap, 1);
+    dict_index_copy_types(tuple, stats_idx, 1);
+    err= construct_index_stats_tuple(stats_idx->fields, tuple, heap, db);
+    if (err == DB_SUCCESS)
+      err= delete_from_stats(index_stats, tuple, trx, heap);
+  }
+  mem_heap_free(heap);
+  return err;
 }
 
 /* tests @{ */
diff --git a/storage/innobase/dict/drop.cc b/storage/innobase/dict/drop.cc
index 15f6c29dfdc..3e1e30780e2 100644
--- a/storage/innobase/dict/drop.cc
+++ b/storage/innobase/dict/drop.cc
@@ -116,7 +116,9 @@ dberr_t trx_t::drop_table_foreign(const table_name_t &name)
 /** Try to drop the statistics for a persistent table.
 @param name        name of persistent table
 @return error code */
-dberr_t trx_t::drop_table_statistics(const table_name_t &name)
+dberr_t trx_t::drop_table_statistics(dict_table_t *table_stats,
+				     dict_table_t *index_stats,
+				     const table_name_t &name)
 {
   ut_ad(dict_sys.locked());
   ut_ad(dict_operation_lock_mode);
@@ -129,10 +131,11 @@ dberr_t trx_t::drop_table_statistics(const table_name_t &name)
   char db[MAX_DB_UTF8_LEN], table[MAX_TABLE_UTF8_LEN];
   dict_fs2utf8(name.m_name, db, sizeof db, table, sizeof table);
 
-  dberr_t err= dict_stats_delete_from_table_stats(db, table, this);
+  dberr_t err= dict_stats_delete_from_table_stats(table_stats,
+						  db, table, this);
   if (err == DB_SUCCESS || err == DB_STATS_DO_NOT_EXIST)
   {
-    err= dict_stats_delete_from_index_stats(db, table, this);
+    err= dict_stats_delete_from_index_stats(index_stats, db, table, this);
     if (err == DB_STATS_DO_NOT_EXIST)
       err= DB_SUCCESS;
   }
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 9103bec89fd..c5140fb4fad 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -1337,7 +1337,7 @@ static void innodb_drop_database(handlerton*, char *path)
       lock_table_for_trx(index_stats, trx, LOCK_X) == DB_SUCCESS)
   {
     row_mysql_lock_data_dictionary(trx);
-    if (dict_stats_delete(db, trx))
+    if (dict_stats_delete(table_stats, index_stats, db, trx))
     {
       /* Ignore this error. Leaving garbage statistics behind is a
       lesser evil. Carry on to try to remove any garbage tables. */
@@ -13886,7 +13886,7 @@ int ha_innobase::delete_table(const char *name)
   }
 
   if (err == DB_SUCCESS && table_stats && index_stats)
-    err= trx->drop_table_statistics(table->name);
+    err= trx->drop_table_statistics(table_stats, index_stats, table->name);
   if (err != DB_SUCCESS)
     goto err_exit;
 
@@ -14411,7 +14411,8 @@ ha_innobase::rename_table(
 	DEBUG_SYNC(thd, "after_innobase_rename_table");
 
 	if (error == DB_SUCCESS && table_stats && index_stats) {
-		error = dict_stats_rename_table(norm_from, norm_to, trx);
+		error = dict_stats_rename_table(table_stats, index_stats,
+						norm_from, norm_to, trx);
 		if (error == DB_DUPLICATE_KEY) {
 			/* The duplicate may also occur in
 			mysql.innodb_index_stats.  */
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index 1a6e6947ab2..fd1e63a1d48 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -10377,7 +10377,8 @@ commit_try_rebuild(
 	ha_innobase_inplace_ctx*ctx,
 	TABLE*			altered_table,
 	const TABLE*		old_table,
-	bool			statistics_exist,
+	dict_table_t*		table_stats,
+	dict_table_t*		index_stats,
 	trx_t*			trx,
 	const char*		table_name)
 {
@@ -10459,8 +10460,10 @@ commit_try_rebuild(
 		if (error == DB_SUCCESS) {
 			/* The statistics for the surviving indexes will be
 			re-inserted in alter_stats_rebuild(). */
-			if (statistics_exist) {
-				error = trx->drop_table_statistics(old_name);
+			if (table_stats && index_stats) {
+				error = trx->drop_table_statistics(
+						table_stats, index_stats,
+						old_name);
 			}
 			if (error == DB_SUCCESS) {
 				error = trx->drop_table(*user_table);
@@ -10663,7 +10666,8 @@ commit_try_norebuild(
 	ha_innobase_inplace_ctx*ctx,
 	TABLE*			altered_table,
 	const TABLE*		old_table,
-	bool			statistics_exist,
+	dict_table_t*		table_stats,
+	dict_table_t*		index_stats,
 	trx_t*			trx,
 	const char*		table_name)
 {
@@ -10685,6 +10689,7 @@ commit_try_norebuild(
 		DBUG_RETURN(true);
 	}
 
+	bool statistics_exist= table_stats && index_stats;
 	for (ulint i = 0; i < ctx->num_to_add_index; i++) {
 		dict_index_t*	index = ctx->add_index[i];
 		DBUG_ASSERT(dict_index_get_online_status(index)
@@ -10793,8 +10798,10 @@ commit_try_norebuild(
 			continue;
 		}
 
-		error = dict_stats_delete_from_index_stats(db, table,
-							   index->name, trx);
+		error = dict_stats_delete_from_index_stats(index_stats,
+                                                           db, table,
+							   index->name,
+							   trx);
 		switch (error) {
 		case DB_SUCCESS:
 		case DB_STATS_DO_NOT_EXIST:
@@ -10806,7 +10813,8 @@ commit_try_norebuild(
 
 	if (!statistics_exist) {
 	} else if (statistics_drop) {
-		error = dict_stats_delete_from_table_stats(db, table, trx);
+		error = dict_stats_delete_from_table_stats(table_stats,
+							   db, table, trx);
 		switch (error) {
 		case DB_SUCCESS:
 		case DB_STATS_DO_NOT_EXIST:
@@ -10814,7 +10822,8 @@ commit_try_norebuild(
 		default:
 			goto handle_error;
 		}
-		error = dict_stats_delete_from_index_stats(db, table, trx);
+		error = dict_stats_delete_from_index_stats(index_stats,
+							   db, table, trx);
 		switch (error) {
 		case DB_STATS_DO_NOT_EXIST:
 			error = DB_SUCCESS;
@@ -10833,7 +10842,7 @@ commit_try_norebuild(
 		tmp_name[0]= (char)0xff;
 		for (size_t i = 0; error == DB_SUCCESS && i < size; i++) {
 			snprintf(tmp_name+1, sizeof(tmp_name)-1, "%zu", i);
-			error = dict_stats_rename_index(db, table,
+			error = dict_stats_rename_index(index_stats, db, table,
 							ha_alter_info->
 							rename_keys[i].
 							old_key->name.str,
@@ -10841,7 +10850,8 @@ commit_try_norebuild(
 		}
 		for (size_t i = 0; error == DB_SUCCESS && i < size; i++) {
 			snprintf(tmp_name+1, sizeof(tmp_name)-1, "%zu", i);
-			error = dict_stats_rename_index(db, table, tmp_name,
+			error = dict_stats_rename_index(index_stats, db, table,
+                                                        tmp_name,
 							ha_alter_info
 							->rename_keys[i].
 							new_key->name.str,
@@ -11690,14 +11700,14 @@ ha_innobase::commit_inplace_alter_table(
 
 			if (commit_try_rebuild(ha_alter_info, ctx,
 					       altered_table, table,
-					       table_stats && index_stats,
+					       table_stats, index_stats,
 					       trx,
 					       table_share->table_name.str)) {
 				goto fail;
 			}
 		} else if (commit_try_norebuild(ha_alter_info, ctx,
 						altered_table, table,
-						table_stats && index_stats,
+						table_stats, index_stats,
 						trx,
 						table_share->table_name.str)) {
 			goto fail;
diff --git a/storage/innobase/include/dict0stats.h b/storage/innobase/include/dict0stats.h
index ba6d66ec383..b284e53a6b3 100644
--- a/storage/innobase/include/dict0stats.h
+++ b/storage/innobase/include/dict0stats.h
@@ -51,7 +51,8 @@ void dict_stats_update_if_needed_func(dict_table_t *table)
 @param table_name     table name
 @param trx            transaction
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_delete_from_table_stats(const char *database_name,
+dberr_t dict_stats_delete_from_table_stats(dict_table_t *table_stats,
+					   const char *database_name,
                                            const char *table_name,
                                            trx_t *trx)
   MY_ATTRIBUTE((nonnull));
@@ -60,7 +61,8 @@ dberr_t dict_stats_delete_from_table_stats(const char *database_name,
 @param table_name     table name
 @param trx            transaction
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_delete_from_index_stats(const char *database_name,
+dberr_t dict_stats_delete_from_index_stats(dict_table_t *index_stats,
+					   const char *database_name,
                                            const char *table_name,
                                            trx_t *trx)
   MY_ATTRIBUTE((nonnull));
@@ -70,7 +72,8 @@ dberr_t dict_stats_delete_from_index_stats(const char *database_name,
 @param index_name     name of the index
 @param trx            transaction (nullptr=start and commit a new one)
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_delete_from_index_stats(const char *database_name,
+dberr_t dict_stats_delete_from_index_stats(dict_table_t *index_stats,
+					   const char *database_name,
                                            const char *table_name,
                                            const char *index_name, trx_t *trx);
 
@@ -131,7 +134,10 @@ dberr_t dict_stats_update_persistent_try(dict_table_t *table);
 @param new_name  new table name
 @param trx       transaction
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_rename_table(const char *old_name, const char *new_name,
+dberr_t dict_stats_rename_table(dict_table_t *table_stats,
+				dict_table_t *index_stats,
+				const char *old_name,
+				const char *new_name,
                                 trx_t *trx);
 /** Rename an index in InnoDB persistent statistics.
 @param db         database name
@@ -140,36 +146,19 @@ dberr_t dict_stats_rename_table(const char *old_name, const char *new_name,
 @param new_name   new table name
 @param trx        transaction
 @return DB_SUCCESS or error code */
-dberr_t dict_stats_rename_index(const char *db, const char *table,
+dberr_t dict_stats_rename_index(dict_table_t *index_stats,
+                                const char *db, const char *table,
                                 const char *old_name, const char *new_name,
                                 trx_t *trx);
 
 /** Delete all persistent statistics for a database.
-@param db    database name
-@param trx   transaction
-@return DB_SUCCESS or error code */
-dberr_t dict_stats_delete(const char *db, trx_t *trx);
-
-/** Save an individual index's statistic into the persistent statistics
-storage.
-@param[in]	index			index to be updated
-@param[in]	last_update		timestamp of the stat
-@param[in]	stat_name		name of the stat
-@param[in]	stat_value		value of the stat
-@param[in]	sample_size		n pages sampled or NULL
-@param[in]	stat_description	description of the stat
-@param[in,out]	trx			transaction
-@return DB_SUCCESS or error code */
-dberr_t
-dict_stats_save_index_stat(
-	dict_index_t*	index,
-	time_t		last_update,
-	const char*	stat_name,
-	ib_uint64_t	stat_value,
-	ib_uint64_t*	sample_size,
-	const char*	stat_description,
-	trx_t*		trx)
-	MY_ATTRIBUTE((nonnull(1, 3, 6, 7)));
+ @param table_stats table statistics
+ @param index_stats index statistics
+ @param db database name
+ @param trx transaction
+ @return DB_SUCCESS or error code */
+dberr_t dict_stats_delete(dict_table_t *table_stats, dict_table_t *index_stats, const char *db, trx_t *trx);
+
 
 #ifdef UNIV_ENABLE_UNIT_TEST_DICT_STATS
 void test_dict_stats_all();
diff --git a/storage/innobase/include/row0upd.h b/storage/innobase/include/row0upd.h
index f60fc3595dc..8b2b534d814 100644
--- a/storage/innobase/include/row0upd.h
+++ b/storage/innobase/include/row0upd.h
@@ -525,6 +525,17 @@ struct upd_node_t{
   void vers_make_delete(trx_t *trx);
 };
 
+/***********************************************************//**
+Updates the clustered index record.
+@return DB_SUCCESS if operation successfully completed, DB_LOCK_WAIT
+in case of a lock wait, else error code */
+MY_ATTRIBUTE((nonnull, warn_unused_result))
+dberr_t
+row_upd_clust_step(
+/*===============*/
+	upd_node_t*	node,	/*!< in: row update node */
+	que_thr_t*	thr);	/*!< in: query thread */
+
 #define	UPD_NODE_MAGIC_N	1579975
 
 /* Node execution states */
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index 889437aa1d2..bb42b2ea746 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -1045,7 +1045,9 @@ struct trx_t : ilist_node<>
   /** Try to drop the statistics for a persistent table.
   @param name        name of persistent table
   @return error code */
-  dberr_t drop_table_statistics(const table_name_t &name);
+  dberr_t drop_table_statistics(dict_table_t *table_stats,
+                                dict_table_t *index_stats,
+                                const table_name_t &name);
   /** Commit the transaction, possibly after drop_table().
   @param deleted   handles of data files that were deleted */
   void commit(std::vector<pfs_os_file_t> &deleted);
