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

MDEV-17859: Clean up the FOREIGN KEY handling

dict_create_add_foreigns_to_dictionary(): Do not commit the transaction.
The operation can still fail in dict_load_foreigns(), and we want
to be able to roll back the transaction.

create_table_info_t::create_table(): Never reset m_drop_before_rollback,
and never commit the transaction. We use a single point of rollback
in ha_innobase::create(). Merge the logic from
row_table_add_foreign_constraints().
parent b26e603a
......@@ -2268,15 +2268,6 @@ dict_create_add_foreigns_to_dictionary(
}
}
trx->op_info = "committing foreign key definitions";
if (trx_is_started(trx)) {
trx_commit(trx);
}
trx->op_info = "";
return(DB_SUCCESS);
}
......
......@@ -12514,8 +12514,6 @@ int create_table_info_t::create_table(bool create_fk)
int primary_key_no;
uint i;
dict_table_t* innobase_table = NULL;
const char* stmt;
size_t stmt_len;
DBUG_ENTER("create_table");
......@@ -12596,9 +12594,7 @@ int create_table_info_t::create_table(bool create_fk)
dict_table_close(innobase_table, TRUE, FALSE);
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
FTS_DOC_ID_INDEX_NAME);
m_drop_before_rollback = false;
error = -1;
DBUG_RETURN(error);
DBUG_RETURN(-1);
case FTS_EXIST_DOC_ID_INDEX:
case FTS_NOT_EXIST_DOC_ID_INDEX:
break;
......@@ -12638,15 +12634,28 @@ int create_table_info_t::create_table(bool create_fk)
dict_table_get_all_fts_indexes(innobase_table, fts->indexes);
}
stmt = innobase_get_stmt_unsafe(m_thd, &stmt_len);
if (stmt) {
dberr_t err = row_table_add_foreign_constraints(
create_fk ? m_trx : NULL, stmt, stmt_len, m_table_name,
m_create_info->options & HA_LEX_CREATE_TMP_TABLE);
size_t stmt_len;
if (const char* stmt = innobase_get_stmt_unsafe(m_thd, &stmt_len)) {
dberr_t err = create_fk
? dict_create_foreign_constraints(
m_trx, stmt, stmt_len, m_table_name,
m_flags2 & DICT_TF2_TEMPORARY)
: DB_SUCCESS;
if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */
dict_names_t fk_tables;
err = dict_load_foreigns(m_table_name, NULL,
false, true,
DICT_ERR_IGNORE_NONE,
fk_tables);
while (err == DB_SUCCESS && !fk_tables.empty()) {
dict_load_table(fk_tables.front(), true,
DICT_ERR_IGNORE_NONE);
fk_tables.pop_front();
}
}
switch (err) {
case DB_PARENT_NO_INDEX:
push_warning_printf(
m_thd, Sql_condition::WARN_LEVEL_WARN,
......@@ -12679,13 +12688,9 @@ int create_table_info_t::create_table(bool create_fk)
break;
}
error = convert_error_code_to_mysql(err, m_flags, NULL);
if (error) {
/* row_table_add_foreign_constraints() dropped
the table */
m_drop_before_rollback = false;
DBUG_RETURN(error);
if (err != DB_SUCCESS) {
DBUG_RETURN(convert_error_code_to_mysql(
err, m_flags, NULL));
}
}
......
......@@ -376,35 +376,6 @@ row_create_index_for_mysql(
large. */
MY_ATTRIBUTE((warn_unused_result));
/*********************************************************************//**
Scans a table create SQL string and adds to the data dictionary
the foreign key constraints declared in the string. This function
should be called after the indexes for a table have been created.
Each foreign key constraint must be accompanied with indexes in
bot participating tables. The indexes are allowed to contain more
fields than mentioned in the constraint.
@param[in] trx transaction (NULL if not adding to dictionary)
@param[in] sql_string table create statement where
foreign keys are declared like:
FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the database
name before it: test.table2; the default
database id the database of parameter name
@param[in] sql_length length of sql_string
@param[in] name table full name in normalized form
@param[in] reject_fks whether to fail with DB_CANNOT_ADD_CONSTRAINT
if any foreign keys are found
@return error code or DB_SUCCESS */
dberr_t
row_table_add_foreign_constraints(
trx_t* trx,
const char* sql_string,
size_t sql_length,
const char* name,
bool reject_fks)
MY_ATTRIBUTE((warn_unused_result));
/*********************************************************************//**
The master thread in srv0srv.cc calls this regularly to drop tables which
we must drop in background after queries to them have ended. Such lazy
dropping of tables is needed in ALTER TABLE on Unix.
......
......@@ -2497,96 +2497,6 @@ row_create_index_for_mysql(
return(err);
}
/*********************************************************************//**
Scans a table create SQL string and adds to the data dictionary
the foreign key constraints declared in the string. This function
should be called after the indexes for a table have been created.
Each foreign key constraint must be accompanied with indexes in
bot participating tables. The indexes are allowed to contain more
fields than mentioned in the constraint.
@param[in] trx transaction (NULL if not adding to dictionary)
@param[in] sql_string table create statement where
foreign keys are declared like:
FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the database
name before it: test.table2; the default
database id the database of parameter name
@param[in] sql_length length of sql_string
@param[in] name table full name in normalized form
@param[in] reject_fks whether to fail with DB_CANNOT_ADD_CONSTRAINT
if any foreign keys are found
@return error code or DB_SUCCESS */
dberr_t
row_table_add_foreign_constraints(
trx_t* trx,
const char* sql_string,
size_t sql_length,
const char* name,
bool reject_fks)
{
dberr_t err;
DBUG_ENTER("row_table_add_foreign_constraints");
ut_ad(mutex_own(&dict_sys->mutex));
ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
ut_a(sql_string);
if (trx) {
trx->op_info = "adding foreign keys";
trx_start_if_not_started_xa(trx, true);
trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
err = dict_create_foreign_constraints(
trx, sql_string, sql_length, name, reject_fks);
DBUG_EXECUTE_IF("ib_table_add_foreign_fail",
err = DB_DUPLICATE_KEY;);
DEBUG_SYNC_C("table_add_foreign_constraints");
} else {
err = DB_SUCCESS;
}
if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */
dict_names_t fk_tables;
err = dict_load_foreigns(name, NULL, false, true,
DICT_ERR_IGNORE_NONE, fk_tables);
while (err == DB_SUCCESS && !fk_tables.empty()) {
dict_load_table(fk_tables.front(), true,
DICT_ERR_IGNORE_NONE);
fk_tables.pop_front();
}
}
if (err != DB_SUCCESS && trx) {
/* We have special error handling here */
trx->error_state = DB_SUCCESS;
if (trx_is_started(trx)) {
trx_rollback_to_savepoint(trx, NULL);
}
row_drop_table_for_mysql(name, trx, SQLCOM_DROP_TABLE, true);
if (trx_is_started(trx)) {
trx_commit_for_mysql(trx);
}
trx->error_state = DB_SUCCESS;
}
DBUG_RETURN(err);
}
/*********************************************************************//**
Drops a table for MySQL as a background operation. MySQL relies on Unix
in ALTER TABLE to the fact that the table handler does not remove the
......
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