Merge branch '10.4' into 10.5
This commit is contained in:
commit
68a925b325
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user