Merge branch '10.4' into 10.5

This commit is contained in:
Dmitry Shulga 2023-09-05 12:41:49 +07:00
commit 68a925b325
9 changed files with 210 additions and 22 deletions

View File

@ -205,6 +205,15 @@ ENDIF()
OPTION(NOT_FOR_DISTRIBUTION "Allow linking with GPLv2-incompatible system libraries. Only set it you never plan to distribute the resulting binaries" OFF)
#
# Enable protection of statement's memory root after first SP/PS execution.
# Can be switched on only for debug build.
#
OPTION(WITH_PROTECT_STATEMENT_MEMROOT "Enable protection of statement's memory root after first SP/PS execution. Turned into account only for debug build" OFF)
IF (CMAKE_BUILD_TYPE MATCHES "Debug" AND WITH_PROTECT_STATEMENT_MEMROOT)
ADD_DEFINITIONS(-DPROTECT_STATEMENT_MEMROOT)
ENDIF()
INCLUDE(check_compiler_flag)
INCLUDE(check_linker_flag)

View File

@ -52,6 +52,10 @@ typedef struct st_mem_root
*/
unsigned int first_block_usage;
#ifdef PROTECT_STATEMENT_MEMROOT
int read_only;
#endif
void (*error_handler)(void);
PSI_memory_key m_psi_key;

View File

@ -77,6 +77,9 @@ void init_alloc_root(PSI_memory_key key, MEM_ROOT *mem_root, size_t block_size,
mem_root->block_num= 4; /* We shift this with >>2 */
mem_root->first_block_usage= 0;
mem_root->m_psi_key= key;
#ifdef PROTECT_STATEMENT_MEMROOT
mem_root->read_only= 0;
#endif
#if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG))
if (pre_alloc_size)
@ -216,6 +219,10 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
DBUG_PRINT("enter",("root: %p", mem_root));
DBUG_ASSERT(alloc_root_inited(mem_root));
#ifdef PROTECT_STATEMENT_MEMROOT
DBUG_ASSERT(mem_root->read_only == 0);
#endif
DBUG_EXECUTE_IF("simulate_out_of_memory",
{
/* Avoid reusing an already allocated block */

View File

@ -534,6 +534,9 @@ sp_head::sp_head(MEM_ROOT *mem_root_arg, sp_package *parent,
:Query_arena(NULL, STMT_INITIALIZED_FOR_SP),
Database_qualified_name(&null_clex_str, &null_clex_str),
main_mem_root(*mem_root_arg),
#ifdef PROTECT_STATEMENT_MEMROOT
executed_counter(0),
#endif
m_parent(parent),
m_handler(sph),
m_flags(0),
@ -791,6 +794,10 @@ sp_head::init(LEX *lex)
*/
lex->trg_table_fields.empty();
#ifdef PROTECT_STATEMENT_MEMROOT
executed_counter= 0;
#endif
DBUG_VOID_RETURN;
}
@ -1434,6 +1441,11 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
err_status= i->execute(thd, &ip);
#ifdef PROTECT_STATEMENT_MEMROOT
if (!err_status)
i->mark_as_run();
#endif
#ifdef HAVE_PSI_STATEMENT_INTERFACE
MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
thd->m_statement_psi= parent_locker;
@ -1535,6 +1547,16 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
/* Reset sp_rcontext::end_partial_result_set flag. */
ctx->end_partial_result_set= FALSE;
#ifdef PROTECT_STATEMENT_MEMROOT
if (thd->is_error())
{
// Don't count a call ended with an error as normal run
executed_counter= 0;
main_mem_root.read_only= 0;
reset_instrs_executed_counter();
}
#endif
} while (!err_status && likely(!thd->killed) &&
likely(!thd->is_fatal_error) &&
!thd->spcont->pause_state);
@ -1647,6 +1669,20 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
err_status|= mysql_change_db(thd, (LEX_CSTRING*)&saved_cur_db_name, TRUE) != 0;
}
#ifdef PROTECT_STATEMENT_MEMROOT
if (!err_status)
{
if (!main_mem_root.read_only &&
has_all_instrs_executed())
{
main_mem_root.read_only= 1;
}
++executed_counter;
DBUG_PRINT("info", ("execute counter: %lu", executed_counter));
}
#endif
m_flags&= ~IS_INVOKED;
if (m_parent)
m_parent->m_invoked_subroutine_count--;
@ -3287,6 +3323,37 @@ void sp_head::add_mark_lead(uint ip, List<sp_instr> *leads)
leads->push_front(i);
}
#ifdef PROTECT_STATEMENT_MEMROOT
int sp_head::has_all_instrs_executed()
{
sp_instr *ip;
uint count= 0;
for (uint i= 0; i < m_instr.elements; ++i)
{
get_dynamic(&m_instr, (uchar*)&ip, i);
if (ip->has_been_run())
++count;
}
return count == m_instr.elements;
}
void sp_head::reset_instrs_executed_counter()
{
sp_instr *ip;
for (uint i= 0; i < m_instr.elements; ++i)
{
get_dynamic(&m_instr, (uchar*)&ip, i);
ip->mark_as_not_run();
}
}
#endif
void
sp_head::opt_mark()
{

View File

@ -140,6 +140,16 @@ class sp_head :private Query_arena,
protected:
MEM_ROOT main_mem_root;
#ifdef PROTECT_STATEMENT_MEMROOT
/*
The following data member is wholly for debugging purpose.
It can be used for possible crash analysis to determine how many times
the stored routine was executed before the mem_root marked read_only
was requested for a memory chunk. Additionally, a value of this data
member is output to the log with DBUG_PRINT.
*/
ulong executed_counter;
#endif
public:
/** Possible values of m_flags */
enum {
@ -823,6 +833,11 @@ public:
return ip;
}
#ifdef PROTECT_STATEMENT_MEMROOT
int has_all_instrs_executed();
void reset_instrs_executed_counter();
#endif
/* Add tables used by routine to the table list. */
bool add_used_tables_to_table_list(THD *thd,
TABLE_LIST ***query_tables_last_ptr,
@ -1109,6 +1124,9 @@ public:
/// Should give each a name or type code for debugging purposes?
sp_instr(uint ip, sp_pcontext *ctx)
:Query_arena(0, STMT_INITIALIZED_FOR_SP), marked(0), m_ip(ip), m_ctx(ctx)
#ifdef PROTECT_STATEMENT_MEMROOT
, m_has_been_run(false)
#endif
{}
virtual ~sp_instr()
@ -1199,6 +1217,25 @@ public:
}
virtual PSI_statement_info* get_psi_info() = 0;
#ifdef PROTECT_STATEMENT_MEMROOT
bool has_been_run() const
{
return m_has_been_run;
}
void mark_as_run()
{
m_has_been_run= true;
}
void mark_as_not_run()
{
m_has_been_run= false;
}
private:
bool m_has_been_run;
#endif
}; // class sp_instr : public Sql_alloc

