Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions mysql-test/suite/innodb/r/sys_truncate_debug.result
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,10 @@ InnoDB YES Supports transactions, row-level locking, foreign keys and encryption
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=0;
NAME FILE_SIZE
innodb_system 3145728
#
# MDEV-38412 System tablespace fails to shrink due to legacy tables
#
# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb_undo_directory=MYSQLTEST_VARDIR/tmp/log_corruption --debug_dbug=d,create_dummy_sys_tables
# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb_undo_directory=MYSQLTEST_VARDIR/tmp/log_corruption --innodb_data_file_path=ibdata1:10M:autoextend:autoshrink
FOUND 2 /InnoDB: Dropping the unknown table/ in mysqld.1.err
# restart
18 changes: 18 additions & 0 deletions mysql-test/suite/innodb/t/sys_truncate_debug.test
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,21 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=0;

--echo #
--echo # MDEV-38412 System tablespace fails to shrink due to legacy tables
--echo #
let bugdir= $MYSQLTEST_VARDIR/tmp/log_corruption;
--mkdir $bugdir
let $dirs= --innodb-data-home-dir=$bugdir --innodb-log-group-home-dir=$bugdir --innodb_undo_directory=$bugdir;
let $restart_parameters=$dirs --debug_dbug=d,create_dummy_sys_tables;
--source include/restart_mysqld.inc

let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err;
let $restart_parameters=$dirs --innodb_data_file_path=ibdata1:10M:autoextend:autoshrink;
--source include/restart_mysqld.inc
let SEARCH_PATTERN=InnoDB: Dropping the unknown table;
--source include/search_pattern_in_file.inc
let $restart_parameters=;
--source include/restart_mysqld.inc
rmdir $bugdir;
57 changes: 57 additions & 0 deletions storage/innobase/dict/dict0crea.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1311,6 +1311,53 @@ dict_create_index_step(
return(thr);
}

#ifdef UNIV_DEBUG
static dberr_t create_dummy_sys_tables(trx_t *trx) noexcept
{
dberr_t error= que_eval_sql(nullptr, "PROCEDURE CREATE_DUMMY_1() IS\n"
"BEGIN\n"
"CREATE TABLE DUMMY_1(SPACE INT, NAME CHAR);\n"
"CREATE UNIQUE CLUSTERED INDEX CLUST_IND"
" ON DUMMY_1(SPACE);\n"
"END;\n", trx);
if (error)
return error;

dict_table_t *dummy1= dict_sys.load_table("DUMMY_1");
if (dummy1)
{
dict_index_t *clust_idx= dummy1->indexes.start;
if (clust_idx && clust_idx->page != FIL_NULL)
{
mtr_t drop_mtr{trx};
drop_mtr.start();
btr_free_if_exists(fil_system.sys_space, clust_idx->page,
clust_idx->page, &drop_mtr);
drop_mtr.commit();
pars_info_t *info= pars_info_create();
pars_info_add_ull_literal(info, "index_id", clust_idx->id);
pars_info_add_int4_literal(info, "page_no", FIL_NULL);
error= que_eval_sql(info,
"PROCEDURE UPDATE_DUMMY_1_PAGE() IS\n"
"BEGIN\n"
"UPDATE SYS_INDEXES SET PAGE_NO = :page_no"
" WHERE ID = :index_id;\n"
"END;\n", trx);
if (error)
return error;
}
}

return que_eval_sql(nullptr, "PROCEDURE CREATE_DUMMY_2() IS\n"
"BEGIN\n"
"CREATE TABLE\n"
"DUMMY_2(TABLE_ID BIGINT, POS INT);\n"
"CREATE UNIQUE CLUSTERED INDEX CLUST_IND"
" ON DUMMY_2(TABLE_ID, POS);\n"
"END;\n", trx);
}
#endif /* UNIV_DEBUG */

bool dict_sys_t::load_sys_tables() noexcept
{
ut_ad(!srv_any_background_activity());
Expand Down Expand Up @@ -1459,6 +1506,16 @@ dberr_t dict_sys_t::create_or_check_sys_tables() noexcept
}
}

DBUG_EXECUTE_IF("create_dummy_sys_tables",
{
error= create_dummy_sys_tables(trx);
if (error)
{
tablename = "DUMMY";
goto err_exit;
}
});

trx->commit();
row_mysql_unlock_data_dictionary(trx);
trx->clear_and_free();
Expand Down
115 changes: 12 additions & 103 deletions storage/innobase/handler/ha_innodb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1822,6 +1822,17 @@ static int innodb_check_version(handlerton *hton, const char *path,
DBUG_RETURN(2);
}

static bool should_drop_garbage_table(const rec_t* rec, ulint len)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing noexcept, and it’d be better to use size_t instead of the alias ulint. There is no comment explaining the return value and the parameters.

