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.cc1110
1 files changed, 655 insertions, 455 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index b1bfab40acd..d1f920fd3a5 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -32,6 +32,8 @@
#include <my_user.h>
+extern "C" uchar *sp_table_key(const uchar *ptr, size_t *plen, my_bool first);
+
Item_result
sp_map_result_type(enum enum_field_types type)
{
@@ -78,19 +80,20 @@ sp_map_item_type(enum enum_field_types type)
}
-/*
+/**
Return a string representation of the Item value.
- NOTE: If the item has a string result type, the string is escaped
- according to its character set.
+ @param thd thread handle
+ @param str string buffer for representation of the value
- SYNOPSIS
- item a pointer to the Item
- str string buffer for representation of the value
+ @note
+ If the item has a string result type, the string is escaped
+ according to its character set.
- RETURN
- NULL on error
- a pointer to valid a valid string on success
+ @retval
+ NULL on error
+ @retval
+ non-NULL a pointer to valid a valid string on success
*/
static String *
@@ -136,16 +139,12 @@ sp_get_item_value(THD *thd, Item *item, String *str)
}
-/*
- SYNOPSIS
- sp_get_flags_for_command()
-
- DESCRIPTION
- Returns a combination of:
- * sp_head::MULTI_RESULTS: added if the 'cmd' is a command that might
- result in multiple result sets being sent back.
- * sp_head::CONTAINS_DYNAMIC_SQL: added if 'cmd' is one of PREPARE,
- EXECUTE, DEALLOCATE.
+/**
+ Returns a combination of:
+ - sp_head::MULTI_RESULTS: added if the 'cmd' is a command that might
+ result in multiple result sets being sent back.
+ - sp_head::CONTAINS_DYNAMIC_SQL: added if 'cmd' is one of PREPARE,
+ EXECUTE, DEALLOCATE.
*/
uint
@@ -162,34 +161,42 @@ sp_get_flags_for_command(LEX *lex)
}
/* fallthrough */
case SQLCOM_ANALYZE:
+ case SQLCOM_BACKUP_TABLE:
case SQLCOM_OPTIMIZE:
case SQLCOM_PRELOAD_KEYS:
case SQLCOM_ASSIGN_TO_KEYCACHE:
case SQLCOM_CHECKSUM:
case SQLCOM_CHECK:
case SQLCOM_HA_READ:
+ case SQLCOM_SHOW_AUTHORS:
case SQLCOM_SHOW_BINLOGS:
case SQLCOM_SHOW_BINLOG_EVENTS:
case SQLCOM_SHOW_CHARSETS:
case SQLCOM_SHOW_COLLATIONS:
case SQLCOM_SHOW_COLUMN_TYPES:
+ case SQLCOM_SHOW_CONTRIBUTORS:
case SQLCOM_SHOW_CREATE:
case SQLCOM_SHOW_CREATE_DB:
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_CREATE_PROC:
+ case SQLCOM_SHOW_CREATE_EVENT:
+ case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_ERRORS:
case SQLCOM_SHOW_FIELDS:
+ case SQLCOM_SHOW_FUNC_CODE:
case SQLCOM_SHOW_GRANTS:
- case SQLCOM_SHOW_INNODB_STATUS:
+ case SQLCOM_SHOW_ENGINE_STATUS:
+ case SQLCOM_SHOW_ENGINE_LOGS:
+ case SQLCOM_SHOW_ENGINE_MUTEX:
+ case SQLCOM_SHOW_EVENTS:
case SQLCOM_SHOW_KEYS:
- case SQLCOM_SHOW_LOGS:
case SQLCOM_SHOW_MASTER_STAT:
- case SQLCOM_SHOW_MUTEX_STATUS:
case SQLCOM_SHOW_NEW_MASTER:
case SQLCOM_SHOW_OPEN_TABLES:
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
+ case SQLCOM_SHOW_PROC_CODE:
case SQLCOM_SHOW_SLAVE_HOSTS:
case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SHOW_STATUS:
@@ -199,10 +206,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_VARIABLES:
case SQLCOM_SHOW_WARNS:
- case SQLCOM_SHOW_PROC_CODE:
- case SQLCOM_SHOW_FUNC_CODE:
case SQLCOM_REPAIR:
- case SQLCOM_BACKUP_TABLE:
case SQLCOM_RESTORE_TABLE:
flags= sp_head::MULTI_RESULTS;
break;
@@ -243,11 +247,14 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_CREATE_TRIGGER:
case SQLCOM_CREATE_USER:
case SQLCOM_ALTER_TABLE:
+ case SQLCOM_GRANT:
+ case SQLCOM_REVOKE:
case SQLCOM_BEGIN:
case SQLCOM_RENAME_TABLE:
case SQLCOM_RENAME_USER:
case SQLCOM_DROP_INDEX:
case SQLCOM_DROP_DB:
+ case SQLCOM_REVOKE_ALL:
case SQLCOM_DROP_USER:
case SQLCOM_DROP_VIEW:
case SQLCOM_DROP_TRIGGER:
@@ -263,6 +270,11 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_ALTER_FUNCTION:
case SQLCOM_DROP_PROCEDURE:
case SQLCOM_DROP_FUNCTION:
+ case SQLCOM_CREATE_EVENT:
+ case SQLCOM_ALTER_EVENT:
+ case SQLCOM_DROP_EVENT:
+ case SQLCOM_INSTALL_PLUGIN:
+ case SQLCOM_UNINSTALL_PLUGIN:
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
break;
default:
@@ -272,18 +284,16 @@ sp_get_flags_for_command(LEX *lex)
return flags;
}
-
-/*
+/**
Prepare an Item for evaluation (call of fix_fields).
- SYNOPSIS
- sp_prepare_func_item()
- thd thread handler
- it_addr pointer on item refernce
+ @param thd thread handler
+ @param it_addr pointer on item refernce
- RETURN
- NULL error
- prepared item
+ @retval
+ NULL error
+ @retval
+ non-NULL prepared item
*/
Item *
@@ -303,17 +313,16 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
}
-/*
+/**
Evaluate an expression and store the result in the field.
- SYNOPSIS
- sp_eval_expr()
- thd - current thread object
- expr_item - the root item of the expression
- result_field - the field to store the result
+ @param thd current thread object
+ @param result_field the field to store the result
+ @param expr_item_ptr the root item of the expression
- RETURN VALUES
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
@@ -324,6 +333,9 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
DBUG_ENTER("sp_eval_expr");
+ if (!*expr_item_ptr)
+ DBUG_RETURN(TRUE);
+
if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr)))
DBUG_RETURN(TRUE);
@@ -354,7 +366,7 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
thd->abort_on_warning= save_abort_on_warning;
thd->transaction.stmt.modified_non_trans_table= save_stmt_modified_non_trans_table;
- if (thd->net.report_error)
+ if (thd->is_error())
{
/* Return error status if something went wrong. */
err_status= TRUE;
@@ -393,41 +405,58 @@ sp_name::sp_name(THD *thd, char *key, uint key_len)
m_explicit_name= false;
}
+
+/**
+ Init the qualified name from the db and name.
+*/
void
sp_name::init_qname(THD *thd)
{
const uint dot= !!m_db.length;
/* m_sroutines format: m_type + [database + dot] + name + nul */
m_sroutines_key.length= 1 + m_db.length + dot + m_name.length;
- if (!(m_sroutines_key.str= thd->alloc(m_sroutines_key.length + 1)))
+ if (!(m_sroutines_key.str= (char*) thd->alloc(m_sroutines_key.length + 1)))
return;
m_qname.length= m_sroutines_key.length - 1;
m_qname.str= m_sroutines_key.str + 1;
sprintf(m_qname.str, "%.*s%.*s%.*s",
- m_db.length, (m_db.length ? m_db.str : ""),
- dot, ".", m_name.length, m_name.str);
+ (int) m_db.length, (m_db.length ? m_db.str : ""),
+ dot, ".",
+ (int) m_name.length, m_name.str);
}
-/*
- Check that the name 'ident' is ok. It's assumed to be an 'ident'
+/**
+ Check that the name 'ident' is ok. It's assumed to be an 'ident'
from the parser, so we only have to check length and trailing spaces.
The former is a standard requirement (and 'show status' assumes a
non-empty name), the latter is a mysql:ism as trailing spaces are
removed by get_field().
-
- RETURN
- TRUE - bad name
- FALSE - name is ok
+
+ @retval
+ TRUE bad name
+ @retval
+ FALSE name is ok
*/
bool
-check_routine_name(LEX_STRING ident)
+check_routine_name(LEX_STRING *ident)
{
- return (!ident.str || !ident.str[0] || ident.str[ident.length-1] == ' ');
-}
+ if (!ident || !ident->str || !ident->str[0] ||
+ ident->str[ident->length-1] == ' ')
+ {
+ my_error(ER_SP_WRONG_NAME, MYF(0), ident->str);
+ return TRUE;
+ }
+ if (check_string_char_length(ident, "", NAME_CHAR_LEN,
+ system_charset_info, 1))
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), ident->str);
+ return TRUE;
+ }
-/* ------------------------------------------------------------------ */
+ return FALSE;
+}
/*
@@ -478,14 +507,20 @@ sp_head::sp_head()
m_flags(0), m_recursion_level(0), m_next_cached_sp(0),
m_cont_level(0)
{
+ const LEX_STRING str_reset= { NULL, 0 };
+
m_first_instance= this;
m_first_free_instance= this;
m_last_cached_sp= this;
m_return_field_def.charset = NULL;
+ /*
+ FIXME: the only use case when name is NULL is events, and it should
+ be rewritten soon. Remove the else part and replace 'if' with
+ an assert when this is done.
+ */
+ m_db= m_name= m_qname= str_reset;
- extern byte *
- sp_table_key(const byte *ptr, uint *plen, my_bool first);
DBUG_ENTER("sp_head::sp_head");
m_backpatch.empty();
@@ -493,6 +528,10 @@ sp_head::sp_head()
m_lex.empty();
hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
+
+ m_body_utf8.str= NULL;
+ m_body_utf8.length= 0;
+
DBUG_VOID_RETURN;
}
@@ -572,9 +611,9 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
spname->init_qname(thd);
m_sroutines_key.length= spname->m_sroutines_key.length;
- m_sroutines_key.str= memdup_root(thd->mem_root,
- spname->m_sroutines_key.str,
- spname->m_sroutines_key.length + 1);
+ m_sroutines_key.str= (char*) memdup_root(thd->mem_root,
+ spname->m_sroutines_key.str,
+ spname->m_sroutines_key.length + 1);
m_sroutines_key.str[0]= static_cast<char>(m_type);
m_qname.length= m_sroutines_key.length - 1;
@@ -585,43 +624,63 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
void
-sp_head::init_strings(THD *thd, LEX *lex)
+sp_head::set_body_start(THD *thd, const char *begin_ptr)
+{
+ m_body_begin= begin_ptr;
+ thd->m_parser_state->m_lip.body_utf8_start(thd, begin_ptr);
+}
+
+
+void
+sp_head::set_stmt_end(THD *thd)
{
- DBUG_ENTER("sp_head::init_strings");
- const char *endp; /* Used to trim the end */
- /* During parsing, we must use thd->mem_root */
- MEM_ROOT *root= thd->mem_root;
- Lex_input_stream *lip= & thd->m_parser_state->m_lip;
+ Lex_input_stream *lip= & thd->m_parser_state->m_lip; /* shortcut */
+ const char *end_ptr= lip->get_cpp_ptr(); /* shortcut */
+
+ /* Make the string of parameters. */
if (m_param_begin && m_param_end)
{
m_params.length= m_param_end - m_param_begin;
- m_params.str= strmake_root(root,
- (char *)m_param_begin, m_params.length);
+ m_params.str= thd->strmake(m_param_begin, m_params.length);
}
- /* If ptr has overrun end_of_query then end_of_query is the end */
- endp= (lip->ptr > lip->end_of_query ? lip->end_of_query : lip->ptr);
+ /* Remember end pointer for further dumping of whole statement. */
+
+ thd->lex->stmt_definition_end= end_ptr;
+
+ /* Make the string of body (in the original character set). */
+
+ m_body.length= end_ptr - m_body_begin;
+ m_body.str= thd->strmake(m_body_begin, m_body.length);
+ trim_whitespace(thd->charset(), & m_body);
+
+ /* Make the string of UTF-body. */
+
+ lip->body_utf8_append(end_ptr);
+
+ m_body_utf8.length= lip->get_body_utf8_length();
+ m_body_utf8.str= thd->strmake(lip->get_body_utf8_str(), m_body_utf8.length);
+ trim_whitespace(thd->charset(), & m_body_utf8);
+
/*
- Trim "garbage" at the end. This is sometimes needed with the
- "/ * ! VERSION... * /" wrapper in dump files.
+ Make the string of whole stored-program-definition query (in the
+ original character set).
*/
- endp= skip_rear_comments(thd->charset(), (char*) m_body_begin, (char*) endp);
- m_body.length= endp - m_body_begin;
- m_body.str= strmake_root(root, m_body_begin, m_body.length);
- m_defstr.length= endp - lip->buf;
- m_defstr.str= strmake_root(root, lip->buf, m_defstr.length);
- DBUG_VOID_RETURN;
+ m_defstr.length= end_ptr - lip->get_cpp_buf();
+ m_defstr.str= thd->strmake(lip->get_cpp_buf(), m_defstr.length);
+ trim_whitespace(thd->charset(), & m_defstr);
}
static TYPELIB *
-create_typelib(MEM_ROOT *mem_root, create_field *field_def, List<String> *src)
+create_typelib(MEM_ROOT *mem_root, Create_field *field_def, List<String> *src)
{
TYPELIB *result= NULL;
CHARSET_INFO *cs= field_def->charset;
DBUG_ENTER("create_typelib");
+
if (src->elements)
{
result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB));
@@ -630,7 +689,7 @@ create_typelib(MEM_ROOT *mem_root, create_field *field_def, List<String> *src)
if (!(result->type_names=(const char **)
alloc_root(mem_root,(sizeof(char *)+sizeof(int))*(result->count+1))))
DBUG_RETURN(0);
- result->type_lengths= (unsigned int *)(result->type_names + result->count+1);
+ result->type_lengths= (uint*)(result->type_names + result->count+1);
List_iterator<String> it(*src);
String conv;
for (uint i=0; i < result->count; i++)
@@ -671,25 +730,20 @@ int
sp_head::create(THD *thd)
{
DBUG_ENTER("sp_head::create");
- int ret;
-
DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s",
m_type, m_name.str, m_params.str, m_body.str));
- if (m_type == TYPE_ENUM_FUNCTION)
- ret= sp_create_function(thd, this);
- else
- ret= sp_create_procedure(thd, this);
-
- DBUG_RETURN(ret);
+ DBUG_RETURN(sp_create_routine(thd, m_type, this));
}
sp_head::~sp_head()
{
+ DBUG_ENTER("sp_head::~sp_head");
destroy();
delete m_next_cached_sp;
if (m_thd)
restore_thd_mem_root(m_thd);
+ DBUG_VOID_RETURN;
}
void
@@ -728,7 +782,7 @@ sp_head::destroy()
}
-/*
+/**
This is only used for result fields from functions (both during
fix_length_and_dec() and evaluation).
*/
@@ -745,7 +799,8 @@ sp_head::create_result_field(uint field_max_length, const char *field_name,
field_length= !m_return_field_def.length ?
field_max_length : m_return_field_def.length;
- field= ::make_field((char*) 0, /* field ptr */
+ field= ::make_field(table->s, /* TABLE_SHARE ptr */
+ (uchar*) 0, /* field ptr */
field_length, /* field [max] length */
(uchar*) "", /* null ptr */
0, /* null bit */
@@ -755,8 +810,10 @@ sp_head::create_result_field(uint field_max_length, const char *field_name,
m_return_field_def.geom_type,
Field::NONE, /* unreg check */
m_return_field_def.interval,
- field_name ? field_name : (const char *) m_name.str,
- table);
+ field_name ? field_name : (const char *) m_name.str);
+
+ if (field)
+ field->init(table);
DBUG_RETURN(field);
}
@@ -770,6 +827,9 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
/*
StoredRoutinesBinlogging
+ This paragraph applies only to statement-based binlogging. Row-based
+ binlogging does not need anything special like this.
+
Top-down overview:
1. Statements
@@ -783,7 +843,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
Statements that have is_update_query(stmt) == FALSE (e.g. SELECTs) are not
written into binary log. Instead we catch function calls the statement
makes and write it into binary log separately (see #3).
-
+
2. PROCEDURE calls
CALL statements are not written into binary log. Instead
@@ -804,7 +864,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
function execution (grep for start_union_events and stop_union_events)
If the answers are No and Yes, we write the function call into the binary
- log as "SELECT spfunc(<param1value>, <param2value>, ...)".
+ log as "SELECT spfunc(<param1value>, <param2value>, ...)"
4. Miscellaneous issues.
@@ -817,7 +877,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
This set is produced by tracking user variable reads during statement
execution.
- Fo SPs, this has the following implications:
+ For SPs, this has the following implications:
1) thd->user_var_events may contain events from several SP statements and
needs to be valid after exection of these statements was finished. In
order to achieve that, we
@@ -830,32 +890,34 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
reset_dynamic(&thd->user_var_events);
calls in several different places. (TODO cosider moving this into
mysql_bin_log.write() function)
+
+ 4.2 Auto_increment storage in binlog
+
+ As we may write two statements to binlog from one single logical statement
+ (case of "SELECT func1(),func2()": it is binlogged as "SELECT func1()" and
+ then "SELECT func2()"), we need to reset auto_increment binlog variables
+ after each binlogged SELECT. Otherwise, the auto_increment value of the
+ first SELECT would be used for the second too.
*/
-/*
- Replace thd->query{_length} with a string that one can write to the binlog
- or the query cache.
-
- SYNOPSIS
- subst_spvars()
- thd Current thread.
- instr Instruction (we look for Item_splocal instances in
- instr->free_list)
- query_str Original query string
-
- DESCRIPTION
+/**
+ Replace thd->query{_length} with a string that one can write to
+ the binlog.
The binlog-suitable string is produced by replacing references to SP local
- variables with NAME_CONST('sp_var_name', value) calls. To make this string
- suitable for the query cache this function allocates some additional space
- for the query cache flags.
-
- RETURN
- FALSE on success
- thd->query{_length} either has been appropriately replaced or there
- is no need for replacements.
- TRUE out of memory error.
+ variables with NAME_CONST('sp_var_name', value) calls.
+
+ @param thd Current thread.
+ @param instr Instruction (we look for Item_splocal instances in
+ instr->free_list)
+ @param query_str Original query string
+
+ @return
+ - FALSE on success.
+ thd->query{_length} either has been appropriately replaced or there
+ is no need for replacements.
+ - TRUE out of memory error.
*/
static bool
@@ -915,7 +977,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
break;
val= (*splocal)->this_item();
- DBUG_PRINT("info", ("print %p", val));
+ DBUG_PRINT("info", ("print 0x%lx", (long) val));
str_value= sp_get_item_value(thd, val, &str_value_holder);
if (str_value)
res|= qbuf.append(*str_value);
@@ -934,7 +996,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
query_cache_send_result_to_client function.
*/
buf_len= qbuf.length() + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE + 1;
- if ((pbuf= alloc_root(thd->mem_root, buf_len)))
+ if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len)))
{
memcpy(pbuf, qbuf.ptr(), qbuf.length());
pbuf[qbuf.length()]= 0;
@@ -974,23 +1036,27 @@ void sp_head::recursion_level_error(THD *thd)
}
-/*
- Execute the routine. The main instruction jump loop is there
+/**
+ Execute the routine. The main instruction jump loop is there.
Assume the parameters already set.
-
- RETURN
+ @todo
+ - Will write this SP statement into binlog separately
+ (TODO: consider changing the condition to "not inside event union")
+
+ @retval
FALSE on success
+ @retval
TRUE on error
-
*/
bool
sp_head::execute(THD *thd)
{
DBUG_ENTER("sp_head::execute");
- char old_db_buf[NAME_LEN+1];
- LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
- bool dbchanged;
+ char saved_cur_db_name_buf[NAME_LEN+1];
+ LEX_STRING saved_cur_db_name=
+ { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+ bool cur_db_changed= FALSE;
sp_rcontext *ctx;
bool err_status= FALSE;
uint ip= 0;
@@ -1006,9 +1072,12 @@ sp_head::execute(THD *thd)
LEX *old_lex;
Item_change_list old_change_list;
String old_packet;
+ Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
+
+ Object_creation_ctx *saved_creation_ctx;
/* Use some extra margin for possible SP recursion and functions */
- if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char*)&old_packet))
+ if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (uchar*)&old_packet))
DBUG_RETURN(TRUE);
/* init per-instruction memroot */
@@ -1043,15 +1112,24 @@ sp_head::execute(THD *thd)
*/
if (m_db.length &&
- (err_status= sp_use_new_db(thd, m_db, &old_db, 0, &dbchanged)))
+ (err_status= mysql_opt_change_db(thd, &m_db, &saved_cur_db_name, FALSE,
+ &cur_db_changed)))
+ {
goto done;
+ }
if ((ctx= thd->spcont))
ctx->clear_handler();
- thd->query_error= 0;
+ thd->is_slave_error= 0;
old_arena= thd->stmt_arena;
/*
+ Switch query context. This has to be done early as this is sometimes
+ allocated trough sql_alloc
+ */
+ saved_creation_ctx= m_creation_ctx->set_n_backup(thd);
+
+ /*
We have to save/restore this info when we are changing call level to
be able properly do close_thread_tables() in instructions.
*/
@@ -1062,6 +1140,25 @@ sp_head::execute(THD *thd)
thd->variables.sql_mode= m_sql_mode;
save_abort_on_warning= thd->abort_on_warning;
thd->abort_on_warning= 0;
+ /**
+ When inside a substatement (a stored function or trigger
+ statement), clear the metadata observer in THD, if any.
+ Remember the value of the observer here, to be able
+ to restore it when leaving the substatement.
+
+ We reset the observer to suppress errors when a substatement
+ uses temporary tables. If a temporary table does not exist
+ at start of the main statement, it's not prelocked
+ and thus is not validated with other prelocked tables.
+
+ Later on, when the temporary table is opened, metadata
+ versions mismatch, expectedly.
+
+ The proper solution for the problem is to re-validate tables
+ of substatements (Bug#12257, Bug#27011, Bug#32868, Bug#33000),
+ but it's not implemented yet.
+ */
+ thd->m_reprepare_observer= 0;
/*
It is also more efficient to save/restore current thd->lex once when
@@ -1096,15 +1193,36 @@ sp_head::execute(THD *thd)
*/
thd->spcont->callers_arena= &backup_arena;
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ /* Discard the initial part of executing routines. */
+ thd->profiling.discard_current_query();
+#endif
do
{
sp_instr *i;
uint hip; // Handler ip
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ /*
+ Treat each "instr" of a routine as discrete unit that could be profiled.
+ Profiling only records information for segments of code that set the
+ source of the query, and almost all kinds of instructions in s-p do not.
+ */
+ thd->profiling.finish_current_query();
+ thd->profiling.start_new_query("continuing inside routine");
+#endif
+
i = get_instr(ip); // Returns NULL when we're done.
if (i == NULL)
+ {
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.discard_current_query();
+#endif
break;
+ }
+
DBUG_PRINT("execute", ("Instruction %u", ip));
+
/* Don't change NOW() in FUNCTION or TRIGGER */
if (!thd->in_sub_stmt)
thd->set_time(); // Make current_time() et al work
@@ -1126,12 +1244,6 @@ sp_head::execute(THD *thd)
err_status= i->execute(thd, &ip);
- /*
- If this SP instruction have sent eof, it has caused no_send_error to be
- set. Clear it back to allow the next instruction to send error. (multi-
- statement execution code clears no_send_error between statements too)
- */
- thd->net.no_send_error= 0;
if (i->free_list)
cleanup_items(i->free_list);
@@ -1174,6 +1286,7 @@ sp_head::execute(THD *thd)
ctx->clear_handler();
ctx->enter_handler(hip);
thd->clear_error();
+ thd->is_fatal_error= 0;
thd->killed= THD::NOT_KILLED;
thd->mysys_var->abort= 0;
continue;
@@ -1181,6 +1294,17 @@ sp_head::execute(THD *thd)
}
} while (!err_status && !thd->killed);
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.finish_current_query();
+ thd->profiling.start_new_query("tail end of routine");
+#endif
+
+ /* Restore query context. */
+
+ m_creation_ctx->restore_env(thd, saved_creation_ctx);
+
+ /* Restore arena. */
+
thd->restore_active_arena(&execute_arena, &backup_arena);
thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error
@@ -1197,13 +1321,15 @@ sp_head::execute(THD *thd)
thd->derived_tables= old_derived_tables;
thd->variables.sql_mode= save_sql_mode;
thd->abort_on_warning= save_abort_on_warning;
+ thd->m_reprepare_observer= save_reprepare_observer;
thd->stmt_arena= old_arena;
state= EXECUTED;
done:
- DBUG_PRINT("info", ("err_status: %d killed: %d query_error: %d",
- err_status, thd->killed, thd->query_error));
+ DBUG_PRINT("info", ("err_status: %d killed: %d is_slave_error: %d report_error: %d",
+ err_status, thd->killed, thd->is_slave_error,
+ thd->is_error()));
if (thd->killed)
err_status= TRUE;
@@ -1211,14 +1337,14 @@ 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 (dbchanged)
+ if (cur_db_changed && !thd->killed)
{
/*
- No access check when changing back to where we came from.
- (It would generate an error from mysql_change_db() when old_db=="")
+ Force switching back to the saved current database, because it may be
+ NULL. In this case, mysql_change_db() would generate an error.
*/
- if (! thd->killed)
- err_status|= mysql_change_db(thd, &old_db, TRUE);
+
+ err_status|= mysql_change_db(thd, &saved_cur_db_name, TRUE);
}
m_flags&= ~IS_INVOKED;
DBUG_PRINT("info",
@@ -1251,22 +1377,26 @@ sp_head::execute(THD *thd)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
-/*
+/**
set_routine_security_ctx() changes routine security context, and
checks if there is an EXECUTE privilege in new context. If there is
no EXECUTE privilege, it changes the context back and returns a
error.
- SYNOPSIS
- set_routine_security_ctx()
- thd thread handle
- sp stored routine to change the context for
- is_proc TRUE is procedure, FALSE if function
- save_ctx pointer to an old security context
-
- RETURN
- TRUE if there was a error, and the context wasn't changed.
- FALSE if the context was changed.
+ @param thd thread handle
+ @param sp stored routine to change the context for
+ @param is_proc TRUE is procedure, FALSE if function
+ @param save_ctx pointer to an old security context
+
+ @todo
+ - Cache if the definer has the right to use the object on the
+ first usage and only reset the cache if someone does a GRANT
+ statement that 'may' affect this.
+
+ @retval
+ TRUE if there was a error, and the context wasn't changed.
+ @retval
+ FALSE if the context was changed.
*/
bool
@@ -1274,7 +1404,11 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
Security_context **save_ctx)
{
*save_ctx= 0;
- if (sp_change_security_context(thd, sp, save_ctx))
+ if (sp->m_chistics->suid != SP_IS_NOT_SUID &&
+ sp->m_security_ctx.change_security_context(thd, &sp->m_definer_user,
+ &sp->m_definer_host,
+ &sp->m_db,
+ save_ctx))
return TRUE;
/*
@@ -1291,7 +1425,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
check_routine_access(thd, EXECUTE_ACL,
sp->m_db.str, sp->m_name.str, is_proc, FALSE))
{
- sp_restore_security_context(thd, *save_ctx);
+ sp->m_security_ctx.restore_security_context(thd, *save_ctx);
*save_ctx= 0;
return TRUE;
}
@@ -1301,30 +1435,36 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
#endif // ! NO_EMBEDDED_ACCESS_CHECKS
-/*
- Execute a trigger:
- - changes security context for triggers
- - switch to new memroot
- - call sp_head::execute
- - restore old memroot
- - restores security context
+/**
+ Execute trigger stored program.
+
+ - changes security context for triggers
+ - switch to new memroot
+ - call sp_head::execute
+ - restore old memroot
+ - restores security context
+
+ @param thd Thread handle
+ @param db database name
+ @param table table name
+ @param grant_info GRANT_INFO structure to be filled with
+ information about definer's privileges
+ on subject table
+
+ @todo
+ - TODO: we should create sp_rcontext once per command and reuse it
+ on subsequent executions of a trigger.
- SYNOPSIS
- sp_head::execute_trigger()
- thd Thread handle
- db database name
- table table name
- grant_info GRANT_INFO structure to be filled with
- information about definer's privileges
- on subject table
-
- RETURN
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
bool
-sp_head::execute_trigger(THD *thd, const char *db, const char *table,
+sp_head::execute_trigger(THD *thd,
+ const LEX_STRING *db_name,
+ const LEX_STRING *table_name,
GRANT_INFO *grant_info)
{
sp_rcontext *octx = thd->spcont;
@@ -1338,26 +1478,43 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
DBUG_PRINT("info", ("trigger %s", m_name.str));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- Security_context *save_ctx;
- if (sp_change_security_context(thd, this, &save_ctx))
+ Security_context *save_ctx= NULL;
+
+
+ if (m_chistics->suid != SP_IS_NOT_SUID &&
+ m_security_ctx.change_security_context(thd,
+ &m_definer_user,
+ &m_definer_host,
+ &m_db,
+ &save_ctx))
DBUG_RETURN(TRUE);
/*
- NOTE: TRIGGER_ACL should be used here.
+ Fetch information about table-level privileges for subject table into
+ GRANT_INFO instance. The access check itself will happen in
+ Item_trigger_field, where this information will be used along with
+ information about column-level privileges.
*/
- if (check_global_access(thd, SUPER_ACL))
+
+ fill_effective_table_privileges(thd,
+ grant_info,
+ db_name->str,
+ table_name->str);
+
+ /* Check that the definer has TRIGGER privilege on the subject table. */
+
+ if (!(grant_info->privilege & TRIGGER_ACL))
{
- sp_restore_security_context(thd, save_ctx);
+ char priv_desc[128];
+ get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL);
+
+ my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc,
+ thd->security_ctx->priv_user, thd->security_ctx->host_or_ip,
+ table_name->str);
+
+ m_security_ctx.restore_security_context(thd, save_ctx);
DBUG_RETURN(TRUE);
}
-
- /*
- Fetch information about table-level privileges to GRANT_INFO
- structure for subject table. Check of privileges that will use it
- and information about column-level privileges will happen in
- Item_trigger_field::fix_fields().
- */
- fill_effective_table_privileges(thd, grant_info, db, table);
#endif // NO_EMBEDDED_ACCESS_CHECKS
/*
@@ -1392,9 +1549,11 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
err_with_cleanup:
thd->restore_active_arena(&call_arena, &backup_arena);
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- sp_restore_security_context(thd, save_ctx);
+ m_security_ctx.restore_security_context(thd, save_ctx);
#endif // NO_EMBEDDED_ACCESS_CHECKS
+
delete nctx;
call_arena.free_items();
free_root(&call_mem_root, MYF(0));
@@ -1407,8 +1566,9 @@ err_with_cleanup:
}
-/*
- Execute a function:
+/**
+ Execute a function.
+
- evaluate parameters
- changes security context for SUID routines
- switch to new memroot
@@ -1417,17 +1577,25 @@ err_with_cleanup:
- evaluate the return value
- restores security context
- SYNOPSIS
- sp_head::execute_function()
- thd Thread handle
- argp Passed arguments (these are items from containing
- statement?)
- argcount Number of passed arguments. We need to check if this is
- correct.
- return_value_fld Save result here.
-
- RETURN
+ @param thd Thread handle
+ @param argp Passed arguments (these are items from containing
+ statement?)
+ @param argcount Number of passed arguments. We need to check if
+ this is correct.
+ @param return_value_fld Save result here.
+
+ @todo
+ We should create sp_rcontext once per command and reuse
+ it on subsequent executions of a function/trigger.
+
+ @todo
+ In future we should associate call arena/mem_root with
+ sp_rcontext and allocate all these objects (and sp_rcontext
+ itself) on it directly rather than juggle with arenas.
+
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
@@ -1446,10 +1614,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
MEM_ROOT call_mem_root;
Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP);
Query_arena backup_arena;
-
DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("info", ("function %s", m_name.str));
+ LINT_INIT(binlog_save_options);
+
/*
Check that the function is called with all specified arguments.
@@ -1510,7 +1679,12 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
goto err_with_cleanup;
}
- need_binlog_call= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG);
+ /*
+ If row-based binlogging, we don't need to binlog the function's call, let
+ each substatement be binlogged its way.
+ */
+ need_binlog_call= mysql_bin_log.is_open() &&
+ (thd->options & OPTION_BIN_LOG) && !thd->current_stmt_binlog_row_based;
/*
Remember the original arguments for unrolled replication of functions
@@ -1553,7 +1727,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
#endif
- binlog_save_options= thd->options;
if (need_binlog_call)
{
query_id_t q;
@@ -1574,6 +1747,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
q= global_query_id;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
mysql_bin_log.start_union_events(thd, q + 1);
+ binlog_save_options= thd->options;
+ thd->options&= ~OPTION_BIN_LOG;
}
/*
@@ -1586,27 +1761,30 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
*/
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- thd->options&= ~OPTION_BIN_LOG;
err_status= execute(thd);
- thd->options= binlog_save_options;
thd->restore_active_arena(&call_arena, &backup_arena);
if (need_binlog_call)
- mysql_bin_log.stop_union_events(thd);
-
- if (need_binlog_call && thd->binlog_evt_union.unioned_events)
{
- Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(),
- thd->binlog_evt_union.unioned_events_trans, FALSE);
- if (mysql_bin_log.write(&qinfo) &&
- thd->binlog_evt_union.unioned_events_trans)
+ mysql_bin_log.stop_union_events(thd);
+ thd->options= binlog_save_options;
+ if (thd->binlog_evt_union.unioned_events)
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
- "Invoked ROUTINE modified a transactional table but MySQL "
- "failed to reflect this change in the binary log");
+ Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(),
+ thd->binlog_evt_union.unioned_events_trans, FALSE);
+ if (mysql_bin_log.write(&qinfo) &&
+ thd->binlog_evt_union.unioned_events_trans)
+ {
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "Invoked ROUTINE modified a transactional table but MySQL "
+ "failed to reflect this change in the binary log");
+ }
+ reset_dynamic(&thd->user_var_events);
+ /* Forget those values, in case more function calls are binlogged: */
+ thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
}
- reset_dynamic(&thd->user_var_events);
}
if (!err_status)
@@ -1621,7 +1799,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- sp_restore_security_context(thd, save_security_ctx);
+ m_security_ctx.restore_security_context(thd, save_security_ctx);
#endif
err_with_cleanup:
@@ -1634,14 +1812,8 @@ err_with_cleanup:
}
-/*
+/**
Execute a procedure.
- SYNOPSIS
- sp_head::execute_procedure()
- thd Thread handle
- args List of values passed as arguments.
-
- DESCRIPTION
The function does the following steps:
- Set all parameters
@@ -1650,8 +1822,12 @@ err_with_cleanup:
- copy back values of INOUT and OUT parameters
- restores security context
- RETURN
+ @param thd Thread handle
+ @param args List of values passed as arguments.
+
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
@@ -1662,6 +1838,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
uint params = m_pcont->context_var_count();
sp_rcontext *save_spcont, *octx;
sp_rcontext *nctx = NULL;
+ bool save_enable_slow_log= false;
+ bool save_log_general= false;
DBUG_ENTER("sp_head::execute_procedure");
DBUG_PRINT("info", ("procedure %s", m_name.str));
@@ -1706,7 +1884,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
{
List_iterator<Item> it_args(*args);
- DBUG_PRINT("info",(" %.*s: eval args", m_name.length, m_name.str));
+ DBUG_PRINT("info",(" %.*s: eval args", (int) m_name.length, m_name.str));
for (uint i= 0 ; i < params ; i++)
{
@@ -1762,11 +1940,24 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
we'll leave it here.
*/
if (!thd->in_sub_stmt)
- close_thread_tables(thd, 0, 0);
+ close_thread_tables(thd);
- DBUG_PRINT("info",(" %.*s: eval args done", m_name.length, m_name.str));
+ DBUG_PRINT("info",(" %.*s: eval args done",
+ (int) m_name.length, m_name.str));
+ }
+ if (!(m_flags & LOG_SLOW_STATEMENTS) && thd->enable_slow_log)
+ {
+ DBUG_PRINT("info", ("Disabling slow log for the execution"));
+ save_enable_slow_log= true;
+ thd->enable_slow_log= FALSE;
+ }
+ if (!(m_flags & LOG_GENERAL_LOG) && !(thd->options & OPTION_LOG_OFF))
+ {
+ DBUG_PRINT("info", ("Disabling general log for the execution"));
+ save_log_general= true;
+ /* disable this bit */
+ thd->options |= OPTION_LOG_OFF;
}
-
thd->spcont= nctx;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -1778,6 +1969,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!err_status)
err_status= execute(thd);
+ if (save_log_general)
+ thd->options &= ~OPTION_LOG_OFF;
+ if (save_enable_slow_log)
+ thd->enable_slow_log= true;
/*
In the case when we weren't able to employ reuse mechanism for
OUT/INOUT paranmeters, we should reallocate memory. This
@@ -1821,7 +2016,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (save_security_ctx)
- sp_restore_security_context(thd, save_security_ctx);
+ m_security_ctx.restore_security_context(thd, save_security_ctx);
#endif
if (!save_spcont)
@@ -1835,7 +2030,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
/**
- @brief Reset lex during parsing, before we parse a sub statement.
+ Reset lex during parsing, before we parse a sub statement.
@param thd Thread handler.
@@ -1869,8 +2064,6 @@ sp_head::reset_lex(THD *thd)
sublex->trg_table_fields.empty();
sublex->sp_lex_in_use= FALSE;
- sublex->in_comment= oldlex->in_comment;
-
/* Reset type info. */
sublex->charset= NULL;
@@ -1882,7 +2075,7 @@ sp_head::reset_lex(THD *thd)
DBUG_RETURN(FALSE);
}
-// Restore lex during parsing, after we have parsed a sub statement.
+/// Restore lex during parsing, after we have parsed a sub statement.
void
sp_head::restore_lex(THD *thd)
{
@@ -1899,6 +2092,14 @@ sp_head::restore_lex(THD *thd)
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
/*
+ If this substatement needs row-based, the entire routine does too (we
+ cannot switch from statement-based to row-based only for this
+ substatement).
+ */
+ if (sublex->is_stmt_unsafe())
+ m_flags|= BINLOG_ROW_BASED_IF_MIXED;
+
+ /*
Add routines which are used by statement to respective set for
this routine.
*/
@@ -1917,6 +2118,9 @@ sp_head::restore_lex(THD *thd)
DBUG_VOID_RETURN;
}
+/**
+ Put the instruction on the backpatch list, associated with the label.
+*/
void
sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
{
@@ -1930,6 +2134,10 @@ sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
}
}
+/**
+ Update all instruction with this label in the backpatch list to
+ the current position.
+*/
void
sp_head::backpatch(sp_label_t *lab)
{
@@ -1950,39 +2158,35 @@ sp_head::backpatch(sp_label_t *lab)
DBUG_VOID_RETURN;
}
-/*
- Prepare an instance of create_field for field creation (fill all necessary
+/**
+ Prepare an instance of Create_field for field creation (fill all necessary
attributes).
- SYNOPSIS
- sp_head::fill_field_definition()
- thd [IN] Thread handle
- lex [IN] Yacc parsing context
- field_type [IN] Field type
- field_def [OUT] An instance of create_field to be filled
+ @param[in] thd Thread handle
+ @param[in] lex Yacc parsing context
+ @param[in] field_type Field type
+ @param[out] field_def An instance of create_field to be filled
- RETURN
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
bool
sp_head::fill_field_definition(THD *thd, LEX *lex,
enum enum_field_types field_type,
- create_field *field_def)
+ Create_field *field_def)
{
- HA_CREATE_INFO sp_db_info;
LEX_STRING cmt = { 0, 0 };
uint unused1= 0;
int unused2= 0;
- load_db_opt_by_name(thd, m_db.str, &sp_db_info);
-
if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec,
lex->type, (Item*) 0, (Item*) 0, &cmt, 0,
&lex->interval_list,
- (lex->charset ? lex->charset :
- sp_db_info.default_table_charset),
+ lex->charset ? lex->charset :
+ thd->variables.collation_database,
lex->uint_geom_type))
return TRUE;
@@ -2057,10 +2261,10 @@ void
sp_head::set_definer(const char *definer, uint definerlen)
{
char user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_LENGTH);
+ LEX_STRING user_name= { user_name_holder, USERNAME_LENGTH };
char host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT host_name(host_name_holder, HOSTNAME_LENGTH);
+ LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH };
parse_user(definer, definerlen, user_name.str, &user_name.length,
host_name.str, &host_name.length);
@@ -2111,18 +2315,17 @@ sp_head::restore_thd_mem_root(THD *thd)
}
-/*
- Check if a user has access right to a routine
-
- SYNOPSIS
- check_show_routine_access()
- thd Thread handler
- sp SP
- full_access Set to 1 if the user has SELECT right to the
- 'mysql.proc' able or is the owner of the routine
- RETURN
- 0 ok
- 1 error
+/**
+ Check if a user has access right to a routine.
+
+ @param thd Thread handler
+ @param sp SP
+ @param full_access Set to 1 if the user has SELECT right to the
+ 'mysql.proc' able or is the owner of the routine
+ @retval
+ false ok
+ @retval
+ true error
*/
bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
@@ -2131,7 +2334,7 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
bzero((char*) &tables,sizeof(tables));
tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*) "proc";
- *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1) ||
+ *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1, TRUE) ||
(!strcmp(sp->m_definer_user.str,
thd->security_ctx->priv_user) &&
!strcmp(sp->m_definer_host.str,
@@ -2143,61 +2346,116 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
}
-int
-sp_head::show_create_procedure(THD *thd)
+/**
+ Implement SHOW CREATE statement for stored routines.
+
+ @param thd Thread context.
+ @param type Stored routine type
+ (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+
+ @return Error status.
+ @retval FALSE on success
+ @retval TRUE on error
+*/
+
+bool
+sp_head::show_create_routine(THD *thd, int type)
{
+ const char *col1_caption= type == TYPE_ENUM_PROCEDURE ?
+ "Procedure" : "Function";
+
+ const char *col3_caption= type == TYPE_ENUM_PROCEDURE ?
+ "Create Procedure" : "Create Function";
+
+ bool err_status;
+
Protocol *protocol= thd->protocol;
- char buff[2048];
- String buffer(buff, sizeof(buff), system_charset_info);
- int res;
- List<Item> field_list;
- byte *sql_mode_str;
- ulong sql_mode_len;
+ List<Item> fields;
+
+ LEX_STRING sql_mode;
+
bool full_access;
- DBUG_ENTER("sp_head::show_create_procedure");
- DBUG_PRINT("info", ("procedure %s", m_name.str));
- LINT_INIT(sql_mode_str);
- LINT_INIT(sql_mode_len);
+ DBUG_ENTER("sp_head::show_create_routine");
+ DBUG_PRINT("info", ("routine %s", m_name.str));
+
+ DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
+ type == TYPE_ENUM_FUNCTION);
if (check_show_routine_access(thd, this, &full_access))
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd,
- m_sql_mode,
- &sql_mode_len);
- field_list.push_back(new Item_empty_string("Procedure", NAME_LEN));
- field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
- // 1024 is for not to confuse old clients
- Item_empty_string *definition=
- new Item_empty_string("Create Procedure", max(buffer.length(),1024));
- definition->maybe_null= TRUE;
- field_list.push_back(definition);
+ sys_var_thd_sql_mode::symbolic_mode_representation(
+ thd, m_sql_mode, &sql_mode);
+
+ /* Send header. */
+
+ fields.push_back(new Item_empty_string(col1_caption, NAME_CHAR_LEN));
+ fields.push_back(new Item_empty_string("sql_mode", sql_mode.length));
+
+ {
+ /*
+ NOTE: SQL statement field must be not less than 1024 in order not to
+ confuse old clients.
+ */
+
+ Item_empty_string *stmt_fld=
+ new Item_empty_string(col3_caption,
+ max(m_defstr.length, 1024));
+
+ stmt_fld->maybe_null= TRUE;
+
+ fields.push_back(stmt_fld);
+ }
+
+ fields.push_back(new Item_empty_string("character_set_client",
+ MY_CS_NAME_SIZE));
+
+ fields.push_back(new Item_empty_string("collation_connection",
+ MY_CS_NAME_SIZE));
+
+ fields.push_back(new Item_empty_string("Database Collation",
+ MY_CS_NAME_SIZE));
+
+ if (protocol->send_fields(&fields,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ {
+ DBUG_RETURN(TRUE);
+ }
+
+ /* Send data. */
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF))
- DBUG_RETURN(1);
protocol->prepare_for_resend();
+
protocol->store(m_name.str, m_name.length, system_charset_info);
- protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+ protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
+
if (full_access)
- protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
+ protocol->store(m_defstr.str, m_defstr.length,
+ m_creation_ctx->get_client_cs());
else
protocol->store_null();
- res= protocol->write();
- send_eof(thd);
- DBUG_RETURN(res);
+
+ protocol->store(m_creation_ctx->get_client_cs()->csname, system_charset_info);
+ protocol->store(m_creation_ctx->get_connection_cl()->name, system_charset_info);
+ protocol->store(m_creation_ctx->get_db_cl()->name, system_charset_info);
+
+ err_status= protocol->write();
+
+ if (!err_status)
+ my_eof(thd);
+
+ DBUG_RETURN(err_status);
}
-/*
- Add instruction to SP
- SYNOPSIS
- sp_head::add_instr()
- instr Instruction
+
+/**
+ Add instruction to SP.
+
+ @param instr Instruction
*/
void sp_head::add_instr(sp_instr *instr)
@@ -2211,71 +2469,24 @@ void sp_head::add_instr(sp_instr *instr)
entire stored procedure, as their life span is equal.
*/
instr->mem_root= &main_mem_root;
- insert_dynamic(&m_instr, (gptr)&instr);
-}
-
-
-int
-sp_head::show_create_function(THD *thd)
-{
- Protocol *protocol= thd->protocol;
- char buff[2048];
- String buffer(buff, sizeof(buff), system_charset_info);
- int res;
- List<Item> field_list;
- byte *sql_mode_str;
- ulong sql_mode_len;
- bool full_access;
- DBUG_ENTER("sp_head::show_create_function");
- DBUG_PRINT("info", ("procedure %s", m_name.str));
- LINT_INIT(sql_mode_str);
- LINT_INIT(sql_mode_len);
-
- if (check_show_routine_access(thd, this, &full_access))
- DBUG_RETURN(1);
-
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd,
- m_sql_mode,
- &sql_mode_len);
- field_list.push_back(new Item_empty_string("Function",NAME_LEN));
- field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
- Item_empty_string *definition=
- new Item_empty_string("Create Function", max(buffer.length(),1024));
- definition->maybe_null= TRUE;
- field_list.push_back(definition);
-
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(1);
- protocol->prepare_for_resend();
- protocol->store(m_name.str, m_name.length, system_charset_info);
- protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
- if (full_access)
- protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
- else
- protocol->store_null();
- res= protocol->write();
- send_eof(thd);
-
- DBUG_RETURN(res);
+ insert_dynamic(&m_instr, (uchar*)&instr);
}
-/*
+/**
Do some minimal optimization of the code:
- 1) Mark used instructions
- 1.1) While doing this, shortcut jumps to jump instructions
- 2) Compact the code, removing unused instructions
+ -# Mark used instructions
+ -# While doing this, shortcut jumps to jump instructions
+ -# Compact the code, removing unused instructions.
This is the main mark and move loop; it relies on the following methods
in sp_instr and its subclasses:
- opt_mark() Mark instruction as reachable
- opt_shortcut_jump() Shortcut jumps to the final destination;
- used by opt_mark().
- opt_move() Update moved instruction
- set_destination() Set the new destination (jump instructions only)
+ - opt_mark() : Mark instruction as reachable
+ - opt_shortcut_jump(): Shortcut jumps to the final destination;
+ used by opt_mark().
+ - opt_move() : Update moved instruction
+ - set_destination() : Set the new destination (jump instructions only)
*/
void sp_head::optimize()
@@ -2302,7 +2513,7 @@ void sp_head::optimize()
sp_instr *ibp;
List_iterator_fast<sp_instr> li(bp);
- set_dynamic(&m_instr, (gptr)&i, dst);
+ set_dynamic(&m_instr, (uchar*)&i, dst);
while ((ibp= li++))
{
sp_instr_opt_meta *im= static_cast<sp_instr_opt_meta *>(ibp);
@@ -2366,9 +2577,10 @@ sp_head::opt_mark()
#ifndef DBUG_OFF
-/*
+/**
Return the routine instructions as a result set.
- Returns 0 if ok, !=0 on error.
+ @return
+ 0 if ok, !=0 on error.
*/
int
sp_head::show_routine_code(THD *thd)
@@ -2422,34 +2634,34 @@ sp_head::show_routine_code(THD *thd)
if ((res= protocol->write()))
break;
}
- send_eof(thd);
+
+ if (!res)
+ my_eof(thd);
DBUG_RETURN(res);
}
#endif // ifndef DBUG_OFF
-/*
+/**
Prepare LEX and thread for execution of instruction, if requested open
and lock LEX's tables, execute instruction's core function, perform
cleanup afterwards.
- SYNOPSIS
- reset_lex_and_exec_core()
- thd - thread context
- nextp - out - next instruction
- open_tables - if TRUE then check read access to tables in LEX's table
- list and open and lock them (used in instructions which
- need to calculate some expression and don't execute
- complete statement).
- sp_instr - instruction for which we prepare context, and which core
- function execute by calling its exec_core() method.
+ @param thd thread context
+ @param nextp out - next instruction
+ @param open_tables if TRUE then check read access to tables in LEX's table
+ list and open and lock them (used in instructions which
+ need to calculate some expression and don't execute
+ complete statement).
+ @param sp_instr instruction for which we prepare context, and which core
+ function execute by calling its exec_core() method.
- NOTE
+ @note
We are not saving/restoring some parts of THD which may need this because
we do this once for whole routine execution in sp_head::execute().
- RETURN VALUE
+ @return
0/non-0 - Success/Failure
*/
@@ -2458,6 +2670,8 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
bool open_tables, sp_instr* instr)
{
int res= 0;
+ DBUG_ENTER("reset_lex_and_exec_core");
+
/*
The flag is saved at the entry to the following substatement.
It's reset further in the common code part.
@@ -2503,13 +2717,17 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
if (!res)
+ {
res= instr->exec_core(thd, nextp);
+ DBUG_PRINT("info",("exec_core returned: %d", res));
+ }
m_lex->unit.cleanup();
- thd->proc_info="closing tables";
+ thd_proc_info(thd, "closing tables");
+ /* Here we also commit or rollback the current statement. */
close_thread_tables(thd);
- thd->proc_info= 0;
+ thd_proc_info(thd, 0);
if (m_lex->query_tables_own_last)
{
@@ -2545,7 +2763,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
cleanup_items() is called in sp_head::execute()
*/
- return res || thd->net.report_error;
+ DBUG_RETURN(res || thd->is_error());
}
@@ -2561,7 +2779,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
Check whenever we have access to tables for this statement
and open and lock them before executing instructions core function.
*/
- if (check_table_access(thd, SELECT_ACL, tables, 0)
+ if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)
|| open_and_lock_tables(thd, tables))
result= -1;
else
@@ -2597,23 +2815,40 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
query= thd->query;
query_length= thd->query_length;
- if (!(res= alloc_query(thd, m_query.str, m_query.length+1)) &&
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ /* This s-p instr is profilable and will be captured. */
+ thd->profiling.set_query_source(m_query.str, m_query.length);
+#endif
+ if (!(res= alloc_query(thd, m_query.str, m_query.length)) &&
!(res=subst_spvars(thd, this, &m_query)))
{
/*
(the order of query cache and subst_spvars calls is irrelevant because
queries with SP vars can't be cached)
*/
+ if (unlikely((thd->options & OPTION_LOG_OFF)==0))
+ general_log_write(thd, COM_QUERY, thd->query, thd->query_length);
+
if (query_cache_send_result_to_client(thd,
thd->query, thd->query_length) <= 0)
{
res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
+
+ if (thd->main_da.is_eof())
+ net_end_statement(thd);
+
query_cache_end_of_result(thd);
+
+ if (!res && unlikely(thd->enable_slow_log))
+ log_slow_statement(thd);
}
else
*nextp= m_ip+1;
thd->query= query;
thd->query_length= query_length;
+
+ if (!thd->is_error())
+ thd->main_da.reset_diagnostics_area();
}
DBUG_RETURN(res);
}
@@ -2723,7 +2958,7 @@ sp_instr_set::print(String *str)
}
str->qs_append(m_offset);
str->qs_append(' ');
- m_value->print(str);
+ m_value->print(str, QT_ORDINARY);
}
@@ -2751,9 +2986,9 @@ void
sp_instr_set_trigger_field::print(String *str)
{
str->append(STRING_WITH_LEN("set_trigger_field "));
- trigger_field->print(str);
+ trigger_field->print(str, QT_ORDINARY);
str->append(STRING_WITH_LEN(":="));
- value->print(str);
+ value->print(str, QT_ORDINARY);
}
/*
@@ -2879,7 +3114,7 @@ sp_instr_jump_if_not::print(String *str)
str->qs_append('(');
str->qs_append(m_cont_dest);
str->qs_append(STRING_WITH_LEN(") "));
- m_expr->print(str);
+ m_expr->print(str, QT_ORDINARY);
}
@@ -2967,7 +3202,7 @@ sp_instr_freturn::print(String *str)
str->qs_append(STRING_WITH_LEN("freturn "));
str->qs_append((uint)m_type);
str->qs_append(' ');
- m_value->print(str);
+ m_value->print(str, QT_ORDINARY);
}
/*
@@ -3193,6 +3428,11 @@ sp_instr_cpop::print(String *str)
sp_instr_copen class functions
*/
+/**
+ @todo
+ Assert that we either have an error or a cursor
+*/
+
int
sp_instr_copen::execute(THD *thd, uint *nextp)
{
@@ -3450,7 +3690,7 @@ sp_instr_set_case_expr::print(String *str)
str->qs_append(STRING_WITH_LEN(") "));
str->qs_append(m_case_expr_id);
str->qs_append(' ');
- m_case_expr->print(str);
+ m_case_expr->print(str, QT_ORDINARY);
}
uint
@@ -3481,44 +3721,6 @@ sp_instr_set_case_expr::opt_move(uint dst, List<sp_instr> *bp)
/* ------------------------------------------------------------------ */
-/*
- Security context swapping
-*/
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-bool
-sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup)
-{
- *backup= 0;
- if (sp->m_chistics->suid != SP_IS_NOT_SUID &&
- (strcmp(sp->m_definer_user.str,
- thd->security_ctx->priv_user) ||
- my_strcasecmp(system_charset_info, sp->m_definer_host.str,
- thd->security_ctx->priv_host)))
- {
- if (acl_getroot_no_password(&sp->m_security_ctx, sp->m_definer_user.str,
- sp->m_definer_host.str,
- sp->m_definer_host.str,
- sp->m_db.str))
- {
- my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str,
- sp->m_definer_host.str);
- return TRUE;
- }
- *backup= thd->security_ctx;
- thd->security_ctx= &sp->m_security_ctx;
- }
- return FALSE;
-}
-
-void
-sp_restore_security_context(THD *thd, Security_context *backup)
-{
- if (backup)
- thd->security_ctx= backup;
-}
-
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
/*
Structure that represent all instances of one table
@@ -3543,33 +3745,32 @@ typedef struct st_sp_table
uint8 trg_event_map;
} SP_TABLE;
-byte *
-sp_table_key(const byte *ptr, uint *plen, my_bool first)
+
+uchar *sp_table_key(const uchar *ptr, size_t *plen, my_bool first)
{
SP_TABLE *tab= (SP_TABLE *)ptr;
*plen= tab->qname.length;
- return (byte *)tab->qname.str;
+ return (uchar *)tab->qname.str;
}
-/*
+/**
Merge the list of tables used by some query into the multi-set of
tables used by routine.
- SYNOPSIS
- merge_table_list()
- thd - thread context
- table - table list
- lex_for_tmp_check - LEX of the query for which we are merging
- table list.
+ @param thd thread context
+ @param table table list
+ @param lex_for_tmp_check LEX of the query for which we are merging
+ table list.
- NOTE
+ @note
This method will use LEX provided to check whenever we are creating
temporary table and mark it as such in target multi-set.
- RETURN VALUE
- TRUE - Success
- FALSE - Error
+ @retval
+ TRUE Success
+ @retval
+ FALSE Error
*/
bool
@@ -3617,8 +3818,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
(and therefore should not be prelocked). Otherwise we will erroneously
treat table with same name but with different alias as non-temporary.
*/
- if ((tab= (SP_TABLE *)hash_search(&m_sptabs, (byte *)tname, tlen)) ||
- ((tab= (SP_TABLE *)hash_search(&m_sptabs, (byte *)tname,
+ if ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname, tlen)) ||
+ ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname,
tlen - alen - 1)) &&
tab->temp))
{
@@ -3650,34 +3851,33 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
tab->lock_type= table->lock_type;
tab->lock_count= tab->query_lock_count= 1;
tab->trg_event_map= table->trg_event_map;
- my_hash_insert(&m_sptabs, (byte *)tab);
+ my_hash_insert(&m_sptabs, (uchar *)tab);
}
}
return TRUE;
}
-/*
+/**
Add tables used by routine to the table list.
- SYNOPSIS
- add_used_tables_to_table_list()
- thd [in] Thread context
- query_tables_last_ptr [in/out] Pointer to the next_global member of
- last element of the list where tables
- will be added (or to its root).
- belong_to_view [in] Uppermost view which uses this routine,
- 0 if none.
-
- DESCRIPTION
Converts multi-set of tables used by this routine to table list and adds
this list to the end of table list specified by 'query_tables_last_ptr'.
Elements of list will be allocated in PS memroot, so this list will be
persistent between PS executions.
- RETURN VALUE
- TRUE - if some elements were added, FALSE - otherwise.
+ @param[in] thd Thread context
+ @param[in,out] query_tables_last_ptr Pointer to the next_global member of
+ last element of the list where tables
+ will be added (or to its root).
+ @param[in] belong_to_view Uppermost view which uses this routine,
+ 0 if none.
+
+ @retval
+ TRUE if some elements were added
+ @retval
+ FALSE otherwise.
*/
bool
@@ -3747,7 +3947,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
}
-/*
+/**
Simple function for adding an explicetly named (systems) table to
the global table list, e.g. "mysql", "proc".
*/
@@ -3770,7 +3970,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
table->table_name= thd->strmake(name, table->table_name_length);
table->alias= thd->strdup(name);
table->lock_type= locktype;
- table->select_lex= lex->current_select; // QQ?
+ table->select_lex= lex->current_select;
table->cacheable_table= 1;
lex->add_to_query_tables(table);