View File

@ -1918,10 +1918,6 @@ retry_share:
goto err_lock;
}
/* Open view */
if (mysql_make_view(thd, share, table_list, false))
goto err_lock;
/*
This table is a view. Validate its metadata version: in particular,
that it was a view when the statement was prepared.
@ -1929,6 +1925,10 @@ retry_share:
if (check_and_update_table_version(thd, table_list, share))
goto err_lock;
/* Open view */
if (mysql_make_view(thd, share, table_list, false))
goto err_lock;
/* TODO: Don't free this */
tdc_release_share(share);
@ -3863,6 +3863,12 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
if (tables->open_strategy && !tables->table)
goto end;
/* Check and update metadata version of a base table. */
error= check_and_update_table_version(thd, tables, tables->table->s);
if (unlikely(error))
goto end;
error= extend_table_list(thd, tables, prelocking_strategy, has_prelocking_list);
if (unlikely(error))
goto end;
@ -3870,11 +3876,6 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
/* Copy grant information from TABLE_LIST instance to TABLE one. */
tables->table->grant= tables->grant;
/* Check and update metadata version of a base table. */
error= check_and_update_table_version(thd, tables, tables->table->s);
if (unlikely(error))
goto end;
/*
After opening a MERGE table add the children to the query list of
tables, so that they are opened too.

View File

@ -172,6 +172,16 @@ public:
Server_side_cursor *cursor;
uchar *packet;
uchar *packet_end;
#ifdef PROTECT_STATEMENT_MEMROOT
/*
The following data member is wholly for debugging purpose.
It can be used for possible crash analysis to determine how many times
the stored routine was executed before the mem_root marked read_only
was requested for a memory chunk. Additionally, a value of this data
member is output to the log with DBUG_PRINT.
*/
ulong executed_counter;
#endif
uint param_count;
uint last_errno;
uint flags;
@ -4004,6 +4014,9 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
cursor(0),
packet(0),
packet_end(0),
#ifdef PROTECT_STATEMENT_MEMROOT
executed_counter(0),
#endif
param_count(0),
last_errno(0),
flags((uint) IS_IN_USE),
@ -4078,8 +4091,13 @@ void Prepared_statement::setup_set_params()
Prepared_statement::~Prepared_statement()
{
DBUG_ENTER("Prepared_statement::~Prepared_statement");
#ifdef PROTECT_STATEMENT_MEMROOT
DBUG_PRINT("enter",("stmt: %p cursor: %p executed_counter: %lu",
this, cursor, executed_counter));
#else
DBUG_PRINT("enter",("stmt: %p cursor: %p",
this, cursor));
#endif
MYSQL_DESTROY_PS(m_prepared_stmt);
@ -4261,6 +4279,10 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
}
lex->set_trg_event_type_for_tables();
#ifdef PROTECT_STATEMENT_MEMROOT
executed_counter= 0;
#endif
/*
While doing context analysis of the query (in check_prepared_statement)
we allocate a lot of additional memory: for open tables, JOINs, derived
@ -4532,9 +4554,31 @@ reexecute:
error= reprepare();
if (likely(!error)) /* Success */
{
#ifdef PROTECT_STATEMENT_MEMROOT
// There was reprepare so the counter of runs should be reset
executed_counter= 0;
mem_root->read_only= 0;
#endif
goto reexecute;
}
}
reset_stmt_params(this);
#ifdef PROTECT_STATEMENT_MEMROOT
if (!error)
{
mem_root->read_only= 1;
++executed_counter;
DBUG_PRINT("info", ("execute counter: %lu", executed_counter));
}
else
{
// Error on call shouldn't be counted as a normal run
executed_counter= 0;
mem_root->read_only= 0;
}
#endif
return error;
}

