Commit f4d1c920 authored by Aleksey Midenkov's avatar Aleksey Midenkov

MDEV-23433 Parent table discovery for children (REPAIR TABLE)

REPAIR TABLE on foreign table updates reference hints in referenced
tables if needed.

Better diagnostic error codes for CHECK TABLE.
parent 2708b725
......@@ -475,18 +475,13 @@ set foreign_key_checks= 1;
create or replace table t1 (id int primary key);
drop table t1;
create or replace table t1 (id int primary key);
check tables t1, ch1;
repair table ch1;
Table Op Msg_type Msg_text
test.t1 check status OK
test.ch1 check Error Referenced table test.t1 does not refer this table
test.ch1 check Note Found 1 foreign keys
test.ch1 check status Operation failed
repair tables t1, ch1;
Table Op Msg_type Msg_text
test.t1 repair status OK
test.ch1 repair Note Found 1 foreign keys
test.ch1 repair Note Updated 1 referenced shares
test.ch1 repair status OK
drop table t1;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (ch1_ibfk_1)
drop table ch1;
# Check drop database
create or replace table t1 (id int primary key);
......
......@@ -343,8 +343,8 @@ create or replace table t1 (id int primary key);
# Since there is no reference hints in t1 now we cannot detect ER_ROW_IS_REFERENCED_2
drop table t1;
create or replace table t1 (id int primary key);
check tables t1, ch1;
repair tables t1, ch1;
# Reference hints updated from repaired foreign tables
repair table ch1;
--error ER_ROW_IS_REFERENCED_2
drop table t1;
......
......@@ -12268,3 +12268,29 @@ ER_SEQUENCE_TABLE_ORDER_BY
eng "ORDER BY"
ER_VARIABLE_IGNORED
eng "The variable '%s' is ignored. It only exists for compatibility with old installations and will be removed in a future release"
ER_FK_MDL_REFERENCED
eng "Could not metadata-lock referenced tables"
ER_FK_NOT_EXISTS
eng "Foreign table `%s.%s` not exists"
ER_FK_REF_NOT_EXISTS
eng "Referenced table `%s.%s` not exists"
ER_FK_NOT_REFERS
eng "Foreign table `%s.%s` does not refer this table"
ER_FK_REF_NOT_REFERS
eng "Referenced table `%s.%s` does not refer this table"
ER_FK_NOT_MATCHES
eng "Foreign table `%s.%s` does not match foreign keys with referenced keys"
ER_FK_REF_NOT_MATCHES
eng "Referenced table `%s.%s` does not match referenced keys with foreign keys"
WARN_FK_FOUND
eng "Found %u foreign keys"
WARN_FK_FOUND_REFERENCES
eng "Found %u referenced keys"
WARN_FK_FOUND_SELFREFS
eng "Found %u self-references"
WARN_FK_UPDATED_SHARES
eng "Updated %u referenced shares"
ER_FK_WRONG_SELFREF
eng "Self-reference in foreign list `%s.%s` does not point to this table"
ER_FK_UNEXPECTED_SELFREFS
eng "Found %u self-references in referenced list (must be 0)"
......@@ -938,6 +938,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
compl_result_code= result_code= HA_ADMIN_FAILED;
}
}
else if (operator_func == &handler::ha_repair)
{
if (table->table->s->fk_check_consistency(thd, true))
{
compl_result_code= result_code= HA_ADMIN_FAILED;
}
}
if (result_code == HA_ADMIN_OK)
{
......
......@@ -9639,8 +9639,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (thd->mdl_context.acquire_locks(&alter_ctx->fk_mdl_reqs,
thd->variables.lock_wait_timeout))
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
"Could not lock referenced tables");
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_FK_MDL_REFERENCED,
ER_THD(thd, ER_FK_MDL_REFERENCED));
goto err;
}
/** Preacquire shares to get ER_NO_SUCH_TABLE before copy data */
......
......@@ -10778,13 +10778,17 @@ KEY * FK_info::find_idx(KEY *key_info, uint keys, bool foreign_idx)
}
bool TABLE_SHARE::fk_check_consistency(THD *thd)
bool TABLE_SHARE::fk_check_consistency(THD *thd, bool repair)
{
mbd::set<Table_name> warned;
mbd::set<Table_name> warned_self;
bool error= false;
uint rk_self_refs= 0;
uint fk_self_refs= 0;
mbd::set<FK_table_to_lock> fk_tables_to_lock;
MDL_request_list fk_mdl_reqs;
mbd::map<TABLE_SHARE *, FK_ref_backup> fk_ref_backup;
for (FK_info &rk: referenced_keys)
{
if (rk.self_ref())
......@@ -10799,10 +10803,7 @@ bool TABLE_SHARE::fk_check_consistency(THD *thd)
if (!sa.share)
{
if (!ha_table_exists(thd, &rk.foreign_db, &rk.foreign_table))
{
my_printf_error(ER_UNKNOWN_ERROR, "Foreign table %s.%s not exists",
MYF(0), rk.foreign_db.str, rk.foreign_table.str);
}
my_error(ER_FK_NOT_EXISTS, MYF(0), rk.foreign_db.str, rk.foreign_table.str);
return true;
}
List_iterator_fast<FK_info> fk_it(sa.share->foreign_keys);
......@@ -10827,13 +10828,8 @@ bool TABLE_SHARE::fk_check_consistency(THD *thd)
if (!warned.insert(Table_name(rk.foreign_db, rk.foreign_table), &warn))
return true;
if (warn)
{
my_printf_error(ER_UNKNOWN_ERROR,
found_table ?
"Foreign table %s.%s does not match foreign keys with referenced keys" :
"Foreign table %s.%s does not refer this table",
MYF(0), sa.share->db.str, sa.share->table_name.str);
}
my_error(found_table ? ER_FK_NOT_MATCHES : ER_FK_NOT_REFERS,
MYF(0), sa.share->db.str, sa.share->table_name.str);
DBUG_ASSERT(warn || error);
error= true;
}
......@@ -10845,8 +10841,8 @@ bool TABLE_SHARE::fk_check_consistency(THD *thd)
if (referenced_keys.elements)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_UNKNOWN_ERROR,
"Found %u referenced keys",
WARN_FK_FOUND_REFERENCES,
ER_THD(thd, WARN_FK_FOUND_REFERENCES),
referenced_keys.elements);
}
......@@ -10860,17 +10856,54 @@ bool TABLE_SHARE::fk_check_consistency(THD *thd)
if (!warned_self.insert(Table_name(fk.foreign_db, fk.foreign_table), &warn))
return true;
if (warn)
{
my_printf_error(ER_UNKNOWN_ERROR,
"Self-reference in foreign list %s.%s does not point to this table",
MYF(0), fk.foreign_db, fk.foreign_table);
}
my_error(ER_FK_WRONG_SELFREF, MYF(0), fk.foreign_db, fk.foreign_table);
DBUG_ASSERT(warn || error);
error= true;
}
fk_self_refs++;
continue;
}
Table_name t(fk.ref_table(thd->mem_root));
if (!fk_tables_to_lock.insert(t))
return true;
}
if (!fk_tables_to_lock.empty())
{
for (const FK_table_to_lock &t: fk_tables_to_lock)
{
MDL_request *req= new (thd->mem_root) MDL_request;
if (!req)
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
return true;
}
MDL_REQUEST_INIT(req, MDL_key::TABLE, t.table.db.str, t.table.name.str,
MDL_SHARED, MDL_STATEMENT);
fk_mdl_reqs.push_front(req);
}
}
if (thd->mdl_context.acquire_locks(&fk_mdl_reqs,
thd->variables.lock_wait_timeout))
{
// FIXME: error code
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
"Could not metadata-lock referenced tables");
return true;
}
if (repair)
{
if (thd->mdl_context.upgrade_shared_locks(&fk_mdl_reqs, MDL_EXCLUSIVE,
thd->variables.lock_wait_timeout))
return true;
}
for (FK_info &fk: foreign_keys)
{
if (fk.self_ref())
continue;
TABLE_LIST tl;
tl.init_one_table(fk.ref_db_ptr(), &fk.referenced_table, NULL, TL_IGNORE);
Share_acquire sa(thd, tl);
......@@ -10878,8 +10911,7 @@ bool TABLE_SHARE::fk_check_consistency(THD *thd)
{
if (!ha_table_exists(thd, fk.ref_db_ptr(), &fk.referenced_table))
{
my_printf_error(ER_UNKNOWN_ERROR, "Referenced table %s.%s not exists",
MYF(0), fk.ref_db().str, fk.referenced_table.str);
my_error(ER_FK_REF_NOT_EXISTS, MYF(0), fk.ref_db().str, fk.referenced_table.str);
}
return true;
}
......@@ -10901,46 +10933,113 @@ bool TABLE_SHARE::fk_check_consistency(THD *thd)
}
if (!rk)
{
bool warn;
if (!warned.insert(Table_name(fk.foreign_db, fk.foreign_table), &warn))
return true;
if (warn)
if (repair)
{
// Similar to fk_handle_alter()
TABLE_SHARE *ref_share= sa.share;
if (ref_share->partitioned())
{
my_error(ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING, MYF(0), "FOREIGN KEY");
return true;
}
FK_ref_backup *ref_bak;
FK_ref_backup fk_bak;
if (fk_bak.init(ref_share))
return true;
auto found= fk_ref_backup.find(ref_share);
if (found != fk_ref_backup.end())
ref_bak= &found->second;
else
ref_bak= fk_ref_backup.insert(ref_share, fk_bak);
if (!ref_bak)
return true;
FK_info *dst= fk.clone(&ref_share->mem_root);
if (!dst ||
ref_share->referenced_keys.push_back(dst, &ref_share->mem_root))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
return true;
}
}
else
{
my_printf_error(ER_UNKNOWN_ERROR,
found_table ?
"Referenced table %s.%s does not match referenced keys with foreign keys" :
"Referenced table %s.%s does not refer this table", MYF(0),
sa.share->db.str, sa.share->table_name.str);
bool warn;
if (!warned.insert(Table_name(fk.foreign_db, fk.foreign_table), &warn))
return true;
if (warn)
{
my_error(found_table ? ER_FK_REF_NOT_MATCHES : ER_FK_NOT_MATCHES, MYF(0),
sa.share->db.str, sa.share->table_name.str);
}
DBUG_ASSERT(warn || error);
error= true;
}
DBUG_ASSERT(warn || error);
error= true;
}
}
if (foreign_keys.elements - fk_self_refs)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_UNKNOWN_ERROR,
"Found %u foreign keys",
WARN_FK_FOUND,
ER_THD(thd, WARN_FK_FOUND),
foreign_keys.elements - fk_self_refs);
}
if (rk_self_refs)
{
my_printf_error(ER_UNKNOWN_ERROR,
"Found %u self-references in referenced list (must be 0)",
MYF(0), rk_self_refs);
my_error(ER_FK_UNEXPECTED_SELFREFS, MYF(0), rk_self_refs);
error= true;
}
if (fk_self_refs)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_UNKNOWN_ERROR,
"Found %u self-references",
WARN_FK_FOUND_SELFREFS,
ER_THD(thd, WARN_FK_FOUND_SELFREFS),
fk_self_refs);
}
/* Update EXTRA2_FOREIGN_KEY_INFO section in FRM files. */
if (repair)
{
const uint count= fk_ref_backup.size();
if (!error)
{
for (auto &key_val: fk_ref_backup)
{
FK_ref_backup *ref_bak= const_cast<FK_ref_backup *>(&key_val.second);
TABLE_SHARE *ref_share= ref_bak->share;
if (ref_share->fk_write_shadow_frm(thd))
{
error= true;
break;
}
ref_bak->install_shadow= true;
}
}
for (auto &key_val: fk_ref_backup)
{
FK_ref_backup *ref_bak= const_cast<FK_ref_backup *>(&key_val.second);
TABLE_SHARE *ref_share= ref_bak->share;
if (error)
{
if (ref_bak->install_shadow)
ref_share->fk_drop_shadow_frm();
ref_bak->rollback();
}
else
{
DBUG_ASSERT(ref_bak->install_shadow);
ref_share->fk_install_shadow_frm();
}
}
if (!error && count)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
WARN_FK_UPDATED_SHARES,
ER_THD(thd, WARN_FK_UPDATED_SHARES), count);
}
return error;
}
......
......@@ -743,7 +743,7 @@ struct TABLE_SHARE
FK_list referenced_keys;
Field *find_field_by_name(const Lex_ident_column n) const;
bool fk_handle_create(THD *thd, FK_create_vector &shares, FK_list *fk_add= NULL);
bool fk_check_consistency(THD *thd);
bool fk_check_consistency(THD *thd, bool repair= false);
bool referenced_by_foreign_key() const
{
return !referenced_keys.is_empty();
......
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