{
if (len < tmp_file_prefix_length)
return false;

if (const char *f= static_cast<const char*>
(memchr(rec, '/', len - tmp_file_prefix_length)))
return !memcmp(f + 1, tmp_file_prefix, tmp_file_prefix_length);
return false;
}

/** Drop any garbage intermediate tables that existed in the system
after a backup was restored.

Expand All @@ -1833,109 +1844,7 @@ data dictionary (while the data files themselves are missing).
We will attempt to drop the tables here. */
static void drop_garbage_tables_after_restore()
{
btr_pcur_t pcur;
trx_t *trx= trx_create();
mtr_t mtr{trx};

ut_ad(!purge_sys.enabled());
ut_d(purge_sys.stop_FTS());

mtr.start();
if (pcur.open_leaf(true, dict_sys.sys_tables->indexes.start, BTR_SEARCH_LEAF,
&mtr) != DB_SUCCESS)
goto all_fail;
for (;;)
{
btr_pcur_move_to_next_user_rec(&pcur, &mtr);

if (!btr_pcur_is_on_user_rec(&pcur))
break;

const rec_t *rec= btr_pcur_get_rec(&pcur);
if (rec_get_deleted_flag(rec, 0))
continue;

static_assert(DICT_FLD__SYS_TABLES__NAME == 0, "compatibility");
size_t len;
if (rec_get_1byte_offs_flag(rec))
{
len= rec_1_get_field_end_info(rec, 0);
if (len & REC_1BYTE_SQL_NULL_MASK)
continue; /* corrupted SYS_TABLES.NAME */
}
else
{
len= rec_2_get_field_end_info(rec, 0);
static_assert(REC_2BYTE_EXTERN_MASK == 16384, "compatibility");
if (len >= REC_2BYTE_EXTERN_MASK)
continue; /* corrupted SYS_TABLES.NAME */
}

if (len < tmp_file_prefix_length)
continue;
if (const char *f= static_cast<const char*>
(memchr(rec, '/', len - tmp_file_prefix_length)))
{
if (memcmp(f + 1, tmp_file_prefix, tmp_file_prefix_length))
continue;
}
else
continue;

btr_pcur_store_position(&pcur, &mtr);
btr_pcur_commit_specify_mtr(&pcur, &mtr);

trx_start_for_ddl(trx);
std::vector<pfs_os_file_t> deleted;
dberr_t err= DB_TABLE_NOT_FOUND;
row_mysql_lock_data_dictionary(trx);

if (dict_table_t *table= dict_sys.load_table
({reinterpret_cast<const char*>(pcur.old_rec), len},
DICT_ERR_IGNORE_DROP))
{
table->acquire();
row_mysql_unlock_data_dictionary(trx);
err= lock_table_for_trx(table, trx, LOCK_X);
if (err == DB_SUCCESS &&
(table->flags2 & (DICT_TF2_FTS_HAS_DOC_ID | DICT_TF2_FTS)))
{
fts_optimize_remove_table(table);
err= fts_lock_tables(trx, *table);
}
if (err == DB_SUCCESS)
err= lock_sys_tables(trx);
row_mysql_lock_data_dictionary(trx);
table->release();

if (err == DB_SUCCESS)
err= trx->drop_table(*table);
if (err != DB_SUCCESS)
goto fail;
trx->commit(deleted);
}
else
{
fail:
trx->rollback();
sql_print_error("InnoDB: cannot drop %.*s: %s",
static_cast<int>(len), pcur.old_rec, ut_strerr(err));
}

row_mysql_unlock_data_dictionary(trx);
for (pfs_os_file_t d : deleted)
os_file_close(d);

mtr.start();
if (pcur.restore_position(BTR_SEARCH_LEAF, &mtr) == btr_pcur_t::CORRUPTED)
break;
}

all_fail:
mtr.commit();
trx->clear_and_free();
ut_free(pcur.old_rec_buf);
ut_d(purge_sys.resume_FTS());
drop_tables_by_filter(should_drop_garbage_table);
}

static int innodb_ddl_recovery_done(handlerton*)
Expand Down
7 changes: 7 additions & 0 deletions storage/innobase/include/srv0srv.h
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,10 @@ static inline void srv_start_periodic_timer(std::unique_ptr<tpool::timer>& t,

void srv_thread_pool_init();
void srv_thread_pool_end();

/** Function type for filtering tables to drop */
using MatchFilter = bool (*)(const rec_t* rec, ulint len);

/** Drop tables from SYS_TABLES matching a filter function.
@param should_drop function to determine if a table should be dropped*/
void drop_tables_by_filter(MatchFilter should_drop) noexcept;
Loading
Loading