View File

@ -735,8 +735,7 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array,
ORDER *group,
List<Window_spec> &win_specs,
List<Item_window_func> &win_funcs,
bool *hidden_group_fields,
uint *reserved)
bool *hidden_group_fields)
{
int res;
enum_parsing_place save_place;
@ -751,13 +750,6 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array,
thd->lex->allow_sum_func.clear_bit(select->nest_level);
res= setup_conds(thd, tables, leaves, conds);
if (thd->lex->current_select->first_cond_optimization)
{
if (!res && *conds && ! thd->lex->current_select->merged_into)
(*reserved)= (*conds)->exists2in_reserved_items();
else
(*reserved)= 0;
}
/* it's not wrong to have non-aggregated columns in a WHERE */
select->set_non_agg_field_used(saved_non_agg_field_used);
@ -1324,6 +1316,15 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
DBUG_ASSERT(select_lex->hidden_bit_fields == 0);
if (setup_wild(thd, tables_list, fields_list, &all_fields, select_lex, false))
DBUG_RETURN(-1);
if (thd->lex->current_select->first_cond_optimization)
{
if ( conds && ! thd->lex->current_select->merged_into)
select_lex->select_n_reserved= conds->exists2in_reserved_items();
else
select_lex->select_n_reserved= 0;
}
if (select_lex->setup_ref_array(thd, real_og_num))
DBUG_RETURN(-1);
@ -1342,8 +1343,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
all_fields, &conds, order, group_list,
select_lex->window_specs,
select_lex->window_funcs,
&hidden_group_fields,
&select_lex->select_n_reserved))
&hidden_group_fields))
DBUG_RETURN(-1);
/*

View File

@ -4902,7 +4902,8 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table,
open_tables function for this table
*/
static int fill_schema_table_from_frm(THD *thd, TABLE *table,
static int fill_schema_table_from_frm(THD *thd, MEM_ROOT *mem_root,
TABLE *table,
ST_SCHEMA_TABLE *schema_table,
LEX_CSTRING *db_name,
LEX_CSTRING *table_name,
@ -4914,6 +4915,9 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table,
TABLE_LIST table_list;
uint res= 0;
char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
Query_arena i_s_arena(mem_root, Query_arena::STMT_CONVENTIONAL_EXECUTION);
Query_arena backup_arena, *old_arena;
bool i_s_arena_active= false;
bzero((char*) &table_list, sizeof(TABLE_LIST));
bzero((char*) &tbl, sizeof(TABLE));
@ -4988,6 +4992,11 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table,
goto end;
}
old_arena= thd->stmt_arena;
thd->stmt_arena= &i_s_arena;
thd->set_n_backup_active_arena(&i_s_arena, &backup_arena);
i_s_arena_active= true;
share= tdc_acquire_share(thd, &table_list, GTS_TABLE | GTS_VIEW);
if (!share)
{
@ -5069,7 +5078,16 @@ end:
savepoint is safe.
*/
DBUG_ASSERT(thd->open_tables == NULL);
thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp);
if (i_s_arena_active)
{
thd->stmt_arena= old_arena;
thd->restore_active_arena(&i_s_arena, &backup_arena);
i_s_arena.free_items();
}
if (!thd->is_fatal_error)
thd->clear_error();
return res;
@ -5298,7 +5316,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
if (!(table_open_method & ~OPEN_FRM_ONLY) &&
db_name != &INFORMATION_SCHEMA_NAME)
{
if (!fill_schema_table_from_frm(thd, table, schema_table,
if (!fill_schema_table_from_frm(thd, &tmp_mem_root,
table, schema_table,
db_name, table_name,
&open_tables_state_backup,
can_deadlock))