summaryrefslogtreecommitdiff
path: root/sql/sp_head.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sp_head.cc')
-rw-r--r--sql/sp_head.cc117
1 files changed, 88 insertions, 29 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 750ddee49c3..df258d49c0a 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1,4 +1,4 @@
-/* Copyright 2002-2008 MySQL AB, 2008-2010 Sun Microsystems, Inc.
+/* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +11,7 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
@@ -237,7 +237,6 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_EVENTS:
case SQLCOM_SHOW_KEYS:
case SQLCOM_SHOW_MASTER_STAT:
- case SQLCOM_SHOW_NEW_MASTER:
case SQLCOM_SHOW_OPEN_TABLES:
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
@@ -551,7 +550,7 @@ sp_head::operator delete(void *ptr, size_t size) throw()
sp_head::sp_head()
- :Query_arena(&main_mem_root, INITIALIZED_FOR_SP),
+ :Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP),
m_flags(0),
m_sp_cache_version(0),
unsafe_flags(0),
@@ -1069,7 +1068,7 @@ void sp_head::recursion_level_error(THD *thd)
if (m_type == TYPE_ENUM_PROCEDURE)
{
my_error(ER_SP_RECURSION_LIMIT, MYF(0),
- thd->variables.max_sp_recursion_depth,
+ static_cast<int>(thd->variables.max_sp_recursion_depth),
m_name.str);
}
else
@@ -1178,10 +1177,17 @@ find_handler_after_execution(THD *thd, sp_rcontext *ctx)
/**
Execute the routine. The main instruction jump loop is there.
Assume the parameters already set.
+
+ @param thd Thread context.
+ @param merge_da_on_success Flag specifying if Warning Info should be
+ propagated to the caller on Completion
+ Condition or not.
+
@todo
- Will write this SP statement into binlog separately
(TODO: consider changing the condition to "not inside event union")
+ @return Error status.
@retval
FALSE on success
@retval
@@ -1189,7 +1195,7 @@ find_handler_after_execution(THD *thd, sp_rcontext *ctx)
*/
bool
-sp_head::execute(THD *thd)
+sp_head::execute(THD *thd, bool merge_da_on_success)
{
DBUG_ENTER("sp_head::execute");
char saved_cur_db_name_buf[SAFE_NAME_LEN+1];
@@ -1204,7 +1210,7 @@ sp_head::execute(THD *thd)
Query_arena *old_arena;
/* per-instruction arena */
MEM_ROOT execute_mem_root;
- Query_arena execute_arena(&execute_mem_root, INITIALIZED_FOR_SP),
+ Query_arena execute_arena(&execute_mem_root, STMT_INITIALIZED_FOR_SP),
backup_arena;
query_id_t old_query_id;
TABLE *old_derived_tables;
@@ -1213,9 +1219,33 @@ sp_head::execute(THD *thd)
String old_packet;
Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
Object_creation_ctx *saved_creation_ctx;
- Warning_info *saved_warning_info, warning_info(thd->warning_info->warn_id());
+ Warning_info *saved_warning_info;
+ Warning_info warning_info(thd->warning_info->warn_id(), false);
- /* Use some extra margin for possible SP recursion and functions */
+ /*
+ Just reporting a stack overrun error
+ (@sa check_stack_overrun()) requires stack memory for error
+ message buffer. Thus, we have to put the below check
+ relatively close to the beginning of the execution stack,
+ where available stack margin is still big. As long as the check
+ has to be fairly high up the call stack, the amount of memory
+ we "book" for has to stay fairly high as well, and hence
+ not very accurate. The number below has been calculated
+ by trial and error, and reflects the amount of memory necessary
+ to execute a single stored procedure instruction, be it either
+ an SQL statement, or, heaviest of all, a CALL, which involves
+ parsing and loading of another stored procedure into the cache
+ (@sa db_load_routine() and Bug#10100).
+ At the time of measuring, a recursive SP invocation required
+ 3232 bytes of stack on 32 bit Linux, 6016 bytes on 64 bit Mac
+ and 11152 on 64 bit Solaris sparc.
+ The same with db_load_routine() required circa 7k bytes and
+ 14k bytes accordingly. Hence, here we book the stack with some
+ reasonable margin.
+
+ Reverting back to 8 * STACK_MIN_SIZE until further fix.
+ 8 * STACK_MIN_SIZE is required on some exotic platforms.
+ */
if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (uchar*)&old_packet))
DBUG_RETURN(TRUE);
@@ -1461,10 +1491,17 @@ sp_head::execute(THD *thd)
thd->m_reprepare_observer= save_reprepare_observer;
thd->stmt_arena= old_arena;
- state= EXECUTED;
+ state= STMT_EXECUTED;
- /* Restore the caller's original warning information area. */
- saved_warning_info->merge_with_routine_info(thd, thd->warning_info);
+ /*
+ Restore the caller's original warning information area:
+ - warnings generated during trigger execution should not be
+ propagated to the caller on success;
+ - if there was an exception during execution, warning info should be
+ propagated to the caller in any case.
+ */
+ if (err_status || merge_da_on_success)
+ saved_warning_info->merge_with_routine_info(thd, thd->warning_info);
thd->warning_info= saved_warning_info;
done:
@@ -1478,7 +1515,7 @@ sp_head::execute(THD *thd)
If the DB has changed, the pointer has changed too, but the
original thd->db will then have been freed
*/
- if (cur_db_changed && !thd->killed)
+ if (cur_db_changed && thd->killed != THD::KILL_CONNECTION)
{
/*
Force switching back to the saved current database, because it may be
@@ -1612,7 +1649,7 @@ sp_head::execute_trigger(THD *thd,
sp_rcontext *nctx = NULL;
bool err_status= FALSE;
MEM_ROOT call_mem_root;
- Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP);
+ Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP);
Query_arena backup_arena;
DBUG_ENTER("sp_head::execute_trigger");
@@ -1686,7 +1723,7 @@ sp_head::execute_trigger(THD *thd,
thd->spcont= nctx;
- err_status= execute(thd);
+ err_status= execute(thd, FALSE);
err_with_cleanup:
thd->restore_active_arena(&call_arena, &backup_arena);
@@ -1753,7 +1790,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
String binlog_buf(buf, sizeof(buf), &my_charset_bin);
bool err_status= FALSE;
MEM_ROOT call_mem_root;
- Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP);
+ Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP);
Query_arena backup_arena;
DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("info", ("function %s", m_name.str));
@@ -1903,7 +1940,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
*/
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- err_status= execute(thd);
+ err_status= execute(thd, TRUE);
thd->restore_active_arena(&call_arena, &backup_arena);
@@ -2109,6 +2146,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
thd->mdl_context.release_transactional_locks();
+ else if (! thd->in_sub_stmt)
+ thd->mdl_context.release_statement_locks();
thd->rollback_item_tree_changes();
@@ -2137,7 +2176,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
#endif
if (!err_status)
- err_status= execute(thd);
+ err_status= execute(thd, TRUE);
if (save_log_general)
thd->variables.option_bits &= ~OPTION_LOG_OFF;
@@ -2509,9 +2548,24 @@ void
sp_head::restore_thd_mem_root(THD *thd)
{
DBUG_ENTER("sp_head::restore_thd_mem_root");
- Item *flist= free_list; // The old list
+
+ /*
+ In some cases our parser detects a syntax error and calls
+ LEX::cleanup_lex_after_parse_error() method only after
+ finishing parsing the whole routine. In such a situation
+ sp_head::restore_thd_mem_root() will be called twice - the
+ first time as part of normal parsing process and the second
+ time by cleanup_lex_after_parse_error().
+ To avoid ruining active arena/mem_root state in this case we
+ skip restoration of old arena/mem_root if this method has been
+ already called for this routine.
+ */
+ if (!m_thd)
+ DBUG_VOID_RETURN;
+
+ Item *flist= free_list; // The old list
set_query_arena(thd); // Get new free_list and mem_root
- state= INITIALIZED_FOR_SP;
+ state= STMT_INITIALIZED_FOR_SP;
DBUG_PRINT("info", ("mem_root 0x%lx returned from thd mem root 0x%lx",
(ulong) &mem_root, (ulong) &thd->mem_root));
@@ -2946,6 +3000,8 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
thd->mdl_context.release_transactional_locks();
+ else if (! thd->in_sub_stmt)
+ thd->mdl_context.release_statement_locks();
}
if (m_lex->query_tables_own_last)
@@ -2973,7 +3029,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
(thd->stmt_da->sql_errno() != ER_CANT_REOPEN_TABLE &&
thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE &&
thd->stmt_da->sql_errno() != ER_UPDATE_TABLE_USED))
- thd->stmt_arena->state= Query_arena::EXECUTED;
+ thd->stmt_arena->state= Query_arena::STMT_EXECUTED;
/*
Merge here with the saved parent's values
@@ -3033,14 +3089,11 @@ int sp_instr::exec_core(THD *thd, uint *nextp)
int
sp_instr_stmt::execute(THD *thd, uint *nextp)
{
- char *query;
- uint32 query_length;
int res;
DBUG_ENTER("sp_instr_stmt::execute");
DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
- query= thd->query();
- query_length= thd->query_length();
+ const CSET_STRING query_backup= thd->query_string;
#if defined(ENABLED_PROFILING)
/* This s-p instr is profilable and will be captured. */
thd->profiling.set_query_source(m_query.str, m_query.length);
@@ -3061,7 +3114,12 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
if (thd->stmt_da->is_eof())
+ {
+ /* Finalize server status flags after executing a statement. */
+ thd->update_server_status();
+
thd->protocol->end_statement();
+ }
query_cache_end_of_result(thd);
@@ -3070,7 +3128,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
}
else
*nextp= m_ip+1;
- thd->set_query(query, query_length);
+ thd->set_query(query_backup);
thd->query_name_consts= 0;
if (!thd->is_error())
@@ -3118,7 +3176,7 @@ sp_instr_stmt::exec_core(THD *thd, uint *nextp)
MYSQL_QUERY_EXEC_START(thd->query(),
thd->thread_id,
(char *) (thd->db ? thd->db : ""),
- thd->security_ctx->priv_user,
+ &thd->security_ctx->priv_user[0],
(char *)thd->security_ctx->host_or_ip,
3);
int res= mysql_execute_command(thd);
@@ -4141,7 +4199,8 @@ sp_head::add_used_tables_to_table_list(THD *thd,
*/
table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
table->lock_type >= TL_WRITE_ALLOW_WRITE ?
- MDL_SHARED_WRITE : MDL_SHARED_READ);
+ MDL_SHARED_WRITE : MDL_SHARED_READ,
+ MDL_TRANSACTION);
/* Everyting else should be zeroed */
@@ -4185,7 +4244,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
table->select_lex= lex->current_select;
table->cacheable_table= 1;
table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
- mdl_type);
+ mdl_type, MDL_TRANSACTION);
lex->add_to_query_tables(table);
return table;