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; ...@@ -17,14 +17,28 @@ BEGIN;
INSERT INTO articles (title,body) VALUES INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...'); ('MySQL Tutorial','DBMS stands for DataBase ...');
# Make durable the AUTO_INCREMENT in the above incomplete transaction. # Make durable the AUTO_INCREMENT in the above incomplete transaction.
connect flush_redo_log,localhost,root,,; connect ddl1, localhost, root,,;
SET GLOBAL innodb_flush_log_at_trx_commit=1; CREATE TABLE t1(a TEXT,b TEXT,FULLTEXT INDEX(a)) ENGINE=InnoDB;
BEGIN; ALTER TABLE t1 ADD FULLTEXT INDEX(b);
DELETE FROM articles LIMIT 1; connection default;
ROLLBACK; connect ddl2, localhost, root,,;
disconnect flush_redo_log; 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; 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 INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...'); ('MySQL Tutorial','DBMS stands for DataBase ...');
CREATE FULLTEXT INDEX idx ON articles (title,body); CREATE FULLTEXT INDEX idx ON articles (title,body);
...@@ -55,7 +69,7 @@ DELETE FROM articles LIMIT 1; ...@@ -55,7 +69,7 @@ DELETE FROM articles LIMIT 1;
ROLLBACK; ROLLBACK;
disconnect flush_redo_log; disconnect flush_redo_log;
connection default; connection default;
# restart # restart: with restart_parameters
disconnect dml; disconnect dml;
INSERT INTO articles (title,body) VALUES INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...'); ('MySQL Tutorial','DBMS stands for DataBase ...');
...@@ -115,7 +129,7 @@ id title body ...@@ -115,7 +129,7 @@ id title body
1 MySQL Tutorial DBMS stands for Database... 1 MySQL Tutorial DBMS stands for Database...
2 MariaDB Tutorial DB means Database ... 2 MariaDB Tutorial DB means Database ...
connection default; connection default;
# restart # restart: with restart_parameters
disconnect dml; disconnect dml;
disconnect dml2; disconnect dml2;
INSERT INTO articles VALUES (8, 12, 'MySQL Tutorial','DBMS stands for DataBase ...'); INSERT INTO articles VALUES (8, 12, 'MySQL Tutorial','DBMS stands for DataBase ...');
...@@ -137,3 +151,6 @@ id title body ...@@ -137,3 +151,6 @@ id title body
1 MySQL Tutorial DBMS stands for Database... 1 MySQL Tutorial DBMS stands for Database...
2 MariaDB Tutorial DB means Database ... 2 MariaDB Tutorial DB means Database ...
DROP TABLE mdev19073, mdev19073_2; 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 @@ ...@@ -7,6 +7,7 @@
# The embedded server tests do not support restarting. # The embedded server tests do not support restarting.
--source include/not_embedded.inc --source include/not_embedded.inc
--source include/maybe_debug.inc --source include/maybe_debug.inc
if ($have_debug) { source include/have_debug_sync.inc; }
FLUSH TABLES; FLUSH TABLES;
# Following are test for crash recovery on FTS index, the first scenario # Following are test for crash recovery on FTS index, the first scenario
...@@ -36,22 +37,118 @@ INSERT INTO articles (title,body) VALUES ...@@ -36,22 +37,118 @@ INSERT INTO articles (title,body) VALUES
('MySQL Security','When configured properly, MySQL ...'); ('MySQL Security','When configured properly, MySQL ...');
BEGIN; BEGIN;
INSERT INTO articles (title,body) VALUES INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...'); ('MySQL Tutorial','DBMS stands for DataBase ...');
--echo # Make durable the AUTO_INCREMENT in the above incomplete transaction. --echo # Make durable the AUTO_INCREMENT in the above incomplete transaction.
--connect (flush_redo_log,localhost,root,,) --connect(ddl1, localhost, root,,)
SET GLOBAL innodb_flush_log_at_trx_commit=1; CREATE TABLE t1(a TEXT,b TEXT,FULLTEXT INDEX(a)) ENGINE=InnoDB;
BEGIN; if ($have_debug)
DELETE FROM articles LIMIT 1; {
ROLLBACK; --disable_query_log
--disconnect flush_redo_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 --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; let $shutdown_timeout=0;
--source include/restart_mysqld.inc --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 # This insert will re-initialize the Doc ID counter, it should not crash
INSERT INTO articles (title,body) VALUES INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...'); ('MySQL Tutorial','DBMS stands for DataBase ...');
...@@ -193,3 +290,31 @@ AGAINST ('Database' IN NATURAL LANGUAGE MODE); ...@@ -193,3 +290,31 @@ AGAINST ('Database' IN NATURAL LANGUAGE MODE);
SELECT * FROM mdev19073_2 WHERE MATCH (title, body) SELECT * FROM mdev19073_2 WHERE MATCH (title, body)
AGAINST ('Database' IN NATURAL LANGUAGE MODE); AGAINST ('Database' IN NATURAL LANGUAGE MODE);
DROP TABLE mdev19073, mdev19073_2; 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( ...@@ -2079,6 +2079,9 @@ dict_load_indexes(
} }
error = DB_CORRUPTION; error = DB_CORRUPTION;
goto func_exit; 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); ut_ad(index);
......
...@@ -797,9 +797,8 @@ fts_check_cached_index( ...@@ -797,9 +797,8 @@ fts_check_cached_index(
/** Clear all fts resources when there is no internal DOC_ID /** Clear all fts resources when there is no internal DOC_ID
and there are no new fts index to add. and there are no new fts index to add.
@param[in,out] table table where fts is to be freed @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)
void fts_clear_all(dict_table_t *table, trx_t *trx)
{ {
if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID) || if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID) ||
!table->fts || !table->fts ||
...@@ -813,7 +812,6 @@ void fts_clear_all(dict_table_t *table, trx_t *trx) ...@@ -813,7 +812,6 @@ void fts_clear_all(dict_table_t *table, trx_t *trx)
fts_optimize_remove_table(table); fts_optimize_remove_table(table);
fts_drop_tables(trx, table);
fts_free(table); fts_free(table);
DICT_TF2_FLAG_UNSET(table, DICT_TF2_FTS); 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) ...@@ -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 Drops FTS auxiliary tables for an FTS index
all the split tables.
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
static
dberr_t dberr_t
fts_drop_index_split_tables( fts_drop_index_tables(
/*========================*/
trx_t* trx, /*!< in: transaction */ trx_t* trx, /*!< in: transaction */
dict_index_t* index) /*!< in: fts instance */ dict_index_t* index) /*!< in: fts instance */
...@@ -1607,18 +1602,6 @@ fts_drop_index_split_tables( ...@@ -1607,18 +1602,6 @@ fts_drop_index_split_tables(
return(error); 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 Drops FTS ancillary tables needed for supporting an FTS index
on the given table. row_mysql_lock_data_dictionary must have been called on the given table. row_mysql_lock_data_dictionary must have been called
...@@ -1774,9 +1757,6 @@ fts_create_one_common_table( ...@@ -1774,9 +1757,6 @@ fts_create_one_common_table(
new_table = NULL; new_table = NULL;
ib::warn() << "Failed to create FTS common table " ib::warn() << "Failed to create FTS common table "
<< fts_table_name; << 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); return(new_table);
} }
...@@ -1884,14 +1864,6 @@ fts_create_common_tables( ...@@ -1884,14 +1864,6 @@ fts_create_common_tables(
FIL_DEFAULT_ENCRYPTION_KEY); FIL_DEFAULT_ENCRYPTION_KEY);
func_exit: 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(); common_tables.clear();
mem_heap_free(heap); mem_heap_free(heap);
...@@ -1979,9 +1951,6 @@ fts_create_one_index_table( ...@@ -1979,9 +1951,6 @@ fts_create_one_index_table(
new_table = NULL; new_table = NULL;
ib::warn() << "Failed to create FTS index table " ib::warn() << "Failed to create FTS index table "
<< table_name; << 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); return(new_table);
...@@ -2016,11 +1985,6 @@ fts_create_index_tables(trx_t* trx, const dict_index_t* index, table_id_t id) ...@@ -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_id = id;
fts_table.table = index->table; 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) { for (i = 0; i < FTS_NUM_AUX_INDEX && error == DB_SUCCESS; ++i) {
dict_table_t* new_table; 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) ...@@ -2035,32 +1999,11 @@ fts_create_index_tables(trx_t* trx, const dict_index_t* index, table_id_t id)
if (new_table == NULL) { if (new_table == NULL) {
error = DB_FAIL; error = DB_FAIL;
break; break;
} else {
aux_idx_tables.push_back(new_table);
} }
mem_heap_empty(heap); 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); mem_heap_free(heap);
return(error); return(error);
......
...@@ -9928,8 +9928,6 @@ commit_try_rebuild( ...@@ -9928,8 +9928,6 @@ commit_try_rebuild(
DBUG_RETURN(true); DBUG_RETURN(true);
} }
dberr_t error;
/* Clear the to_be_dropped flag in the data dictionary cache /* Clear the to_be_dropped flag in the data dictionary cache
of user_table. */ of user_table. */
for (ulint i = 0; i < ctx->num_to_drop_index; i++) { for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
...@@ -9956,18 +9954,26 @@ commit_try_rebuild( ...@@ -9956,18 +9954,26 @@ commit_try_rebuild(
rebuilt_table->flags2 |= DICT_TF2_DISCARDED; rebuilt_table->flags2 |= DICT_TF2_DISCARDED;
} }
dberr_t error = (ctx->old_table->flags2 & DICT_TF2_FTS)
? fts_drop_tables(trx, ctx->old_table)
: DB_SUCCESS;
if (error == DB_SUCCESS) {
/* We can now rename the old table as a temporary table, /* We can now rename the old table as a temporary table,
rename the new temporary table as the old table and drop the rename the new temporary table as the old table and drop the
old table. */ old table. */
char* old_name= mem_heap_strdup(ctx->heap, user_table->name.m_name); char* old_name= mem_heap_strdup(ctx->heap,
user_table->name.m_name);
error = row_rename_table_for_mysql(user_table->name.m_name, error = row_rename_table_for_mysql(user_table->name.m_name,
ctx->tmp_name, trx, false, false); ctx->tmp_name, trx,
false, false);
if (error == DB_SUCCESS) { if (error == DB_SUCCESS) {
error = row_rename_table_for_mysql(rebuilt_table->name.m_name, error = row_rename_table_for_mysql(
old_name, trx, rebuilt_table->name.m_name, old_name, trx,
false, false); false, false);
} }
}
/* We must be still holding a table handle. */ /* We must be still holding a table handle. */
DBUG_ASSERT(user_table->get_ref_count() == 1); DBUG_ASSERT(user_table->get_ref_count() == 1);
...@@ -10244,33 +10250,29 @@ commit_try_norebuild( ...@@ -10244,33 +10250,29 @@ commit_try_norebuild(
dberr_t error; dberr_t error;
dict_index_t* index; dict_index_t* index;
const char *op = "rename index to add"; 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. */ /* 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]; index = ctx->add_index[i];
DBUG_ASSERT(dict_index_get_online_status(index) DBUG_ASSERT(dict_index_get_online_status(index)
== ONLINE_INDEX_COMPLETE); == ONLINE_INDEX_COMPLETE);
DBUG_ASSERT(!index->is_committed()); DBUG_ASSERT(!index->is_committed());
error = row_merge_rename_index_to_add( error = row_merge_rename_index_to_add(
trx, ctx->new_table->id, index->id); trx, ctx->new_table->id, index->id);
handle_error: if (error) {
switch (error) { goto handle_error;
case DB_SUCCESS: }
break; }
case DB_TOO_MANY_CONCURRENT_TRXS:
my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0)); for (dict_index_t *index = UT_LIST_GET_FIRST(ctx->old_table->indexes);
DBUG_RETURN(true); index; index = UT_LIST_GET_NEXT(indexes, index)) {
default: if (index->type & DICT_FTS) {
sql_print_error("InnoDB: %s: %s\n", op, num_fts_index++;
ut_strerr(error));
DBUG_ASSERT(0);
my_error(ER_INTERNAL_ERROR, MYF(0), op);
DBUG_RETURN(true);
} }
} }
for (i = 0; i < ctx->num_to_drop_index; i++) { for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
index = ctx->drop_index[i]; index = ctx->drop_index[i];
DBUG_ASSERT(index->is_committed()); DBUG_ASSERT(index->is_committed());
DBUG_ASSERT(index->table == ctx->new_table); DBUG_ASSERT(index->table == ctx->new_table);
...@@ -10287,11 +10289,39 @@ commit_try_norebuild( ...@@ -10287,11 +10289,39 @@ commit_try_norebuild(
pars_info_t* info = pars_info_create(); pars_info_t* info = pars_info_create();
pars_info_add_ull_literal(info, "indexid", index->id); pars_info_add_ull_literal(info, "indexid", index->id);
error = que_eval_sql(info, drop_index, FALSE, trx); 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) { if (error != DB_SUCCESS) {
goto handle_error; 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, if (innobase_rename_or_enlarge_columns_try(ha_alter_info, ctx,
altered_table, old_table, altered_table, old_table,
trx, table_name)) { trx, table_name)) {
...@@ -10462,50 +10492,21 @@ commit_cache_norebuild( ...@@ -10462,50 +10492,21 @@ commit_cache_norebuild(
index->set_committed(true); index->set_committed(true);
} }
if (ctx->num_to_drop_index) {
for (ulint i = 0; i < ctx->num_to_drop_index; i++) { for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
dict_index_t* index = ctx->drop_index[i]; dict_index_t* index = ctx->drop_index[i];
DBUG_ASSERT(index->is_committed()); DBUG_ASSERT(index->is_committed());
DBUG_ASSERT(index->table == ctx->new_table); DBUG_ASSERT(index->table == ctx->new_table);
DBUG_ASSERT(index->to_be_dropped); DBUG_ASSERT(index->to_be_dropped);
/* Replace the indexes in foreign key if (!dict_foreign_replace_index(index->table, ctx->col_names,
constraints if needed. */ index)) {
if (!dict_foreign_replace_index(
index->table, ctx->col_names, index)) {
found = false; 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);
}
dict_index_remove_from_cache(index->table, index); dict_index_remove_from_cache(index->table, index);
} }
fts_clear_all(ctx->old_table, trx); fts_clear_all(ctx->old_table);
trx_commit_for_mysql(trx);
}
if (!ctx->is_instant()) { if (!ctx->is_instant()) {
innobase_rename_or_enlarge_columns_cache( innobase_rename_or_enlarge_columns_cache(
......
...@@ -958,9 +958,8 @@ fts_trx_create( ...@@ -958,9 +958,8 @@ fts_trx_create(
/** Clear all fts resources when there is no internal DOC_ID /** Clear all fts resources when there is no internal DOC_ID
and there are no new fts index to add. and there are no new fts index to add.
@param[in,out] table table where fts is to be freed @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);
void fts_clear_all(dict_table_t *table, trx_t *trx);
/** Check whether the given name is fts auxiliary table /** Check whether the given name is fts auxiliary table
and fetch the parent table id and index id and fetch the parent table id and index id
......
...@@ -171,11 +171,9 @@ row_merge_drop_indexes( ...@@ -171,11 +171,9 @@ row_merge_drop_indexes(
bool locked, bool locked,
const trx_t* alter_trx=NULL); const trx_t* alter_trx=NULL);
/*********************************************************************//** /** During recovery, drop recovered index stubs that were created in
Drop all partially created indexes during crash recovery. */ prepare_inplace_alter_table_dict(). */
void void row_merge_drop_temp_indexes();
row_merge_drop_temp_indexes(void);
/*=============================*/
/** Create temporary merge files in the given paramater path, and if /** Create temporary merge files in the given paramater path, and if
UNIV_PFS_IO defined, register the file descriptor with Performance Schema. UNIV_PFS_IO defined, register the file descriptor with Performance Schema.
......
...@@ -3773,6 +3773,27 @@ row_merge_drop_indexes_dict( ...@@ -3773,6 +3773,27 @@ row_merge_drop_indexes_dict(
trx->op_info = ""; 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. /** Drop indexes that were created before an error occurred.
The data dictionary must have been locked exclusively by the caller, The data dictionary must have been locked exclusively by the caller,
because the transaction will not be committed. because the transaction will not be committed.
...@@ -3898,7 +3919,7 @@ row_merge_drop_indexes( ...@@ -3898,7 +3919,7 @@ row_merge_drop_indexes(
ut_error; ut_error;
} }
fts_clear_all(table, trx); row_merge_drop_fulltext_indexes(trx, table);
return; return;
} }
...@@ -3954,22 +3975,92 @@ row_merge_drop_indexes( ...@@ -3954,22 +3975,92 @@ row_merge_drop_indexes(
} }
} }
fts_clear_all(table, trx); row_merge_drop_fulltext_indexes(trx, table);
table->drop_aborted = FALSE; table->drop_aborted = FALSE;
ut_d(dict_table_check_for_dup_indexes(table, CHECK_ALL_COMPLETE)); ut_d(dict_table_check_for_dup_indexes(table, CHECK_ALL_COMPLETE));
} }
/*********************************************************************//** /** Drop fulltext indexes */
Drop all partially created indexes during crash recovery. */ static ibool row_merge_drop_fts(void *node, void *trx)
void
row_merge_drop_temp_indexes(void)
/*=============================*/
{ {
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[] = static const char sql[] =
"PROCEDURE DROP_TEMP_INDEXES_PROC () IS\n" "PROCEDURE DROP_TEMP_INDEXES_PROC () IS\n"
"ixid CHAR;\n" "ixid CHAR;\n"
"found INT;\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" "DECLARE CURSOR index_cur IS\n"
" SELECT ID FROM SYS_INDEXES\n" " SELECT ID FROM SYS_INDEXES\n"
" WHERE SUBSTR(NAME,0,1)='" TEMP_INDEX_PREFIX_STR "'\n" " WHERE SUBSTR(NAME,0,1)='" TEMP_INDEX_PREFIX_STR "'\n"
...@@ -3977,6 +4068,15 @@ row_merge_drop_temp_indexes(void) ...@@ -3977,6 +4068,15 @@ row_merge_drop_temp_indexes(void)
"BEGIN\n" "BEGIN\n"
"found := 1;\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" "OPEN index_cur;\n"
"WHILE found = 1 LOOP\n" "WHILE found = 1 LOOP\n"
" FETCH index_cur INTO ixid;\n" " FETCH index_cur INTO ixid;\n"
...@@ -3989,13 +4089,11 @@ row_merge_drop_temp_indexes(void) ...@@ -3989,13 +4089,11 @@ row_merge_drop_temp_indexes(void)
"END LOOP;\n" "END LOOP;\n"
"CLOSE index_cur;\n" "CLOSE index_cur;\n"
"END;\n"; "END;\n";
trx_t* trx;
dberr_t error;
/* Load the table definitions that contain partially defined /* Load the table definitions that contain partially defined
indexes, so that the data dictionary information can be checked indexes, so that the data dictionary information can be checked
when accessing the tablename.ibd files. */ when accessing the tablename.ibd files. */
trx = trx_create(); trx_t* trx = trx_create();
trx->op_info = "dropping partially created indexes"; trx->op_info = "dropping partially created indexes";
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
/* Ensure that this transaction will be rolled back and locks /* Ensure that this transaction will be rolled back and locks
...@@ -4004,16 +4102,17 @@ row_merge_drop_temp_indexes(void) ...@@ -4004,16 +4102,17 @@ row_merge_drop_temp_indexes(void)
trx->dict_operation = true; trx->dict_operation = true;
trx->op_info = "dropping indexes"; 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 /* Even though we ensure that DDL transactions are WAIT
and DEADLOCK free, we could encounter other errors e.g., and DEADLOCK free, we could encounter other errors e.g.,
DB_TOO_MANY_CONCURRENT_TRXS. */ DB_TOO_MANY_CONCURRENT_TRXS. */
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;
ib::error() << "row_merge_drop_temp_indexes failed with error" ib::error() << "row_merge_drop_temp_indexes(): " << error;
<< error;
} }
trx_commit_for_mysql(trx); trx_commit_for_mysql(trx);
......
...@@ -3325,9 +3325,6 @@ row_drop_table_for_mysql( ...@@ -3325,9 +3325,6 @@ row_drop_table_for_mysql(
dict_stats_recalc_pool_del(table); dict_stats_recalc_pool_del(table);
dict_stats_defrag_pool_del(table, NULL); dict_stats_defrag_pool_del(table, NULL);
if (btr_defragment_active) { 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); 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) ...@@ -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 Before any transaction-generating background threads or the
purge have been started, we can purge have been started, we can
start transactions in row_merge_drop_temp_indexes() and start transactions in row_merge_drop_temp_indexes(),
fts_drop_orphaned_tables(), and roll back recovered transactions. and roll back recovered transactions.
Arbitrary user transactions may be executed when all the undo log Arbitrary user transactions may be executed when all the undo log
related background processes (including purge) are disabled due to 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