mirror of
https://github.com/MariaDB/server.git
synced 2026-05-18 21:07:24 +02:00
MDEV-34309 [1/2] prelock referentially related tables for CHECK TABLE
This commit is contained in:
parent
5d7f6f7e6e
commit
1115f790dd
5 changed files with 164 additions and 61 deletions
|
|
@ -3682,6 +3682,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
|
|||
belong_to_view,
|
||||
stab->trg_event_map,
|
||||
query_tables_last_ptr,
|
||||
TABLE_LIST::OPEN_NORMAL,
|
||||
stab->for_insert_data);
|
||||
tab_buff+= ALIGN_SIZE(sizeof(TABLE_LIST));
|
||||
result= TRUE;
|
||||
|
|
|
|||
|
|
@ -342,9 +342,11 @@ static inline bool table_not_corrupt_error(uint sql_errno)
|
|||
static int debug_fail_counter= 0;
|
||||
#endif
|
||||
|
||||
static bool open_only_one_table(THD* thd, TABLE_LIST* table,
|
||||
bool repair_table_use_frm,
|
||||
bool is_view_operator_func)
|
||||
static
|
||||
bool open_only_one_table(THD* thd, TABLE_LIST* table,
|
||||
bool repair_table_use_frm,
|
||||
bool is_view_operator_func,
|
||||
Prelocking_strategy *prelock_strategy)
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
SELECT_LEX *select= lex->first_select_lex();
|
||||
|
|
@ -433,7 +435,7 @@ static bool open_only_one_table(THD* thd, TABLE_LIST* table,
|
|||
*/
|
||||
|
||||
open_error= (thd->open_temporary_tables(table) ||
|
||||
open_and_lock_tables(thd, table, TRUE, 0));
|
||||
open_and_lock_tables(thd, table, TRUE, 0, prelock_strategy));
|
||||
}
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
|
|
@ -647,9 +649,11 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
/* open only one table from local list of command */
|
||||
while (1)
|
||||
{
|
||||
Check_table_prelocking_strategy prelocking_strategy;
|
||||
open_error= open_only_one_table(thd, table,
|
||||
no_errors_from_open,
|
||||
(view_operator_func != NULL));
|
||||
(view_operator_func != NULL),
|
||||
&prelocking_strategy);
|
||||
thd->open_options&= ~extra_open_options;
|
||||
|
||||
/*
|
||||
|
|
@ -974,8 +978,10 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
|
||||
table->lock_type= TL_READ;
|
||||
DBUG_ASSERT(view_operator_func == NULL);
|
||||
DML_prelocking_strategy prelocking_strategy;
|
||||
open_error= open_only_one_table(thd, table,
|
||||
no_errors_from_open, FALSE);
|
||||
no_errors_from_open, FALSE,
|
||||
&prelocking_strategy);
|
||||
thd->open_options&= ~extra_open_options;
|
||||
|
||||
if (unlikely(!open_error))
|
||||
|
|
|
|||
128
sql/sql_base.cc
128
sql/sql_base.cc
|
|
@ -3910,9 +3910,8 @@ bool extend_table_list(THD *thd, TABLE_LIST *tables,
|
|||
{
|
||||
bool error= false;
|
||||
LEX *lex= thd->lex;
|
||||
bool maybe_need_prelocking=
|
||||
(tables->updating && tables->lock_type >= TL_FIRST_WRITE)
|
||||
|| thd->lex->default_used;
|
||||
bool maybe_need_prelocking= prelocking_strategy->may_need_prelocking(thd,
|
||||
tables);
|
||||
|
||||
if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
|
||||
! has_prelocking_list && maybe_need_prelocking)
|
||||
|
|
@ -4946,8 +4945,8 @@ bool DML_prelocking_strategy::handle_routine(THD *thd,
|
|||
@note this can be changed to use a hash, instead of scanning the linked
|
||||
list, if the performance of this function will ever become an issue
|
||||
*/
|
||||
bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db,
|
||||
LEX_CSTRING *table, thr_lock_type lock_type)
|
||||
bool table_already_fk_prelocked(TABLE_LIST *tl, const LEX_CSTRING *db,
|
||||
const LEX_CSTRING *table, thr_lock_type lock_type)
|
||||
{
|
||||
for (; tl; tl= tl->next_global )
|
||||
{
|
||||
|
|
@ -5009,6 +5008,7 @@ add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx,
|
|||
TABLE_LIST::PRELOCK_NONE,
|
||||
0, 0,
|
||||
&prelocking_ctx->query_tables_last,
|
||||
TABLE_LIST::OPEN_NORMAL,
|
||||
tables->for_insert_data);
|
||||
/*
|
||||
Store link to the new table_list that will be used by open so that
|
||||
|
|
@ -5020,6 +5020,12 @@ add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx,
|
|||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
static bool
|
||||
prepare_fk_prelocking_list(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
const TABLE_LIST *table_list,
|
||||
const List <FOREIGN_KEY_INFO> &fk_list,
|
||||
const Prelocking_strategy *prelocking_strategy,
|
||||
uint8 row_event_map, bool parent);
|
||||
/**
|
||||
Extend the table_list to include foreign tables for prelocking.
|
||||
|
||||
|
|
@ -5034,55 +5040,77 @@ add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx,
|
|||
@retval TRUE Failure (OOM).
|
||||
*/
|
||||
inline bool
|
||||
prepare_fk_prelocking_list(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
TABLE_LIST *table_list, bool *need_prelocking,
|
||||
uint8 op)
|
||||
prepare_fk_parent_prelocking_list(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
TABLE_LIST *table_list,
|
||||
const
|
||||
Prelocking_strategy *prelocking_strategy,
|
||||
bool *need_prelocking,
|
||||
uint8 op)
|
||||
{
|
||||
DBUG_ENTER("prepare_fk_prelocking_list");
|
||||
DBUG_ENTER("prepare_fk_parent_prelocking_list");
|
||||
List <FOREIGN_KEY_INFO> fk_list;
|
||||
List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list);
|
||||
FOREIGN_KEY_INFO *fk;
|
||||
Query_arena *arena, backup;
|
||||
TABLE *table= table_list->table;
|
||||
|
||||
if (!table->file->referenced_by_foreign_key())
|
||||
DBUG_RETURN(FALSE);
|
||||
|
||||
arena= thd->activate_stmt_arena_if_needed(&backup);
|
||||
Query_arena_stmt arena_ctx(thd);
|
||||
|
||||
table->file->get_parent_foreign_key_list(thd, &fk_list);
|
||||
if (unlikely(thd->is_error()))
|
||||
{
|
||||
if (arena)
|
||||
thd->restore_active_arena(arena, &backup);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*need_prelocking= TRUE;
|
||||
*need_prelocking= true;
|
||||
DBUG_RETURN(prepare_fk_prelocking_list(thd, prelocking_ctx, table_list, fk_list,
|
||||
prelocking_strategy, op, true));
|
||||
}
|
||||
|
||||
while ((fk= fk_list_it++))
|
||||
inline bool fk_has_cascade_event(const FOREIGN_KEY_INFO &fk,
|
||||
uint8 row_event_map)
|
||||
{
|
||||
bool can_delete= row_event_map & trg2bit(TRG_EVENT_DELETE);
|
||||
bool can_update= row_event_map & trg2bit(TRG_EVENT_UPDATE);
|
||||
return (can_delete && fk_modifies_child(fk.delete_method))
|
||||
|| (can_update && fk_modifies_child(fk.update_method));
|
||||
}
|
||||
|
||||
inline bool
|
||||
prepare_fk_prelocking_list(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
const TABLE_LIST *table_list,
|
||||
const List <FOREIGN_KEY_INFO> &fk_list,
|
||||
const Prelocking_strategy *prelocking_strategy,
|
||||
uint8 row_event_map,
|
||||
bool parent)
|
||||
{
|
||||
if (!parent)
|
||||
row_event_map= 0; // Child tables are never updated.
|
||||
|
||||
for (const FOREIGN_KEY_INFO &fk: fk_list)
|
||||
{
|
||||
// FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access
|
||||
thr_lock_type lock_type;
|
||||
const LEX_CSTRING &rel_table_name= parent ? fk.foreign_table
|
||||
: fk.referenced_table;
|
||||
const LEX_CSTRING &rel_table_db= parent ? fk.foreign_db : fk.referenced_db;
|
||||
|
||||
if ((op & trg2bit(TRG_EVENT_DELETE) && fk_modifies_child(fk->delete_method))
|
||||
|| (op & trg2bit(TRG_EVENT_UPDATE) && fk_modifies_child(fk->update_method)))
|
||||
lock_type= TL_FIRST_WRITE;
|
||||
else
|
||||
lock_type= TL_READ;
|
||||
// FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access
|
||||
thr_lock_type lock_type= fk_has_cascade_event(fk, row_event_map)
|
||||
? TL_FIRST_WRITE : TL_READ;
|
||||
|
||||
if (table_already_fk_prelocked(prelocking_ctx->query_tables,
|
||||
&fk->foreign_db, &fk->foreign_table, lock_type))
|
||||
&rel_table_db, &rel_table_name,
|
||||
lock_type))
|
||||
continue;
|
||||
|
||||
TABLE_LIST *tl= new (thd) TABLE_LIST();
|
||||
tl->init_one_table_for_prelocking(&fk->foreign_db, &fk->foreign_table,
|
||||
NULL, lock_type, TABLE_LIST::PRELOCK_FK, table_list->belong_to_view,
|
||||
op, &prelocking_ctx->query_tables_last, table_list->for_insert_data);
|
||||
tl->init_one_table_for_prelocking(&rel_table_db,
|
||||
&rel_table_name,
|
||||
NULL, lock_type,
|
||||
TABLE_LIST::PRELOCK_FK,
|
||||
table_list->belong_to_view, row_event_map,
|
||||
&prelocking_ctx->query_tables_last,
|
||||
prelocking_strategy->get_fk_open_strategy(lock_type),
|
||||
table_list->for_insert_data);
|
||||
}
|
||||
if (arena)
|
||||
thd->restore_active_arena(arena, &backup);
|
||||
DBUG_RETURN(FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -5128,16 +5156,16 @@ bool DML_prelocking_strategy::handle_table(THD *thd,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
if (prepare_fk_prelocking_list(thd, prelocking_ctx, table_list,
|
||||
need_prelocking,
|
||||
table_list->trg_event_map))
|
||||
if (prepare_fk_parent_prelocking_list(thd, prelocking_ctx, table_list,
|
||||
this, need_prelocking,
|
||||
table_list->trg_event_map))
|
||||
return TRUE;
|
||||
}
|
||||
else if (table_list->slave_fk_event_map)
|
||||
{
|
||||
if (prepare_fk_prelocking_list(thd, prelocking_ctx, table_list,
|
||||
need_prelocking,
|
||||
table_list->slave_fk_event_map))
|
||||
if (prepare_fk_parent_prelocking_list(thd, prelocking_ctx, table_list,
|
||||
this, need_prelocking,
|
||||
table_list->slave_fk_event_map))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -5166,6 +5194,28 @@ bool DML_prelocking_strategy::handle_table(THD *thd,
|
|||
}
|
||||
|
||||
|
||||
bool
|
||||
Check_table_prelocking_strategy::handle_table(THD *thd,
|
||||
Query_tables_list *prelocking_ctx,
|
||||
TABLE_LIST *tables,
|
||||
bool *need_prelocking)
|
||||
{
|
||||
Query_arena_stmt stmt_ctx(thd);
|
||||
List <FOREIGN_KEY_INFO> fk_child_list, fk_parent_list;
|
||||
tables->table->file->get_foreign_key_list(thd, &fk_child_list);
|
||||
tables->table->file->get_parent_foreign_key_list(thd, &fk_parent_list);
|
||||
if (unlikely(thd->is_error()))
|
||||
return TRUE;
|
||||
|
||||
for (const auto *fk_list: {&fk_child_list, &fk_parent_list})
|
||||
{
|
||||
if (prepare_fk_prelocking_list(thd, prelocking_ctx, tables, *fk_list, this,
|
||||
0, fk_list == &fk_parent_list))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
Open all tables used by DEFAULT functions.
|
||||
|
||||
|
|
|
|||
|
|
@ -397,6 +397,13 @@ inline bool setup_fields_with_no_wrap(THD *thd, Ref_ptr_array ref_pointer_array,
|
|||
return res;
|
||||
}
|
||||
|
||||
inline static
|
||||
bool check_table_referential_checks_needed(THD *thd)
|
||||
{
|
||||
return thd->lex->check_opt.flags & T_EXTEND
|
||||
&& !(thd->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS);
|
||||
}
|
||||
|
||||
/**
|
||||
An abstract class for a strategy specifying how the prelocking
|
||||
algorithm should extend the prelocking set while processing
|
||||
|
|
@ -417,6 +424,19 @@ public:
|
|||
virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
TABLE_LIST *table_list, bool *need_prelocking)= 0;
|
||||
virtual bool handle_end(THD *thd) { return 0; };
|
||||
|
||||
virtual bool may_need_prelocking(THD *thd, TABLE_LIST *table) const
|
||||
{
|
||||
return (table->updating && table->lock_type >= TL_FIRST_WRITE)
|
||||
|| thd->lex->default_used;
|
||||
}
|
||||
|
||||
virtual TABLE_LIST::enum_open_strategy
|
||||
get_fk_open_strategy(thr_lock_type lock_type) const
|
||||
{
|
||||
DBUG_ASSERT(!current_thd->lex->query_tables->updating); // should be DML
|
||||
return TABLE_LIST::OPEN_STUB;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -438,6 +458,13 @@ public:
|
|||
TABLE_LIST *table_list, bool *need_prelocking) override;
|
||||
bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
TABLE_LIST *table_list, bool *need_prelocking) override;
|
||||
|
||||
TABLE_LIST::enum_open_strategy
|
||||
get_fk_open_strategy(thr_lock_type lock_type) const override
|
||||
{
|
||||
return lock_type < TL_FIRST_WRITE ? TABLE_LIST::OPEN_STUB
|
||||
: TABLE_LIST::OPEN_NORMAL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -485,6 +512,23 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class Check_table_prelocking_strategy : public DML_prelocking_strategy
|
||||
{
|
||||
public:
|
||||
bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
TABLE_LIST *table_list, bool *need_prelocking) override;
|
||||
bool may_need_prelocking(THD *thd, TABLE_LIST *table) const override
|
||||
{
|
||||
return check_table_referential_checks_needed(thd);
|
||||
}
|
||||
|
||||
TABLE_LIST::enum_open_strategy
|
||||
get_fk_open_strategy(thr_lock_type lock_type) const override
|
||||
{
|
||||
return TABLE_LIST::OPEN_NORMAL;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool
|
||||
open_tables(THD *thd, const DDL_options_st &options,
|
||||
TABLE_LIST **tables, uint *counter, uint flags)
|
||||
|
|
|
|||
32
sql/table.h
32
sql/table.h
|
|
@ -2499,6 +2499,20 @@ struct TABLE_LIST
|
|||
vers_conditions.name= table->s->vers.name;
|
||||
}
|
||||
|
||||
/**
|
||||
Indicates that if TABLE_LIST object corresponds to the table/view
|
||||
which requires special handling.
|
||||
*/
|
||||
enum enum_open_strategy
|
||||
{
|
||||
/* Normal open. */
|
||||
OPEN_NORMAL= 0,
|
||||
/* Associate a table share only if the the table exists. */
|
||||
OPEN_IF_EXISTS,
|
||||
/* Don't associate a table share. */
|
||||
OPEN_STUB
|
||||
};
|
||||
|
||||
inline void init_one_table_for_prelocking(const LEX_CSTRING *db_arg,
|
||||
const LEX_CSTRING *table_name_arg,
|
||||
const LEX_CSTRING *alias_arg,
|
||||
|
|
@ -2507,6 +2521,7 @@ struct TABLE_LIST
|
|||
TABLE_LIST *belong_to_view_arg,
|
||||
uint8 trg_event_map_arg,
|
||||
TABLE_LIST ***last_ptr,
|
||||
enum_open_strategy open_strat,
|
||||
my_bool insert_data)
|
||||
|
||||
{
|
||||
|
|
@ -2519,8 +2534,7 @@ struct TABLE_LIST
|
|||
belong_to_view= belong_to_view_arg;
|
||||
trg_event_map= trg_event_map_arg;
|
||||
/* MDL is enough for read-only FK checks, we don't need the table */
|
||||
if (prelocking_type == PRELOCK_FK && lock_type < TL_FIRST_WRITE)
|
||||
open_strategy= OPEN_STUB;
|
||||
open_strategy= open_strat;
|
||||
|
||||
**last_ptr= this;
|
||||
prev_global= *last_ptr;
|
||||
|
|
@ -2852,19 +2866,7 @@ struct TABLE_LIST
|
|||
used for implicit LOCK TABLES only and won't be used in real statement.
|
||||
*/
|
||||
prelocking_types prelocking_placeholder;
|
||||
/**
|
||||
Indicates that if TABLE_LIST object corresponds to the table/view
|
||||
which requires special handling.
|
||||
*/
|
||||
enum enum_open_strategy
|
||||
{
|
||||
/* Normal open. */
|
||||
OPEN_NORMAL= 0,
|
||||
/* Associate a table share only if the the table exists. */
|
||||
OPEN_IF_EXISTS,
|
||||
/* Don't associate a table share. */
|
||||
OPEN_STUB
|
||||
} open_strategy;
|
||||
enum_open_strategy open_strategy;
|
||||
/** TRUE if an alias for this table was specified in the SQL. */
|
||||
bool is_alias;
|
||||
/** TRUE if the table is referred to in the statement using a fully
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue