Skip to content

Commit

Permalink
Merge branch '10.5' into 10.6
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitryshulga committed Sep 5, 2023
2 parents b0a4381 + 68a925b commit de5dba9
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 22 deletions.
9 changes: 9 additions & 0 deletions CMakeLists.txt
Expand Up @@ -193,6 +193,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)

Expand Down
4 changes: 4 additions & 0 deletions include/my_alloc.h
Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions mysys/my_alloc.c
Expand Up @@ -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)
Expand Down Expand Up @@ -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 */
Expand Down
67 changes: 67 additions & 0 deletions sql/sp_head.cc
Expand Up @@ -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),
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1537,6 +1549,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);
Expand Down Expand Up @@ -1649,6 +1671,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--;
Expand Down Expand Up @@ -3292,6 +3328,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()
{
Expand Down
37 changes: 37 additions & 0 deletions sql/sp_head.h
Expand Up @@ -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 {
Expand Down Expand Up @@ -823,6 +833,11 @@ class sp_head :private Query_arena,
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,
Expand Down Expand Up @@ -1110,6 +1125,9 @@ class sp_instr :public Query_arena, public Sql_alloc
/// 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()
Expand Down Expand Up @@ -1200,6 +1218,25 @@ class sp_instr :public Query_arena, public Sql_alloc
}
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


Expand Down
19 changes: 10 additions & 9 deletions sql/sql_base.cc
Expand Up @@ -1944,17 +1944,17 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
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.
*/
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);

Expand Down Expand Up @@ -3908,18 +3908,19 @@ 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;

/* 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.
Expand Down
44 changes: 44 additions & 0 deletions sql/sql_prepare.cc
Expand Up @@ -176,6 +176,16 @@ class Prepared_statement: public Statement
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;
Expand Down Expand Up @@ -4106,6 +4116,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),
Expand Down Expand Up @@ -4180,8 +4193,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);

Expand Down Expand Up @@ -4375,6 +4393,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
Expand Down Expand Up @@ -4646,9 +4668,31 @@ Prepared_statement::execute_loop(String *expanded_query,
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;
}
Expand Down

0 comments on commit de5dba9

Please sign in to comment.