Commit cc2ddde4 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-18518 follow-up fixes

Make DDL operations that involve FULLTEXT INDEX atomic.
In particular, we must drop the internal FTS_ tables in the same
DDL transaction with ALTER TABLE.

Remove all references to fts_drop_orphaned_tables().

row_merge_drop_temp_indexes(): Drop also the internal FTS_ tables
that are associated with index stubs that were created in
prepare_inplace_alter_table_dict() for
CREATE FULLTEXT INDEX before the server was killed.

fts_clear_all(): Remove the fts_drop_tables() call. It has to be
executed before the transaction is committed!

dict_load_indexes(): Do not load any metadata for index stubs
that had been created by prepare_inplace_alter_table_dict()

fts_create_one_common_table(), fts_create_common_tables(),
fts_create_one_index_table(), fts_create_index_tables():
Remove redundant error handling. The tables will be dropped
just fine by dict_drop_index_tree().

commit_try_norebuild(): Also drop the FTS_ tables when dropping
FULLTEXT INDEX.

The changes to the test case innodb_fts.crash_recovery has been
extensively tested. The non-debug server will be killed while
the 3 ALTER TABLE are in any phase of execution. With the debug
server, DEBUG_SYNC should make the test deterministic.
parent b2e0a45d
......@@ -17,14 +17,28 @@ BEGIN;
INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...');
# Make durable the AUTO_INCREMENT in the above incomplete transaction.
connect flush_redo_log,localhost,root,,;
SET GLOBAL innodb_flush_log_at_trx_commit=1;
BEGIN;
DELETE FROM articles LIMIT 1;
ROLLBACK;
disconnect flush_redo_log;
connect ddl1, localhost, root,,;
CREATE TABLE t1(a TEXT,b TEXT,FULLTEXT INDEX(a)) ENGINE=InnoDB;
ALTER TABLE t1 ADD FULLTEXT INDEX(b);
connection default;
connect ddl2, localhost, root,,;
CREATE TABLE t2(a TEXT,b TEXT,FULLTEXT INDEX(a)) ENGINE=InnoDB;
ALTER TABLE t2 DROP INDEX a, ADD FULLTEXT INDEX(b), FORCE;
connection default;
connect ddl3, localhost, root,,;
CREATE TABLE t3(a TEXT,b TEXT,FULLTEXT INDEX(a)) ENGINE=InnoDB;
ALTER TABLE t3 DROP INDEX a, ADD FULLTEXT INDEX(b), ALGORITHM=COPY;
connection default;
# restart
# restart: with restart_parameters
disconnect ddl1;
disconnect ddl2;
disconnect ddl3;
CHECK TABLE t1,t2,t3;
Table Op Msg_type Msg_text
test.t1 check status OK
test.t2 check status OK
test.t3 check status OK
DROP TABLE t1,t2,t3;
INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...');
CREATE FULLTEXT INDEX idx ON articles (title,body);
......@@ -55,7 +69,7 @@ DELETE FROM articles LIMIT 1;
ROLLBACK;
disconnect flush_redo_log;
connection default;
# restart
# restart: with restart_parameters
disconnect dml;
INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...');
......@@ -115,7 +129,7 @@ id title body
1 MySQL Tutorial DBMS stands for Database...
2 MariaDB Tutorial DB means Database ...
connection default;
# restart
# restart: with restart_parameters
disconnect dml;
disconnect dml2;
INSERT INTO articles VALUES (8, 12, 'MySQL Tutorial','DBMS stands for DataBase ...');
......@@ -137,3 +151,6 @@ id title body
1 MySQL Tutorial DBMS stands for Database...
2 MariaDB Tutorial DB means Database ...
DROP TABLE mdev19073, mdev19073_2;
SELECT * FROM information_schema.innodb_sys_tables
WHERE name LIKE 'test/%' AND name NOT LIKE 'test/#sql-ib%';
TABLE_ID NAME FLAG N_COLS SPACE ROW_FORMAT ZIP_PAGE_SIZE SPACE_TYPE
......@@ -7,6 +7,7 @@
# The embedded server tests do not support restarting.
--source include/not_embedded.inc
--source include/maybe_debug.inc
if ($have_debug) { source include/have_debug_sync.inc; }
FLUSH TABLES;
# Following are test for crash recovery on FTS index, the first scenario
......@@ -36,22 +37,118 @@ INSERT INTO articles (title,body) VALUES
('MySQL Security','When configured properly, MySQL ...');
BEGIN;
INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...');
--echo # Make durable the AUTO_INCREMENT in the above incomplete transaction.
--connect (flush_redo_log,localhost,root,,)
SET GLOBAL innodb_flush_log_at_trx_commit=1;
BEGIN;
DELETE FROM articles LIMIT 1;
ROLLBACK;
--disconnect flush_redo_log
--connect(ddl1, localhost, root,,)
CREATE TABLE t1(a TEXT,b TEXT,FULLTEXT INDEX(a)) ENGINE=InnoDB;
if ($have_debug)
{
--disable_query_log
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL 1 WAIT_FOR ever';
--enable_query_log
}
send ALTER TABLE t1 ADD FULLTEXT INDEX(b);
--connection default
if ($have_debug)
{
--disable_query_log
SET DEBUG_SYNC='now WAIT_FOR 1';
--enable_query_log
}
--connect(ddl2, localhost, root,,)
CREATE TABLE t2(a TEXT,b TEXT,FULLTEXT INDEX(a)) ENGINE=InnoDB;
if ($have_debug)
{
--disable_query_log
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL 2 WAIT_FOR ever';
--enable_query_log
}
send ALTER TABLE t2 DROP INDEX a, ADD FULLTEXT INDEX(b), FORCE;
--connection default
if ($have_debug)
{
--disable_query_log
SET DEBUG_SYNC='now WAIT_FOR 2';
--enable_query_log
}
--connect(ddl3, localhost, root,,)
CREATE TABLE t3(a TEXT,b TEXT,FULLTEXT INDEX(a)) ENGINE=InnoDB;
if ($have_debug)
{
--disable_query_log
SET DEBUG_SYNC='alter_table_before_rename_result_table SIGNAL 3 WAIT_FOR ever';
--enable_query_log
}
send ALTER TABLE t3 DROP INDEX a, ADD FULLTEXT INDEX(b), ALGORITHM=COPY;
--connection default
if ($have_debug)
{
--disable_query_log
SET DEBUG_SYNC='now WAIT_FOR 3';
--enable_query_log
}
if (!$have_debug)
{
# Work around the lack of MDEV-24626
let $restart_parameters=--innodb-force-recovery=1;
}
if ($have_debug)
{
let $restart_parameters=--innodb-force-recovery=0;
}
let $restart_noprint=1;
let $shutdown_timeout=0;
--source include/restart_mysqld.inc
disconnect ddl1;
disconnect ddl2;
disconnect ddl3;
if (!$have_debug)
{
# Work around the lack of MDEV-25180: We may have index name mismatches
# because there is a race condition in the non-debug test.
--disable_result_log
}
CHECK TABLE t1,t2,t3;
if (!$have_debug)
{
--enable_result_log
--echo Table Op Msg_type Msg_text
--echo test.t1 check status OK
--echo test.t2 check status OK
--echo test.t3 check status OK
--disable_query_log
--echo DROP TABLE t1,t2,t3;
DROP TABLE t1,t2;
--disable_warnings
DROP TABLE IF EXISTS t3;
--enable_warnings
# The server may be killed at any time of executing ALTER TABLE on t1,t2,t3.
# Remove possible #sql- tables left behind by ALTER TABLE t2 or ALTER TABLE t3.
DELIMITER $$;
BEGIN NOT ATOMIC
DECLARE c TEXT DEFAULT
(SELECT GROUP_CONCAT(CONCAT('DROP TABLE `#mysql50#',SUBSTR(name,6),'`;'))
FROM information_schema.innodb_sys_tables
WHERE name LIKE 'test/#sql-alter-%' OR name LIKE 'test/#sql-backup-%');
IF c IS NOT NULL THEN EXECUTE IMMEDIATE c; END IF;
END;
$$
DELIMITER ;$$
--enable_query_log
}
if ($have_debug)
{
DROP TABLE t1,t2,t3;
}
# This insert will re-initialize the Doc ID counter, it should not crash
INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...');
......@@ -193,3 +290,31 @@ AGAINST ('Database' IN NATURAL LANGUAGE MODE);
SELECT * FROM mdev19073_2 WHERE MATCH (title, body)
AGAINST ('Database' IN NATURAL LANGUAGE MODE);
DROP TABLE mdev19073, mdev19073_2;
# Work around the lack of MDEV-25180 (Atomic ALTER TABLE)
let $datadir=`select @@datadir`;
if (!$have_debug)
{
--disable_query_log
call mtr.add_suppression("Cannot find index [ab] in InnoDB index dictionary\\.");
call mtr.add_suppression("InnoDB indexes are inconsistent with what defined in \\.frm for table \\./test/t[123]");
call mtr.add_suppression("InnoDB could not find key no [01] with name [ab] from dict cache for table test/t[123]");
call mtr.add_suppression("InnoDB: Table test/t[123] contains .* indexes inside InnoDB");
call mtr.add_suppression("InnoDB: Table `test`\\.`t3` does not exist");
# Work around the lack of MDEV-24626 as well.
--remove_files_wildcard $datadir/test #sql-alter-*.ibd
--remove_files_wildcard $datadir/test #sql-backup-*.ibd
call mtr.add_suppression("InnoDB: Cannot (read first page of|open datafile for read-only:) '\\./test/(FTS_|#sql-(alter|backup)-).*\\.ibd'");
call mtr.add_suppression("InnoDB: Datafile '\\./test/(FTS_|#sql-(alter|backup)-).*\\.ibd' is corrupted");
call mtr.add_suppression("InnoDB: (The error means|Operating system error)");
call mtr.add_suppression("InnoDB: If you are installing InnoDB");
call mtr.add_suppression("InnoDB: Ignoring tablespace for `test`\\.`(FTS_|#sql-(backup|alter)-).*` because it could not be opened\\.");
call mtr.add_suppression("InnoDB: Tablespace [1-9][0-9]* was not found at ./test/(FTS_|#sql-(alter|backup)-).*\\.ibd, and innodb_force_recovery was set");
call mtr.add_suppression("InnoDB: Corrupted page \\[page id: space=[1-9][0-9]*, page number=0\\] of datafile './test/(FTS_|#sql-(alter|backup)-).*\\.ibd' could not be found in the doublewrite buffer\\.");
--enable_query_log
}
--remove_files_wildcard $datadir/test #sql-*.frm
SELECT * FROM information_schema.innodb_sys_tables
WHERE name LIKE 'test/%' AND name NOT LIKE 'test/#sql-ib%';
......@@ -2079,6 +2079,9 @@ dict_load_indexes(
}
error = DB_CORRUPTION;
goto func_exit;
} else if (rec[8 + 8 + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN]
== static_cast<byte>(*TEMP_INDEX_PREFIX_STR)) {
goto next_rec;
}
ut_ad(index);
......
......@@ -797,9 +797,8 @@ fts_check_cached_index(
/** Clear all fts resources when there is no internal DOC_ID
and there are no new fts index to add.
@param[in,out] table table where fts is to be freed
@param[in] trx transaction to drop all fts tables */
void fts_clear_all(dict_table_t *table, trx_t *trx)
@param[in,out] table table where fts is to be freed */
void fts_clear_all(dict_table_t *table)
{
if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID) ||
!table->fts ||
......@@ -813,7 +812,6 @@ void fts_clear_all(dict_table_t *table, trx_t *trx)
fts_optimize_remove_table(table);
fts_drop_tables(trx, table);
fts_free(table);
DICT_TF2_FLAG_UNSET(table, DICT_TF2_FTS);
}
......@@ -1572,13 +1570,10 @@ static dberr_t fts_drop_common_tables(trx_t *trx, fts_table_t *fts_table)
}
/****************************************************************//**
Since we do a horizontal split on the index table, we need to drop
all the split tables.
Drops FTS auxiliary tables for an FTS index
@return DB_SUCCESS or error code */
static
dberr_t
fts_drop_index_split_tables(
/*========================*/
fts_drop_index_tables(
trx_t* trx, /*!< in: transaction */
dict_index_t* index) /*!< in: fts instance */
......@@ -1607,18 +1602,6 @@ fts_drop_index_split_tables(
return(error);
}
/****************************************************************//**
Drops FTS auxiliary tables for an FTS index
@return DB_SUCCESS or error code */
dberr_t
fts_drop_index_tables(
/*==================*/
trx_t* trx, /*!< in: transaction */
dict_index_t* index) /*!< in: Index to drop */
{
return(fts_drop_index_split_tables(trx, index));
}
/****************************************************************//**
Drops FTS ancillary tables needed for supporting an FTS index
on the given table. row_mysql_lock_data_dictionary must have been called
......@@ -1774,9 +1757,6 @@ fts_create_one_common_table(
new_table = NULL;
ib::warn() << "Failed to create FTS common table "
<< fts_table_name;
trx->error_state = DB_SUCCESS;
row_drop_table_for_mysql(fts_table_name, trx, SQLCOM_DROP_DB);
trx->error_state = error;
}
return(new_table);
}
......@@ -1884,14 +1864,6 @@ fts_create_common_tables(
FIL_DEFAULT_ENCRYPTION_KEY);
func_exit:
if (error != DB_SUCCESS) {
for (it = common_tables.begin(); it != common_tables.end();
++it) {
row_drop_table_for_mysql((*it)->name.m_name, trx,
SQLCOM_DROP_DB);
}
}
common_tables.clear();
mem_heap_free(heap);
......@@ -1979,9 +1951,6 @@ fts_create_one_index_table(
new_table = NULL;
ib::warn() << "Failed to create FTS index table "
<< table_name;
trx->error_state = DB_SUCCESS;
row_drop_table_for_mysql(table_name, trx, SQLCOM_DROP_DB);
trx->error_state = error;
}
return(new_table);
......@@ -2016,11 +1985,6 @@ fts_create_index_tables(trx_t* trx, const dict_index_t* index, table_id_t id)
fts_table.table_id = id;
fts_table.table = index->table;
/* aux_idx_tables vector is used for dropping FTS AUX INDEX
tables on error condition. */
std::vector<dict_table_t*> aux_idx_tables;
std::vector<dict_table_t*>::const_iterator it;
for (i = 0; i < FTS_NUM_AUX_INDEX && error == DB_SUCCESS; ++i) {
dict_table_t* new_table;
......@@ -2035,32 +1999,11 @@ fts_create_index_tables(trx_t* trx, const dict_index_t* index, table_id_t id)
if (new_table == NULL) {
error = DB_FAIL;
break;
} else {
aux_idx_tables.push_back(new_table);
}
mem_heap_empty(heap);
DBUG_EXECUTE_IF("ib_fts_index_table_error",
/* Return error after creating FTS_INDEX_5
aux table. */
if (i == 4) {
error = DB_FAIL;
break;
}
);
}
if (error != DB_SUCCESS) {
for (it = aux_idx_tables.begin(); it != aux_idx_tables.end();
++it) {
row_drop_table_for_mysql((*it)->name.m_name, trx,
SQLCOM_DROP_DB);
}
}
aux_idx_tables.clear();
mem_heap_free(heap);
return(error);
......
......@@ -9928,8 +9928,6 @@ commit_try_rebuild(
DBUG_RETURN(true);
}
dberr_t error;
/* Clear the to_be_dropped flag in the data dictionary cache
of user_table. */
for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
......@@ -9956,17 +9954,25 @@ commit_try_rebuild(
rebuilt_table->flags2 |= DICT_TF2_DISCARDED;
}
/* We can now rename the old table as a temporary table,
rename the new temporary table as the old table and drop the
old table. */
char* old_name= mem_heap_strdup(ctx->heap, user_table->name.m_name);
dberr_t error = (ctx->old_table->flags2 & DICT_TF2_FTS)
? fts_drop_tables(trx, ctx->old_table)
: DB_SUCCESS;
error = row_rename_table_for_mysql(user_table->name.m_name,
ctx->tmp_name, trx, false, false);
if (error == DB_SUCCESS) {
error = row_rename_table_for_mysql(rebuilt_table->name.m_name,
old_name, trx,
/* We can now rename the old table as a temporary table,
rename the new temporary table as the old table and drop the
old table. */
char* old_name= mem_heap_strdup(ctx->heap,
user_table->name.m_name);
error = row_rename_table_for_mysql(user_table->name.m_name,
ctx->tmp_name, trx,
false, false);
if (error == DB_SUCCESS) {
error = row_rename_table_for_mysql(
rebuilt_table->name.m_name, old_name, trx,
false, false);
}
}
/* We must be still holding a table handle. */
......@@ -10244,33 +10250,29 @@ commit_try_norebuild(
dberr_t error;
dict_index_t* index;
const char *op = "rename index to add";
ulint i;
ulint num_fts_index = 0;
/* We altered the table in place. Mark the indexes as committed. */
for (i = 0; i < ctx->num_to_add_index; i++) {
for (ulint i = 0; i < ctx->num_to_add_index; i++) {
index = ctx->add_index[i];
DBUG_ASSERT(dict_index_get_online_status(index)
== ONLINE_INDEX_COMPLETE);
DBUG_ASSERT(!index->is_committed());
error = row_merge_rename_index_to_add(
trx, ctx->new_table->id, index->id);
handle_error:
switch (error) {
case DB_SUCCESS:
break;
case DB_TOO_MANY_CONCURRENT_TRXS:
my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0));
DBUG_RETURN(true);
default:
sql_print_error("InnoDB: %s: %s\n", op,
ut_strerr(error));
DBUG_ASSERT(0);
my_error(ER_INTERNAL_ERROR, MYF(0), op);
DBUG_RETURN(true);
if (error) {
goto handle_error;
}
}
for (i = 0; i < ctx->num_to_drop_index; i++) {
for (dict_index_t *index = UT_LIST_GET_FIRST(ctx->old_table->indexes);
index; index = UT_LIST_GET_NEXT(indexes, index)) {
if (index->type & DICT_FTS) {
num_fts_index++;
}
}
for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
index = ctx->drop_index[i];
DBUG_ASSERT(index->is_committed());
DBUG_ASSERT(index->table == ctx->new_table);
......@@ -10287,11 +10289,39 @@ commit_try_norebuild(
pars_info_t* info = pars_info_create();
pars_info_add_ull_literal(info, "indexid", index->id);
error = que_eval_sql(info, drop_index, FALSE, trx);
if (error == DB_SUCCESS && index->type & DICT_FTS) {
DBUG_ASSERT(index->table->fts);
DEBUG_SYNC_C("norebuild_fts_drop");
error = fts_drop_index(index->table, index, trx);
ut_ad(num_fts_index);
num_fts_index--;
}
if (error != DB_SUCCESS) {
goto handle_error;
}
}
if ((ctx->old_table->flags2 & DICT_TF2_FTS) && !num_fts_index) {
error = fts_drop_tables(trx, ctx->old_table);
if (error != DB_SUCCESS) {
handle_error:
switch (error) {
case DB_TOO_MANY_CONCURRENT_TRXS:
my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0));
break;
default:
sql_print_error("InnoDB: %s: %s\n", op,
ut_strerr(error));
DBUG_ASSERT(0);
my_error(ER_INTERNAL_ERROR, MYF(0), op);
}
DBUG_RETURN(true);
}
}
if (innobase_rename_or_enlarge_columns_try(ha_alter_info, ctx,
altered_table, old_table,
trx, table_name)) {
......@@ -10462,51 +10492,22 @@ commit_cache_norebuild(
index->set_committed(true);
}
if (ctx->num_to_drop_index) {
for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
dict_index_t* index = ctx->drop_index[i];
DBUG_ASSERT(index->is_committed());
DBUG_ASSERT(index->table == ctx->new_table);
DBUG_ASSERT(index->to_be_dropped);
/* Replace the indexes in foreign key
constraints if needed. */
if (!dict_foreign_replace_index(
index->table, ctx->col_names, index)) {
found = false;
}
/* Mark the index dropped
in the data dictionary cache. */
index->lock.u_lock(SRW_LOCK_CALL);
index->page = FIL_NULL;
index->lock.u_unlock();
}
trx_start_for_ddl(trx);
for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
dict_index_t* index = ctx->drop_index[i];
DBUG_ASSERT(index->is_committed());
DBUG_ASSERT(index->table == ctx->new_table);
if (index->type & DICT_FTS) {
DBUG_ASSERT(index->type == DICT_FTS
|| (index->type
& DICT_CORRUPT));
DBUG_ASSERT(index->table->fts);
DEBUG_SYNC_C("norebuild_fts_drop");
fts_drop_index(index->table, index, trx);
}
for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
dict_index_t* index = ctx->drop_index[i];
DBUG_ASSERT(index->is_committed());
DBUG_ASSERT(index->table == ctx->new_table);
DBUG_ASSERT(index->to_be_dropped);
dict_index_remove_from_cache(index->table, index);
if (!dict_foreign_replace_index(index->table, ctx->col_names,
index)) {
found = false;
}
fts_clear_all(ctx->old_table, trx);
trx_commit_for_mysql(trx);
dict_index_remove_from_cache(index->table, index);
}
fts_clear_all(ctx->old_table);
if (!ctx->is_instant()) {
innobase_rename_or_enlarge_columns_cache(
ha_alter_info, altered_table, table, ctx->new_table);
......
......@@ -958,9 +958,8 @@ fts_trx_create(
/** Clear all fts resources when there is no internal DOC_ID
and there are no new fts index to add.
@param[in,out] table table where fts is to be freed
@param[in] trx transaction to drop all fts tables */
void fts_clear_all(dict_table_t *table, trx_t *trx);
@param[in,out] table table where fts is to be freed */
void fts_clear_all(dict_table_t *table);
/** Check whether the given name is fts auxiliary table
and fetch the parent table id and index id
......
......@@ -171,11 +171,9 @@ row_merge_drop_indexes(
bool locked,
const trx_t* alter_trx=NULL);
/*********************************************************************//**
Drop all partially created indexes during crash recovery. */
void
row_merge_drop_temp_indexes(void);
/*=============================*/
/** During recovery, drop recovered index stubs that were created in
prepare_inplace_alter_table_dict(). */
void row_merge_drop_temp_indexes();
/** Create temporary merge files in the given paramater path, and if
UNIV_PFS_IO defined, register the file descriptor with Performance Schema.
......
......@@ -3773,6 +3773,27 @@ row_merge_drop_indexes_dict(
trx->op_info = "";
}
/** Drop common internal tables if all fulltext indexes are dropped
@param trx transaction
@param table user table */
static void row_merge_drop_fulltext_indexes(trx_t *trx, dict_table_t *table)
{
if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID) ||
!table->fts ||
!ib_vector_is_empty(table->fts->indexes))
return;
for (const dict_index_t *index= dict_table_get_first_index(table);
index; index= dict_table_get_next_index(index))
if (index->type & DICT_FTS)
return;
fts_optimize_remove_table(table);
fts_drop_tables(trx, table);
fts_free(table);
DICT_TF2_FLAG_UNSET(table, DICT_TF2_FTS);
}
/** Drop indexes that were created before an error occurred.
The data dictionary must have been locked exclusively by the caller,
because the transaction will not be committed.
......@@ -3898,7 +3919,7 @@ row_merge_drop_indexes(
ut_error;
}
fts_clear_all(table, trx);
row_merge_drop_fulltext_indexes(trx, table);
return;
}
......@@ -3954,22 +3975,92 @@ row_merge_drop_indexes(
}
}
fts_clear_all(table, trx);
row_merge_drop_fulltext_indexes(trx, table);
table->drop_aborted = FALSE;
ut_d(dict_table_check_for_dup_indexes(table, CHECK_ALL_COMPLETE));
}
/*********************************************************************//**
Drop all partially created indexes during crash recovery. */
void
row_merge_drop_temp_indexes(void)
/*=============================*/
/** Drop fulltext indexes */
static ibool row_merge_drop_fts(void *node, void *trx)
{
auto s= static_cast<sel_node_t*>(node);
const dfield_t *table_id= que_node_get_val(s->select_list);
ut_ad(table_id->type.mtype == DATA_BINARY);
node= que_node_get_next(s->select_list);
ut_ad(!que_node_get_next(node));
const dfield_t *index_id= que_node_get_val(node);
ut_ad(index_id->type.mtype == DATA_BINARY);
static const char sql[]=
"PROCEDURE DROP_TABLES_PROC () IS\n"
"tid CHAR;\n"
"iid CHAR;\n"
"DECLARE CURSOR cur_tab IS\n"
"SELECT ID FROM SYS_TABLES\n"
"WHERE INSTR(NAME,:name)+45=LENGTH(NAME)"
" AND INSTR('123456',SUBSTR(NAME,LENGTH(NAME)-1,1))>0"
" FOR UPDATE;\n"
"DECLARE CURSOR cur_idx IS\n"
"SELECT ID FROM SYS_INDEXES\n"
"WHERE TABLE_ID = tid FOR UPDATE;\n"
"BEGIN\n"
"OPEN cur_tab;\n"
"WHILE 1 = 1 LOOP\n"
" FETCH cur_tab INTO tid;\n"
" IF (SQL % NOTFOUND) THEN EXIT; END IF;\n"
" OPEN cur_idx;\n"
" WHILE 1 = 1 LOOP\n"
" FETCH cur_idx INTO iid;\n"
" IF (SQL % NOTFOUND) THEN EXIT; END IF;\n"
" DELETE FROM SYS_FIELDS WHERE INDEX_ID=iid;\n"
" DELETE FROM SYS_INDEXES WHERE CURRENT OF cur_idx;\n"
" END LOOP;\n"
" CLOSE cur_idx;\n"
" DELETE FROM SYS_COLUMNS WHERE TABLE_ID=tid;\n"
" DELETE FROM SYS_TABLES WHERE CURRENT OF cur_tab;\n"
"END LOOP;\n"
"CLOSE cur_tab;\n"
"END;\n";
if (table_id->len == 8 && index_id->len == 8)
{
char buf[sizeof "/FTS_0000000000000000_0000000000000000_INDEX_"];
snprintf(buf, sizeof buf, "/FTS_%016llx_%016llx_INDEX_",
static_cast<ulonglong>
(mach_read_from_8(static_cast<const byte*>(table_id->data))),
static_cast<ulonglong>
(mach_read_from_8(static_cast<const byte*>(index_id->data))));
auto pinfo= pars_info_create();
pars_info_add_str_literal(pinfo, "name", buf);
que_eval_sql(pinfo, sql, false, static_cast<trx_t*>(trx));
}
return true;
}
/** During recovery, drop recovered index stubs that were created in
prepare_inplace_alter_table_dict(). */
void row_merge_drop_temp_indexes()
{
static_assert(DICT_FTS == 32, "compatibility");
static const char sql[] =
"PROCEDURE DROP_TEMP_INDEXES_PROC () IS\n"
"ixid CHAR;\n"
"found INT;\n"
"DECLARE FUNCTION drop_fts;\n"
"DECLARE CURSOR fts_cur IS\n"
" SELECT TABLE_ID,ID FROM SYS_INDEXES\n"
" WHERE TYPE=32"
" AND SUBSTR(NAME,0,1)='" TEMP_INDEX_PREFIX_STR "'\n"
" FOR UPDATE;\n"
"DECLARE CURSOR index_cur IS\n"
" SELECT ID FROM SYS_INDEXES\n"
" WHERE SUBSTR(NAME,0,1)='" TEMP_INDEX_PREFIX_STR "'\n"
......@@ -3977,6 +4068,15 @@ row_merge_drop_temp_indexes(void)
"BEGIN\n"
"found := 1;\n"
"OPEN fts_cur;\n"
"WHILE found = 1 LOOP\n"
" FETCH fts_cur INTO drop_fts();\n"
" IF (SQL % NOTFOUND) THEN\n"
" found := 0;\n"
" END IF;\n"
"END LOOP;\n"
"CLOSE fts_cur;\n"
"OPEN index_cur;\n"
"WHILE found = 1 LOOP\n"
" FETCH index_cur INTO ixid;\n"
......@@ -3989,13 +4089,11 @@ row_merge_drop_temp_indexes(void)
"END LOOP;\n"
"CLOSE index_cur;\n"
"END;\n";
trx_t* trx;
dberr_t error;
/* Load the table definitions that contain partially defined
indexes, so that the data dictionary information can be checked
when accessing the tablename.ibd files. */
trx = trx_create();
trx_t* trx = trx_create();
trx->op_info = "dropping partially created indexes";
row_mysql_lock_data_dictionary(trx);
/* Ensure that this transaction will be rolled back and locks
......@@ -4004,16 +4102,17 @@ row_merge_drop_temp_indexes(void)
trx->dict_operation = true;
trx->op_info = "dropping indexes";
error = que_eval_sql(NULL, sql, FALSE, trx);
if (error != DB_SUCCESS) {
pars_info_t* pinfo = pars_info_create();
pars_info_bind_function(pinfo, "drop_fts", row_merge_drop_fts, trx);
if (dberr_t error = que_eval_sql(pinfo, sql, FALSE, trx)) {
/* Even though we ensure that DDL transactions are WAIT
and DEADLOCK free, we could encounter other errors e.g.,
DB_TOO_MANY_CONCURRENT_TRXS. */
trx->error_state = DB_SUCCESS;
ib::error() << "row_merge_drop_temp_indexes failed with error"
<< error;
ib::error() << "row_merge_drop_temp_indexes(): " << error;
}
trx_commit_for_mysql(trx);
......
......@@ -3325,9 +3325,6 @@ row_drop_table_for_mysql(
dict_stats_recalc_pool_del(table);
dict_stats_defrag_pool_del(table, NULL);
if (btr_defragment_active) {
/* During fts_drop_orphaned_tables() the
btr_defragment_mutex has not yet been
initialized by btr_defragment_init(). */
btr_defragment_remove_table(table);
}
......
......@@ -265,8 +265,8 @@ trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr)
Before any transaction-generating background threads or the
purge have been started, we can
start transactions in row_merge_drop_temp_indexes() and
fts_drop_orphaned_tables(), and roll back recovered transactions.
start transactions in row_merge_drop_temp_indexes(),
and roll back recovered transactions.
Arbitrary user transactions may be executed when all the undo log
related background processes (including purge) are disabled due to
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment