mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
Decrease stack space usage of mysql_execute_command()
The extensive usage of stack space, especially when used with ASan (AdressSanitizer) of mysql_execute_command caused the test rpl.rpl_row_sp011 to fail because it did run out of stack. In this test case mysql_execute_command is called recursively for each function all. Changes done: - Changed a few functions that used big local variables to be marked __attribute__ ((noinline)) - Moved sub parts that used big local variables to external functions. - Changed wsrep_commit_empty() from inline to normal function as this used more than 1K of stack space and because there is no reason for this rarely used function to be inline. End result (with gcc 7.4.1 on Intel Xeon): Starting point for stack space usage: gcc -O: 7800 gcc with -fsanitize=address -O (ASan) : 27240 After this patch: gcc -O: 1160 gcc -O0 (debug build) 1584 gcc with -fsanitize=address -O (ASan): 4424 gcc with -fsanitize=address -O2 (ASan): 3874 A 6x improvement and will allow us to run all mtr tests with ASan.
This commit is contained in:
parent
e21408b799
commit
1fbaf8b6a8
3 changed files with 305 additions and 248 deletions
454
sql/sql_parse.cc
454
sql/sql_parse.cc
|
@ -133,6 +133,10 @@ static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state);
|
|||
static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables);
|
||||
static bool execute_show_status(THD *, TABLE_LIST *);
|
||||
static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
|
||||
static bool generate_incident_event(THD *thd);
|
||||
static int show_create_db(THD *thd, LEX *lex);
|
||||
static bool alter_routine(THD *thd, LEX *lex);
|
||||
static bool drop_routine(THD *thd, LEX *lex);
|
||||
|
||||
const char *any_db="*any*"; // Special symbol for check_access
|
||||
|
||||
|
@ -2873,7 +2877,8 @@ bool sp_process_definer(THD *thd)
|
|||
@return FALSE in case of success, TRUE in case of error.
|
||||
*/
|
||||
|
||||
static bool lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
|
||||
static bool __attribute__ ((noinline))
|
||||
lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
|
||||
{
|
||||
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
|
||||
MDL_deadlock_and_lock_abort_error_handler deadlock_handler;
|
||||
|
@ -3034,7 +3039,8 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
|
|||
}
|
||||
|
||||
|
||||
static int mysql_create_routine(THD *thd, LEX *lex)
|
||||
static int __attribute__ ((noinline))
|
||||
mysql_create_routine(THD *thd, LEX *lex)
|
||||
{
|
||||
DBUG_ASSERT(lex->sphead != 0);
|
||||
DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
|
||||
|
@ -4443,40 +4449,8 @@ mysql_execute_command(THD *thd)
|
|||
break;
|
||||
}
|
||||
case SQLCOM_REPLACE:
|
||||
#ifndef DBUG_OFF
|
||||
if (mysql_bin_log.is_open())
|
||||
{
|
||||
/*
|
||||
Generate an incident log event before writing the real event
|
||||
to the binary log. We put this event is before the statement
|
||||
since that makes it simpler to check that the statement was
|
||||
not executed on the slave (since incidents usually stop the
|
||||
slave).
|
||||
|
||||
Observe that any row events that are generated will be
|
||||
generated before.
|
||||
|
||||
This is only for testing purposes and will not be present in a
|
||||
release build.
|
||||
*/
|
||||
|
||||
Incident incident= INCIDENT_NONE;
|
||||
DBUG_PRINT("debug", ("Just before generate_incident()"));
|
||||
DBUG_EXECUTE_IF("incident_database_resync_on_replace",
|
||||
incident= INCIDENT_LOST_EVENTS;);
|
||||
if (incident)
|
||||
{
|
||||
Incident_log_event ev(thd, incident);
|
||||
(void) mysql_bin_log.write(&ev); /* error is ignored */
|
||||
if (mysql_bin_log.rotate_and_purge(true))
|
||||
{
|
||||
res= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DBUG_PRINT("debug", ("Just after generate_incident()"));
|
||||
}
|
||||
#endif
|
||||
if ((res= generate_incident_event(thd)))
|
||||
break;
|
||||
/* fall through */
|
||||
case SQLCOM_INSERT:
|
||||
{
|
||||
|
@ -5090,26 +5064,9 @@ mysql_execute_command(THD *thd)
|
|||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_CREATE_DB:
|
||||
{
|
||||
char db_name_buff[NAME_LEN+1];
|
||||
LEX_CSTRING db_name;
|
||||
DBUG_EXECUTE_IF("4x_server_emul",
|
||||
my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;);
|
||||
|
||||
db_name.str= db_name_buff;
|
||||
db_name.length= lex->name.length;
|
||||
strmov(db_name_buff, lex->name.str);
|
||||
|
||||
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
|
||||
|
||||
if (check_db_name((LEX_STRING*) &db_name))
|
||||
{
|
||||
my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
|
||||
break;
|
||||
}
|
||||
res= mysqld_show_create_db(thd, &db_name, &lex->name, lex->create_info);
|
||||
res= show_create_db(thd, lex);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_CREATE_EVENT:
|
||||
case SQLCOM_ALTER_EVENT:
|
||||
#ifdef HAVE_EVENT_SCHEDULER
|
||||
|
@ -5689,153 +5646,16 @@ mysql_execute_command(THD *thd)
|
|||
|
||||
case SQLCOM_ALTER_PROCEDURE:
|
||||
case SQLCOM_ALTER_FUNCTION:
|
||||
{
|
||||
int sp_result;
|
||||
const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
|
||||
if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db,
|
||||
&lex->spname->m_name, sph, 0))
|
||||
goto error;
|
||||
|
||||
/*
|
||||
Note that if you implement the capability of ALTER FUNCTION to
|
||||
alter the body of the function, this command should be made to
|
||||
follow the restrictions that log-bin-trust-function-creators=0
|
||||
already puts on CREATE FUNCTION.
|
||||
*/
|
||||
/* Conditionally writes to binlog */
|
||||
sp_result= sph->sp_update_routine(thd, lex->spname, &lex->sp_chistics);
|
||||
switch (sp_result)
|
||||
{
|
||||
case SP_OK:
|
||||
my_ok(thd);
|
||||
break;
|
||||
case SP_KEY_NOT_FOUND:
|
||||
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
||||
sph->type_str(), ErrConvDQName(lex->spname).ptr());
|
||||
goto error;
|
||||
default:
|
||||
my_error(ER_SP_CANT_ALTER, MYF(0),
|
||||
sph->type_str(), ErrConvDQName(lex->spname).ptr());
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (alter_routine(thd, lex))
|
||||
goto error;
|
||||
break;
|
||||
case SQLCOM_DROP_PROCEDURE:
|
||||
case SQLCOM_DROP_FUNCTION:
|
||||
case SQLCOM_DROP_PACKAGE:
|
||||
case SQLCOM_DROP_PACKAGE_BODY:
|
||||
{
|
||||
#ifdef HAVE_DLOPEN
|
||||
if (lex->sql_command == SQLCOM_DROP_FUNCTION &&
|
||||
! lex->spname->m_explicit_name)
|
||||
{
|
||||
/* DROP FUNCTION <non qualified name> */
|
||||
udf_func *udf = find_udf(lex->spname->m_name.str,
|
||||
lex->spname->m_name.length);
|
||||
if (udf)
|
||||
{
|
||||
if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0))
|
||||
goto error;
|
||||
|
||||
if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
|
||||
{
|
||||
my_ok(thd);
|
||||
break;
|
||||
}
|
||||
my_error(ER_SP_DROP_FAILED, MYF(0),
|
||||
"FUNCTION (UDF)", lex->spname->m_name.str);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (lex->spname->m_db.str == NULL)
|
||||
{
|
||||
if (lex->if_exists())
|
||||
{
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
|
||||
ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST),
|
||||
"FUNCTION (UDF)", lex->spname->m_name.str);
|
||||
res= FALSE;
|
||||
my_ok(thd);
|
||||
break;
|
||||
}
|
||||
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
||||
"FUNCTION (UDF)", lex->spname->m_name.str);
|
||||
goto error;
|
||||
}
|
||||
/* Fall thought to test for a stored function */
|
||||
}
|
||||
#endif
|
||||
|
||||
int sp_result;
|
||||
const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
|
||||
|
||||
if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db, &lex->spname->m_name,
|
||||
Sp_handler::handler(lex->sql_command), 0))
|
||||
goto error;
|
||||
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
|
||||
|
||||
/* Conditionally writes to binlog */
|
||||
sp_result= sph->sp_drop_routine(thd, lex->spname);
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
/*
|
||||
We're going to issue an implicit REVOKE statement so we close all
|
||||
open tables. We have to keep metadata locks as this ensures that
|
||||
this statement is atomic against concurent FLUSH TABLES WITH READ
|
||||
LOCK. Deadlocks which can arise due to fact that this implicit
|
||||
statement takes metadata locks should be detected by a deadlock
|
||||
detector in MDL subsystem and reported as errors.
|
||||
|
||||
No need to commit/rollback statement transaction, it's not started.
|
||||
|
||||
TODO: Long-term we should either ensure that implicit REVOKE statement
|
||||
is written into binary log as a separate statement or make both
|
||||
dropping of routine and implicit REVOKE parts of one fully atomic
|
||||
statement.
|
||||
*/
|
||||
DBUG_ASSERT(thd->transaction.stmt.is_empty());
|
||||
close_thread_tables(thd);
|
||||
|
||||
if (sp_result != SP_KEY_NOT_FOUND &&
|
||||
sp_automatic_privileges && !opt_noacl &&
|
||||
sp_revoke_privileges(thd, lex->spname->m_db.str, lex->spname->m_name.str,
|
||||
Sp_handler::handler(lex->sql_command)))
|
||||
{
|
||||
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
ER_PROC_AUTO_REVOKE_FAIL,
|
||||
ER_THD(thd, ER_PROC_AUTO_REVOKE_FAIL));
|
||||
/* If this happens, an error should have been reported. */
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
res= sp_result;
|
||||
switch (sp_result) {
|
||||
case SP_OK:
|
||||
my_ok(thd);
|
||||
break;
|
||||
case SP_KEY_NOT_FOUND:
|
||||
if (lex->if_exists())
|
||||
{
|
||||
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
|
||||
ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST),
|
||||
sph->type_str(),
|
||||
ErrConvDQName(lex->spname).ptr());
|
||||
if (!res)
|
||||
my_ok(thd);
|
||||
break;
|
||||
}
|
||||
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
||||
sph->type_str(), ErrConvDQName(lex->spname).ptr());
|
||||
goto error;
|
||||
default:
|
||||
my_error(ER_SP_DROP_FAILED, MYF(0),
|
||||
sph->type_str(), ErrConvDQName(lex->spname).ptr());
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (drop_routine(thd, lex))
|
||||
goto error;
|
||||
break;
|
||||
case SQLCOM_SHOW_CREATE_PROC:
|
||||
case SQLCOM_SHOW_CREATE_FUNC:
|
||||
case SQLCOM_SHOW_CREATE_PACKAGE:
|
||||
|
@ -6358,7 +6178,15 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
|
|||
}
|
||||
|
||||
|
||||
static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
|
||||
/**
|
||||
SHOW STATUS
|
||||
|
||||
Notes: This is noinline as we don't want to have system_status_var (> 3K)
|
||||
to be on the stack of mysql_execute_command()
|
||||
*/
|
||||
|
||||
static bool __attribute__ ((noinline))
|
||||
execute_show_status(THD *thd, TABLE_LIST *all_tables)
|
||||
{
|
||||
bool res;
|
||||
system_status_var old_status_var= thd->status_var;
|
||||
|
@ -6443,8 +6271,9 @@ static TABLE *find_temporary_table_for_rename(THD *thd,
|
|||
}
|
||||
|
||||
|
||||
static bool check_rename_table(THD *thd, TABLE_LIST *first_table,
|
||||
TABLE_LIST *all_tables)
|
||||
static bool __attribute__ ((noinline))
|
||||
check_rename_table(THD *thd, TABLE_LIST *first_table,
|
||||
TABLE_LIST *all_tables)
|
||||
{
|
||||
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
||||
TABLE_LIST *table;
|
||||
|
@ -6483,6 +6312,227 @@ static bool check_rename_table(THD *thd, TABLE_LIST *first_table,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Generate an incident log event before writing the real event
|
||||
to the binary log. We put this event is before the statement
|
||||
since that makes it simpler to check that the statement was
|
||||
not executed on the slave (since incidents usually stop the
|
||||
slave).
|
||||
|
||||
Observe that any row events that are generated will be generated before.
|
||||
|
||||
This is only for testing purposes and will not be present in a release build.
|
||||
*/
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
static bool __attribute__ ((noinline)) generate_incident_event(THD *thd)
|
||||
{
|
||||
if (mysql_bin_log.is_open())
|
||||
{
|
||||
|
||||
Incident incident= INCIDENT_NONE;
|
||||
DBUG_PRINT("debug", ("Just before generate_incident()"));
|
||||
DBUG_EXECUTE_IF("incident_database_resync_on_replace",
|
||||
incident= INCIDENT_LOST_EVENTS;);
|
||||
if (incident)
|
||||
{
|
||||
Incident_log_event ev(thd, incident);
|
||||
(void) mysql_bin_log.write(&ev); /* error is ignored */
|
||||
if (mysql_bin_log.rotate_and_purge(true))
|
||||
return 1;
|
||||
}
|
||||
DBUG_PRINT("debug", ("Just after generate_incident()"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static bool generate_incident_event(THD *thd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int __attribute__ ((noinline))
|
||||
show_create_db(THD *thd, LEX *lex)
|
||||
{
|
||||
char db_name_buff[NAME_LEN+1];
|
||||
LEX_CSTRING db_name;
|
||||
DBUG_EXECUTE_IF("4x_server_emul",
|
||||
my_error(ER_UNKNOWN_ERROR, MYF(0)); return 1;);
|
||||
|
||||
db_name.str= db_name_buff;
|
||||
db_name.length= lex->name.length;
|
||||
strmov(db_name_buff, lex->name.str);
|
||||
|
||||
if (check_db_name((LEX_STRING*) &db_name))
|
||||
{
|
||||
my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
|
||||
return 1;
|
||||
}
|
||||
return mysqld_show_create_db(thd, &db_name, &lex->name, lex->create_info);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Called on SQLCOM_ALTER_PROCEDURE and SQLCOM_ALTER_FUNCTION
|
||||
*/
|
||||
|
||||
static bool __attribute__ ((noinline))
|
||||
alter_routine(THD *thd, LEX *lex)
|
||||
{
|
||||
int sp_result;
|
||||
const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
|
||||
if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db,
|
||||
&lex->spname->m_name, sph, 0))
|
||||
return 1;
|
||||
/*
|
||||
Note that if you implement the capability of ALTER FUNCTION to
|
||||
alter the body of the function, this command should be made to
|
||||
follow the restrictions that log-bin-trust-function-creators=0
|
||||
already puts on CREATE FUNCTION.
|
||||
*/
|
||||
/* Conditionally writes to binlog */
|
||||
sp_result= sph->sp_update_routine(thd, lex->spname, &lex->sp_chistics);
|
||||
switch (sp_result) {
|
||||
case SP_OK:
|
||||
my_ok(thd);
|
||||
return 0;
|
||||
case SP_KEY_NOT_FOUND:
|
||||
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
||||
sph->type_str(), ErrConvDQName(lex->spname).ptr());
|
||||
return 1;
|
||||
default:
|
||||
my_error(ER_SP_CANT_ALTER, MYF(0),
|
||||
sph->type_str(), ErrConvDQName(lex->spname).ptr());
|
||||
return 1;
|
||||
}
|
||||
return 0; /* purecov: deadcode */
|
||||
}
|
||||
|
||||
|
||||
static bool __attribute__ ((noinline))
|
||||
drop_routine(THD *thd, LEX *lex)
|
||||
{
|
||||
int sp_result;
|
||||
#ifdef HAVE_DLOPEN
|
||||
if (lex->sql_command == SQLCOM_DROP_FUNCTION &&
|
||||
! lex->spname->m_explicit_name)
|
||||
{
|
||||
/* DROP FUNCTION <non qualified name> */
|
||||
udf_func *udf = find_udf(lex->spname->m_name.str,
|
||||
lex->spname->m_name.length);
|
||||
if (udf)
|
||||
{
|
||||
if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0))
|
||||
return 1;
|
||||
|
||||
if (!mysql_drop_function(thd, &lex->spname->m_name))
|
||||
{
|
||||
my_ok(thd);
|
||||
return 0;
|
||||
}
|
||||
my_error(ER_SP_DROP_FAILED, MYF(0),
|
||||
"FUNCTION (UDF)", lex->spname->m_name.str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lex->spname->m_db.str == NULL)
|
||||
{
|
||||
if (lex->if_exists())
|
||||
{
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
|
||||
ER_SP_DOES_NOT_EXIST,
|
||||
ER_THD(thd, ER_SP_DOES_NOT_EXIST),
|
||||
"FUNCTION (UDF)", lex->spname->m_name.str);
|
||||
my_ok(thd);
|
||||
return 0;
|
||||
}
|
||||
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
||||
"FUNCTION (UDF)", lex->spname->m_name.str);
|
||||
return 1;
|
||||
}
|
||||
/* Fall trough to test for a stored function */
|
||||
}
|
||||
#endif /* HAVE_DLOPEN */
|
||||
|
||||
const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
|
||||
|
||||
if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db,
|
||||
&lex->spname->m_name,
|
||||
Sp_handler::handler(lex->sql_command), 0))
|
||||
return 1;
|
||||
|
||||
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
|
||||
|
||||
/* Conditionally writes to binlog */
|
||||
sp_result= sph->sp_drop_routine(thd, lex->spname);
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
/*
|
||||
We're going to issue an implicit REVOKE statement so we close all
|
||||
open tables. We have to keep metadata locks as this ensures that
|
||||
this statement is atomic against concurent FLUSH TABLES WITH READ
|
||||
LOCK. Deadlocks which can arise due to fact that this implicit
|
||||
statement takes metadata locks should be detected by a deadlock
|
||||
detector in MDL subsystem and reported as errors.
|
||||
|
||||
No need to commit/rollback statement transaction, it's not started.
|
||||
|
||||
TODO: Long-term we should either ensure that implicit REVOKE statement
|
||||
is written into binary log as a separate statement or make both
|
||||
dropping of routine and implicit REVOKE parts of one fully atomic
|
||||
statement.
|
||||
*/
|
||||
DBUG_ASSERT(thd->transaction.stmt.is_empty());
|
||||
close_thread_tables(thd);
|
||||
|
||||
if (sp_result != SP_KEY_NOT_FOUND &&
|
||||
sp_automatic_privileges && !opt_noacl &&
|
||||
sp_revoke_privileges(thd, lex->spname->m_db.str, lex->spname->m_name.str,
|
||||
Sp_handler::handler(lex->sql_command)))
|
||||
{
|
||||
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
ER_PROC_AUTO_REVOKE_FAIL,
|
||||
ER_THD(thd, ER_PROC_AUTO_REVOKE_FAIL));
|
||||
/* If this happens, an error should have been reported. */
|
||||
return 1;
|
||||
}
|
||||
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
||||
|
||||
switch (sp_result) {
|
||||
case SP_OK:
|
||||
my_ok(thd);
|
||||
return 0;
|
||||
case SP_KEY_NOT_FOUND:
|
||||
int res;
|
||||
if (lex->if_exists())
|
||||
{
|
||||
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
|
||||
ER_SP_DOES_NOT_EXIST,
|
||||
ER_THD(thd, ER_SP_DOES_NOT_EXIST),
|
||||
sph->type_str(),
|
||||
ErrConvDQName(lex->spname).ptr());
|
||||
if (res)
|
||||
return 1;
|
||||
my_ok(thd);
|
||||
return 0;
|
||||
}
|
||||
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
||||
sph->type_str(), ErrConvDQName(lex->spname).ptr());
|
||||
return 1;
|
||||
default:
|
||||
my_error(ER_SP_DROP_FAILED, MYF(0),
|
||||
sph->type_str(), ErrConvDQName(lex->spname).ptr());
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef WITH_WSREP
|
||||
wsrep_error_label:
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Compare requested privileges with the privileges acquired from the
|
||||
|
@ -9150,8 +9200,8 @@ void sql_kill(THD *thd, longlong id, killed_state state, killed_type type)
|
|||
}
|
||||
|
||||
|
||||
static
|
||||
void sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
|
||||
static void __attribute__ ((noinline))
|
||||
sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
|
||||
{
|
||||
uint error;
|
||||
ha_rows rows;
|
||||
|
|
|
@ -2782,3 +2782,54 @@ bool wsrep_consistency_check(THD *thd)
|
|||
{
|
||||
return thd->wsrep_consistency_check == CONSISTENCY_CHECK_RUNNING;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Commit an empty transaction.
|
||||
|
||||
If the transaction is real and the wsrep transaction is still active,
|
||||
the transaction did not generate any rows or keys and is committed
|
||||
as empty. Here the wsrep transaction is rolled back and after statement
|
||||
step is performed to leave the wsrep transaction in the state as it
|
||||
never existed.
|
||||
|
||||
This should not be an inline functions as it requires a lot of stack space
|
||||
because of WSREP_DBUG() usage. It's also not a function that is
|
||||
frequently called.
|
||||
*/
|
||||
|
||||
void wsrep_commit_empty(THD* thd, bool all)
|
||||
{
|
||||
DBUG_ENTER("wsrep_commit_empty");
|
||||
WSREP_DEBUG("wsrep_commit_empty(%llu)", thd->thread_id);
|
||||
if (wsrep_is_real(thd, all) &&
|
||||
wsrep_thd_is_local(thd) &&
|
||||
thd->wsrep_trx().active() &&
|
||||
thd->wsrep_trx().state() != wsrep::transaction::s_committed)
|
||||
{
|
||||
/* @todo CTAS with STATEMENT binlog format and empty result set
|
||||
seems to be committing empty. Figure out why and try to fix
|
||||
elsewhere. */
|
||||
DBUG_ASSERT(!wsrep_has_changes(thd) ||
|
||||
(thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
|
||||
!thd->is_current_stmt_binlog_format_row()));
|
||||
bool have_error= wsrep_current_error(thd);
|
||||
int ret= wsrep_before_rollback(thd, all) ||
|
||||
wsrep_after_rollback(thd, all) ||
|
||||
wsrep_after_statement(thd);
|
||||
/* The committing transaction was empty but it held some locks and
|
||||
got BF aborted. As there were no certified changes in the
|
||||
data, we ignore the deadlock error and rely on error reporting
|
||||
by storage engine/server. */
|
||||
if (!ret && !have_error && wsrep_current_error(thd))
|
||||
{
|
||||
DBUG_ASSERT(wsrep_current_error(thd) == wsrep::e_deadlock_error);
|
||||
thd->wsrep_cs().reset_error();
|
||||
}
|
||||
if (ret)
|
||||
{
|
||||
WSREP_DEBUG("wsrep_commit_empty failed: %d", wsrep_current_error(thd));
|
||||
}
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
class THD;
|
||||
|
||||
void wsrep_commit_empty(THD* thd, bool all);
|
||||
|
||||
/*
|
||||
Return true if THD has active wsrep transaction.
|
||||
*/
|
||||
|
@ -466,50 +468,4 @@ wsrep_current_error_status(THD* thd)
|
|||
return thd->wsrep_cs().current_error_status();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Commit an empty transaction.
|
||||
|
||||
If the transaction is real and the wsrep transaction is still active,
|
||||
the transaction did not generate any rows or keys and is committed
|
||||
as empty. Here the wsrep transaction is rolled back and after statement
|
||||
step is performed to leave the wsrep transaction in the state as it
|
||||
never existed.
|
||||
*/
|
||||
static inline void wsrep_commit_empty(THD* thd, bool all)
|
||||
{
|
||||
DBUG_ENTER("wsrep_commit_empty");
|
||||
WSREP_DEBUG("wsrep_commit_empty(%llu)", thd->thread_id);
|
||||
if (wsrep_is_real(thd, all) &&
|
||||
wsrep_thd_is_local(thd) &&
|
||||
thd->wsrep_trx().active() &&
|
||||
thd->wsrep_trx().state() != wsrep::transaction::s_committed)
|
||||
{
|
||||
/* @todo CTAS with STATEMENT binlog format and empty result set
|
||||
seems to be committing empty. Figure out why and try to fix
|
||||
elsewhere. */
|
||||
DBUG_ASSERT(!wsrep_has_changes(thd) ||
|
||||
(thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
|
||||
!thd->is_current_stmt_binlog_format_row()));
|
||||
bool have_error= wsrep_current_error(thd);
|
||||
int ret= wsrep_before_rollback(thd, all) ||
|
||||
wsrep_after_rollback(thd, all) ||
|
||||
wsrep_after_statement(thd);
|
||||
/* The committing transaction was empty but it held some locks and
|
||||
got BF aborted. As there were no certified changes in the
|
||||
data, we ignore the deadlock error and rely on error reporting
|
||||
by storage engine/server. */
|
||||
if (!ret && !have_error && wsrep_current_error(thd))
|
||||
{
|
||||
DBUG_ASSERT(wsrep_current_error(thd) == wsrep::e_deadlock_error);
|
||||
thd->wsrep_cs().reset_error();
|
||||
}
|
||||
if (ret)
|
||||
{
|
||||
WSREP_DEBUG("wsrep_commit_empty failed: %d", wsrep_current_error(thd));
|
||||
}
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
#endif /* WSREP_TRANS_OBSERVER */
|
||||
|
|
Loading…
Add table
Reference in a new issue