diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 486 |
1 files changed, 256 insertions, 230 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ab915bf3fcc..9b1cb836867 100644 --- a/sql/sql_parse.cc +++ b/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 */ @@ -4096,31 +4102,7 @@ mysql_execute_command(THD *thd) mysql_mutex_unlock(&LOCK_active_mi); break; } - case SQLCOM_SHOW_SLAVE_STAT: - { - /* Accept one of two privileges */ - if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) - goto error; - if (lex->verbose) - { - mysql_mutex_lock(&LOCK_active_mi); - res= show_all_master_info(thd); - mysql_mutex_unlock(&LOCK_active_mi); - } - else - { - LEX_MASTER_INFO *lex_mi= &thd->lex->mi; - Master_info *mi; - if ((mi= get_master_info(&lex_mi->connection_name, - Sql_condition::WARN_LEVEL_ERROR))) - { - res= show_master_info(thd, mi, 0); - mi->release(); - } - } - break; - } case SQLCOM_SHOW_MASTER_STAT: { /* Accept one of two privileges */ @@ -4468,40 +4450,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: { @@ -5115,26 +5065,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 @@ -5714,153 +5647,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: @@ -6091,12 +5887,15 @@ mysql_execute_command(THD *thd) DBUG_ASSERT(first_table == all_tables && first_table != 0); /* fall through */ case SQLCOM_ALTER_SEQUENCE: + case SQLCOM_SHOW_SLAVE_STAT: case SQLCOM_SIGNAL: case SQLCOM_RESIGNAL: case SQLCOM_GET_DIAGNOSTICS: case SQLCOM_CALL: DBUG_ASSERT(lex->m_sql_cmd != NULL); res= lex->m_sql_cmd->execute(thd); + DBUG_PRINT("result", ("res: %d killed: %d is_error: %d", + res, thd->killed, thd->is_error())); break; default: @@ -6380,7 +6179,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; @@ -6465,8 +6272,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; @@ -6505,6 +6313,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 @@ -7503,10 +7532,7 @@ void THD::reset_for_next_command(bool do_clear_error) void mysql_init_select(LEX *lex) { - SELECT_LEX *select_lex= lex->current_select; - select_lex->init_select(); - lex->wild= 0; - lex->exchange= 0; + lex->init_select(); } @@ -9220,8 +9246,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; |