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.cc2032
1 files changed, 1434 insertions, 598 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 0179214e508..458955b2d6b 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -15,7 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_prepare.h"
@@ -23,13 +23,12 @@
#include "probes_mysql.h"
#include "sql_show.h" // append_identifier
#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
-#include "sql_table.h" // sp_prepare_create_field,
- // prepare_create_field
#include "sql_acl.h" // *_ACL
#include "sql_array.h" // Dynamic_array
#include "log_event.h" // Query_log_event
#include "sql_derived.h" // mysql_handle_derived
#include "sql_cte.h"
+#include "sql_select.h" // Virtual_tmp_table
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
@@ -73,8 +72,12 @@ static void reset_start_time_for_sp(THD *thd)
Item::Type
-sp_map_item_type(enum enum_field_types type)
+sp_map_item_type(const Type_handler *handler)
{
+ if (handler == &type_handler_row)
+ return Item::ROW_ITEM;
+ enum_field_types type= real_type_to_type(handler->real_field_type());
+
switch (type) {
case MYSQL_TYPE_BIT:
case MYSQL_TYPE_TINY:
@@ -95,68 +98,47 @@ sp_map_item_type(enum enum_field_types type)
}
-/**
- Return a string representation of the Item value.
-
- @param thd thread handle
- @param 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.
+bool Item_splocal::append_for_log(THD *thd, String *str)
+{
+ if (fix_fields_if_needed(thd, NULL))
+ return true;
- @retval
- NULL on error
- @retval
- non-NULL a pointer to valid a valid string on success
-*/
+ if (limit_clause_param)
+ return str->append_ulonglong(val_uint());
-static String *
-sp_get_item_value(THD *thd, Item *item, String *str)
-{
- switch (item->result_type()) {
- case REAL_RESULT:
- case INT_RESULT:
- case DECIMAL_RESULT:
- if (item->field_type() != MYSQL_TYPE_BIT)
- return item->val_str(str);
- /* fall through */
- case STRING_RESULT:
- {
- String *result= item->val_str(str);
+ /*
+ ROW variables are currently not allowed in select_list, e.g.:
+ SELECT row_variable;
+ ROW variables can appear in query parts where name is not important, e.g.:
+ SELECT ROW(1,2)=row_variable FROM t1;
+ So we can skip using NAME_CONST() and use ROW() constants directly.
+ */
+ if (type_handler() == &type_handler_row)
+ return append_value_for_log(thd, str);
- if (!result)
- return NULL;
+ if (str->append(STRING_WITH_LEN(" NAME_CONST('")) ||
+ str->append(&m_name) ||
+ str->append(STRING_WITH_LEN("',")))
+ return true;
+ return append_value_for_log(thd, str) || str->append(')');
+}
- {
- StringBuffer<STRING_BUFFER_USUAL_SIZE> buf(result->charset());
- CHARSET_INFO *cs= thd->variables.character_set_client;
-
- buf.append('_');
- buf.append(result->charset()->csname);
- if (cs->escape_with_backslash_is_dangerous)
- buf.append(' ');
- append_query_string(cs, &buf, result->ptr(), result->length(),
- thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
- buf.append(" COLLATE '");
- buf.append(item->collation.collation->name);
- buf.append('\'');
- str->copy(buf);
-
- return str;
- }
- }
- case ROW_RESULT:
- default:
- return NULL;
- }
+bool Item_splocal::append_value_for_log(THD *thd, String *str)
+{
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1);
+ Item *item= this_item();
+ String *str_value= item->type_handler()->print_item_value(thd, item,
+ &str_value_holder);
+ return str_value ?
+ str->append(*str_value) :
+ str->append(STRING_WITH_LEN("NULL"));
}
-bool Item_splocal::append_for_log(THD *thd, String *str)
+bool Item_splocal_row_field::append_for_log(THD *thd, String *str)
{
- if (fix_fields(thd, NULL))
+ if (fix_fields_if_needed(thd, NULL))
return true;
if (limit_clause_param)
@@ -164,15 +146,11 @@ bool Item_splocal::append_for_log(THD *thd, String *str)
if (str->append(STRING_WITH_LEN(" NAME_CONST('")) ||
str->append(&m_name) ||
+ str->append(".") ||
+ str->append(&m_field_name) ||
str->append(STRING_WITH_LEN("',")))
return true;
-
- StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1);
- String *str_value= sp_get_item_value(thd, this_item(), &str_value_holder);
- if (str_value)
- return str->append(*str_value) || str->append(')');
- else
- return str->append(STRING_WITH_LEN("NULL)"));
+ return append_value_for_log(thd, str) || str->append(')');
}
@@ -215,6 +193,8 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_CREATE_DB:
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_CREATE_PROC:
+ case SQLCOM_SHOW_CREATE_PACKAGE:
+ case SQLCOM_SHOW_CREATE_PACKAGE_BODY:
case SQLCOM_SHOW_CREATE_EVENT:
case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_CREATE_USER:
@@ -235,11 +215,14 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
case SQLCOM_SHOW_PROC_CODE:
+ case SQLCOM_SHOW_PACKAGE_BODY_CODE:
case SQLCOM_SHOW_SLAVE_HOSTS:
case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SHOW_STATUS:
case SQLCOM_SHOW_STATUS_FUNC:
case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STATUS_PACKAGE:
+ case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
case SQLCOM_SHOW_STORAGE_ENGINES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TABLE_STATUS:
@@ -263,12 +246,14 @@ sp_get_flags_for_command(LEX *lex)
flags= sp_head::CONTAINS_DYNAMIC_SQL;
break;
case SQLCOM_CREATE_TABLE:
+ case SQLCOM_CREATE_SEQUENCE:
if (lex->tmp_table())
flags= 0;
else
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
break;
case SQLCOM_DROP_TABLE:
+ case SQLCOM_DROP_SEQUENCE:
if (lex->tmp_table())
flags= 0;
else
@@ -282,11 +267,14 @@ sp_get_flags_for_command(LEX *lex)
break;
case SQLCOM_CREATE_INDEX:
case SQLCOM_CREATE_DB:
+ case SQLCOM_CREATE_PACKAGE:
+ case SQLCOM_CREATE_PACKAGE_BODY:
case SQLCOM_CREATE_VIEW:
case SQLCOM_CREATE_TRIGGER:
case SQLCOM_CREATE_USER:
case SQLCOM_CREATE_ROLE:
case SQLCOM_ALTER_TABLE:
+ case SQLCOM_ALTER_SEQUENCE:
case SQLCOM_ALTER_USER:
case SQLCOM_GRANT:
case SQLCOM_GRANT_ROLE:
@@ -297,6 +285,8 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_RENAME_USER:
case SQLCOM_DROP_INDEX:
case SQLCOM_DROP_DB:
+ case SQLCOM_DROP_PACKAGE:
+ case SQLCOM_DROP_PACKAGE_BODY:
case SQLCOM_REVOKE_ALL:
case SQLCOM_DROP_USER:
case SQLCOM_DROP_ROLE:
@@ -359,8 +349,8 @@ sp_get_flags_for_command(LEX *lex)
/**
Prepare an Item for evaluation (call of fix_fields).
- @param thd thread handler
@param it_addr pointer on item refernce
+ @param cols expected number of elements (1 for scalar, >=1 for ROWs)
@retval
NULL error
@@ -368,15 +358,31 @@ sp_get_flags_for_command(LEX *lex)
non-NULL prepared item
*/
-Item *
-sp_prepare_func_item(THD* thd, Item **it_addr)
+Item *THD::sp_prepare_func_item(Item **it_addr, uint cols)
{
- DBUG_ENTER("sp_prepare_func_item");
- it_addr= (*it_addr)->this_item_addr(thd, it_addr);
+ DBUG_ENTER("THD::sp_prepare_func_item");
+ Item *res= sp_fix_func_item(it_addr);
+ if (res && res->check_cols(cols))
+ DBUG_RETURN(NULL);
+ DBUG_RETURN(res);
+}
+
+
+/**
+ Fix an Item for evaluation for SP.
+*/
+
+Item *THD::sp_fix_func_item(Item **it_addr)
+{
+ DBUG_ENTER("THD::sp_fix_func_item");
+ if ((*it_addr)->fix_fields_if_needed(this, it_addr))
+ {
+ DBUG_PRINT("info", ("fix_fields() failed"));
+ DBUG_RETURN(NULL);
+ }
+ it_addr= (*it_addr)->this_item_addr(this, it_addr);
- if (!(*it_addr)->fixed &&
- ((*it_addr)->fix_fields(thd, it_addr) ||
- (*it_addr)->check_cols(1)))
+ if ((*it_addr)->fix_fields_if_needed(this, it_addr))
{
DBUG_PRINT("info", ("fix_fields() failed"));
DBUG_RETURN(NULL);
@@ -388,7 +394,6 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
/**
Evaluate an expression and store the result in the field.
- @param thd current thread object
@param result_field the field to store the result
@param expr_item_ptr the root item of the expression
@@ -398,54 +403,13 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
TRUE on error
*/
-bool
-sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
+bool THD::sp_eval_expr(Field *result_field, Item **expr_item_ptr)
{
- Item *expr_item;
- enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
- bool save_abort_on_warning= thd->abort_on_warning;
- bool save_stmt_modified_non_trans_table=
- thd->transaction.stmt.modified_non_trans_table;
-
- DBUG_ENTER("sp_eval_expr");
-
- if (!*expr_item_ptr)
- goto error;
-
- if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr)))
- goto error;
-
- /*
- Set THD flags to emit warnings/errors in case of overflow/type errors
- during saving the item into the field.
-
- Save original values and restore them after save.
- */
-
- thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
- thd->abort_on_warning= thd->is_strict_mode();
- thd->transaction.stmt.modified_non_trans_table= FALSE;
-
+ DBUG_ENTER("THD::sp_eval_expr");
+ DBUG_ASSERT(*expr_item_ptr);
+ Sp_eval_expr_state state(this);
/* Save the value in the field. Convert the value if needed. */
-
- expr_item->save_in_field(result_field, 0);
-
- thd->count_cuted_fields= save_count_cuted_fields;
- thd->abort_on_warning= save_abort_on_warning;
- thd->transaction.stmt.modified_non_trans_table= save_stmt_modified_non_trans_table;
-
- if (!thd->is_error())
- DBUG_RETURN(FALSE);
-
-error:
- /*
- In case of error during evaluation, leave the result field set to NULL.
- Sic: we can't do it in the beginning of the function because the
- result field might be needed for its own re-evaluation, e.g. case of
- set x = x + 1;
- */
- result_field->set_null();
- DBUG_RETURN (TRUE);
+ DBUG_RETURN(result_field->sp_prepare_and_store_item(this, expr_item_ptr));
}
@@ -462,42 +426,14 @@ error:
*/
sp_name::sp_name(const MDL_key *key, char *qname_buff)
+ :Database_qualified_name(key->db_name(), key->db_name_length(),
+ key->name(), key->name_length()),
+ m_explicit_name(false)
{
- m_db.str= (char*)key->db_name();
- m_db.length= key->db_name_length();
- m_name.str= (char*)key->name();
- m_name.length= key->name_length();
- m_qname.str= qname_buff;
if (m_db.length)
- {
strxmov(qname_buff, m_db.str, ".", m_name.str, NullS);
- m_qname.length= m_db.length + 1 + m_name.length;
- }
else
- {
strmov(qname_buff, m_name.str);
- m_qname.length= m_name.length;
- }
- 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_qname format: [database + dot] + name + '\0' */
- m_qname.length= m_db.length + dot + m_name.length;
- if (!(m_qname.str= (char*) thd->alloc(m_qname.length + 1)))
- return;
- sprintf(m_qname.str, "%.*s%.*s%.*s",
- (int) m_db.length, (m_db.length ? m_db.str : ""),
- dot, ".",
- (int) m_name.length, m_name.str);
- DBUG_ASSERT(ok_for_lower_case_names(m_db.str));
}
@@ -515,7 +451,7 @@ sp_name::init_qname(THD *thd)
*/
bool
-check_routine_name(LEX_STRING *ident)
+check_routine_name(const LEX_CSTRING *ident)
{
DBUG_ASSERT(ident);
DBUG_ASSERT(ident->str);
@@ -545,7 +481,8 @@ sp_head::operator new(size_t size) throw()
MEM_ROOT own_root;
sp_head *sp;
- init_sql_alloc(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC, MYF(0));
+ init_sql_alloc(&own_root, "sp_head",
+ MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC, MYF(0));
sp= (sp_head *) alloc_root(&own_root, size);
if (sp == NULL)
DBUG_RETURN(NULL);
@@ -575,14 +512,37 @@ sp_head::operator delete(void *ptr, size_t size) throw()
}
-sp_head::sp_head()
+sp_head::sp_head(sp_package *parent, const Sp_handler *sph)
:Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP),
+ Database_qualified_name(&null_clex_str, &null_clex_str),
+ m_parent(parent),
+ m_handler(sph),
m_flags(0),
+ m_tmp_query(NULL),
+ m_explicit_name(false),
+ /*
+ 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_qname(null_clex_str),
+ m_params(null_clex_str),
+ m_body(null_clex_str),
+ m_body_utf8(null_clex_str),
+ m_defstr(null_clex_str),
m_sp_cache_version(0),
m_creation_ctx(0),
unsafe_flags(0),
+ m_created(0),
+ m_modified(0),
m_recursion_level(0),
m_next_cached_sp(0),
+ m_param_begin(NULL),
+ m_param_end(NULL),
+ m_body_begin(NULL),
+ m_thd_root(NULL),
+ m_thd(NULL),
+ m_pcont(new (&main_mem_root) sp_pcontext()),
m_cont_level(0)
{
m_first_instance= this;
@@ -590,80 +550,203 @@ sp_head::sp_head()
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= null_lex_str;
DBUG_ENTER("sp_head::sp_head");
m_security_ctx.init();
m_backpatch.empty();
+ m_backpatch_goto.empty();
m_cont_backpatch.empty();
m_lex.empty();
+ my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
my_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;
}
-void
-sp_head::init(LEX *lex)
+sp_package::sp_package(LEX *top_level_lex,
+ const sp_name *name,
+ const Sp_handler *sph)
+ :sp_head(NULL, sph),
+ m_current_routine(NULL),
+ m_top_level_lex(top_level_lex),
+ m_rcontext(NULL),
+ m_invoked_subroutine_count(0),
+ m_is_instantiated(false),
+ m_is_cloning_routine(false)
{
- DBUG_ENTER("sp_head::init");
+ init_sp_name(name);
+}
- lex->spcont= m_pcont= new sp_pcontext();
- if (!lex->spcont)
- DBUG_VOID_RETURN;
+sp_package::~sp_package()
+{
+ m_routine_implementations.cleanup();
+ m_routine_declarations.cleanup();
+ m_body= null_clex_str;
+ if (m_current_routine)
+ delete m_current_routine->sphead;
+ delete m_rcontext;
+}
+
+
+/*
+ Test if two routines have equal specifications
+*/
+
+bool sp_head::eq_routine_spec(const sp_head *sp) const
+{
+ // TODO: Add tests for equal return data types (in case of FUNCTION)
+ // TODO: Add tests for equal argument data types
+ return
+ m_handler->type() == sp->m_handler->type() &&
+ m_pcont->context_var_count() == sp->m_pcont->context_var_count();
+}
+
+bool sp_package::validate_after_parser(THD *thd)
+{
+ if (m_handler->type() != TYPE_ENUM_PACKAGE_BODY)
+ return false;
+ sp_head *sp= sp_cache_lookup(&thd->sp_package_spec_cache, this);
+ sp_package *spec= sp ? sp->get_package() : NULL;
+ DBUG_ASSERT(spec); // CREATE PACKAGE must already be cached
+ return validate_public_routines(thd, spec) ||
+ validate_private_routines(thd);
+}
+
+
+bool sp_package::validate_public_routines(THD *thd, sp_package *spec)
+{
/*
- Altough trg_table_fields list is used only in triggers we init for all
- types of stored procedures to simplify reset_lex()/restore_lex() code.
+ Check that all routines declared in CREATE PACKAGE
+ have implementations in CREATE PACKAGE BODY.
*/
- lex->trg_table_fields.empty();
- my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
+ List_iterator<LEX> it(spec->m_routine_declarations);
+ for (LEX *lex; (lex= it++); )
+ {
+ bool found= false;
+ DBUG_ASSERT(lex->sphead);
+ List_iterator<LEX> it2(m_routine_implementations);
+ for (LEX *lex2; (lex2= it2++); )
+ {
+ DBUG_ASSERT(lex2->sphead);
+ if (Sp_handler::eq_routine_name(lex2->sphead->m_name,
+ lex->sphead->m_name) &&
+ lex2->sphead->eq_routine_spec(lex->sphead))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ my_error(ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY, MYF(0),
+ ErrConvDQName(lex->sphead).ptr());
+ return true;
+ }
+ }
+ return false;
+}
- m_param_begin= NULL;
- m_param_end= NULL;
- m_body_begin= NULL ;
+bool sp_package::validate_private_routines(THD *thd)
+{
+ /*
+ Check that all forwad declarations in
+ CREATE PACKAGE BODY have implementations.
+ */
+ List_iterator<LEX> it(m_routine_declarations);
+ for (LEX *lex; (lex= it++); )
+ {
+ bool found= false;
+ DBUG_ASSERT(lex->sphead);
+ List_iterator<LEX> it2(m_routine_implementations);
+ for (LEX *lex2; (lex2= it2++); )
+ {
+ DBUG_ASSERT(lex2->sphead);
+ if (Sp_handler::eq_routine_name(lex2->sphead->m_name,
+ lex->sphead->m_name) &&
+ lex2->sphead->eq_routine_spec(lex->sphead))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ my_error(ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED, MYF(0),
+ ErrConvDQName(lex->sphead).ptr());
+ return true;
+ }
+ }
+ return false;
+}
- m_qname.str= NULL;
- m_qname.length= 0;
- m_explicit_name= false;
+LEX *sp_package::LexList::find(const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ const char *dot;
+ if (lex->sphead->m_handler->type() == type &&
+ (dot= strrchr(lex->sphead->m_name.str, '.')))
+ {
+ size_t ofs= dot + 1 - lex->sphead->m_name.str;
+ LEX_CSTRING non_qualified_sphead_name= lex->sphead->m_name;
+ non_qualified_sphead_name.str+= ofs;
+ non_qualified_sphead_name.length-= ofs;
+ if (Sp_handler::eq_routine_name(non_qualified_sphead_name, name))
+ return lex;
+ }
+ }
+ return NULL;
+}
- m_db.str= NULL;
- m_db.length= 0;
- m_name.str= NULL;
- m_name.length= 0;
+LEX *sp_package::LexList::find_qualified(const LEX_CSTRING &name,
+ stored_procedure_type type)
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ if (lex->sphead->m_handler->type() == type &&
+ Sp_handler::eq_routine_name(lex->sphead->m_name, name))
+ return lex;
+ }
+ return NULL;
+}
- m_params.str= NULL;
- m_params.length= 0;
- m_body.str= NULL;
- m_body.length= 0;
+void
+sp_head::init(LEX *lex)
+{
+ DBUG_ENTER("sp_head::init");
+
+ lex->spcont= m_pcont;
- m_defstr.str= NULL;
- m_defstr.length= 0;
+ if (!lex->spcont)
+ DBUG_VOID_RETURN;
- m_return_field_def.charset= NULL;
+ /*
+ Altough trg_table_fields list is used only in triggers we init for all
+ types of stored procedures to simplify reset_lex()/restore_lex() code.
+ */
+ lex->trg_table_fields.empty();
DBUG_VOID_RETURN;
}
void
-sp_head::init_sp_name(THD *thd, sp_name *spname)
+sp_head::init_sp_name(const sp_name *spname)
{
DBUG_ENTER("sp_head::init_sp_name");
@@ -672,24 +755,8 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
DBUG_ASSERT(spname && spname->m_db.str && spname->m_db.length);
/* We have to copy strings to get them into the right memroot. */
-
- m_db.length= spname->m_db.length;
- m_db.str= strmake_root(thd->mem_root, spname->m_db.str, spname->m_db.length);
-
- m_name.length= spname->m_name.length;
- m_name.str= strmake_root(thd->mem_root, spname->m_name.str,
- spname->m_name.length);
-
+ Database_qualified_name::copy(&main_mem_root, spname->m_db, spname->m_name);
m_explicit_name= spname->m_explicit_name;
-
- if (spname->m_qname.length == 0)
- spname->init_qname(thd);
-
- m_qname.length= spname->m_qname.length;
- m_qname.str= (char*) memdup_root(thd->mem_root,
- spname->m_qname.str,
- spname->m_qname.length + 1);
-
DBUG_VOID_RETURN;
}
@@ -707,7 +774,6 @@ sp_head::set_stmt_end(THD *thd)
{
Lex_input_stream *lip= & thd->m_parser_state->m_lip; /* shortcut */
const char *end_ptr= lip->get_cpp_ptr(); /* shortcut */
- uint not_used;
/* Make the string of parameters. */
@@ -725,7 +791,7 @@ sp_head::set_stmt_end(THD *thd)
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, &not_used);
+ trim_whitespace(thd->charset(), &m_body);
/* Make the string of UTF-body. */
@@ -733,7 +799,7 @@ sp_head::set_stmt_end(THD *thd)
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, &not_used);
+ trim_whitespace(thd->charset(), &m_body_utf8);
/*
Make the string of whole stored-program-definition query (in the
@@ -742,59 +808,7 @@ sp_head::set_stmt_end(THD *thd)
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, &not_used);
-}
-
-
-static TYPELIB *
-create_typelib(MEM_ROOT *mem_root, Column_definition *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));
- result->count= src->elements;
- result->name= "";
- if (!(result->type_names=(const char **)
- alloc_root(mem_root,(sizeof(char *)+sizeof(int))*(result->count+1))))
- DBUG_RETURN(0);
- 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++)
- {
- uint32 dummy;
- uint length;
- String *tmp= it++;
-
- if (String::needs_conversion(tmp->length(), tmp->charset(),
- cs, &dummy))
- {
- uint cnv_errs;
- conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
-
- length= conv.length();
- result->type_names[i]= (char*) strmake_root(mem_root, conv.ptr(),
- length);
- }
- else
- {
- length= tmp->length();
- result->type_names[i]= strmake_root(mem_root, tmp->ptr(), length);
- }
-
- // Strip trailing spaces.
- length= cs->cset->lengthsp(cs, result->type_names[i], length);
- result->type_lengths[i]= length;
- ((uchar *)result->type_names[i])[length]= '\0';
- }
- result->type_names[result->count]= 0;
- result->type_lengths[result->count]= 0;
- }
- DBUG_RETURN(result);
+ trim_whitespace(thd->charset(), &m_defstr);
}
@@ -837,16 +851,28 @@ sp_head::~sp_head()
}
+void sp_package::LexList::cleanup()
+{
+ List_iterator<LEX> it(*this);
+ for (LEX *lex; (lex= it++); )
+ {
+ lex_end(lex);
+ delete lex;
+ }
+}
+
+
/**
This is only used for result fields from functions (both during
fix_length_and_dec() and evaluation).
*/
Field *
-sp_head::create_result_field(uint field_max_length, const char *field_name,
- TABLE *table)
+sp_head::create_result_field(uint field_max_length, const LEX_CSTRING *field_name,
+ TABLE *table) const
{
Field *field;
+ LEX_CSTRING name;
DBUG_ENTER("sp_head::create_result_field");
@@ -882,18 +908,25 @@ sp_head::create_result_field(uint field_max_length, const char *field_name,
Perhaps we should refactor prepare_create_field() to set
Create_field::length to maximum octet length for BLOBs,
instead of packed length).
+
+ Note, for integer data types, field_max_length can be bigger
+ than the user specified length, e.g. a field of the INT(1) data type
+ is translated to the item with max_length=11.
*/
DBUG_ASSERT(field_max_length <= m_return_field_def.length ||
+ m_return_field_def.type_handler()->cmp_type() == INT_RESULT ||
(current_thd->stmt_arena->is_stmt_execute() &&
m_return_field_def.length == 8 &&
(m_return_field_def.pack_flag &
(FIELDFLAG_BLOB|FIELDFLAG_GEOM))));
+ if (field_name)
+ name= *field_name;
+ else
+ name= m_name;
field= m_return_field_def.make_field(table->s, /* TABLE_SHARE ptr */
table->in_use->mem_root,
- field_name ?
- field_name :
- (const char *) m_name.str);
+ &name);
field->vcol_info= m_return_field_def.vcol_info;
if (field)
@@ -1027,7 +1060,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
rewritables.sort(cmp_rqp_locations);
- thd->query_name_consts= rewritables.elements();
+ thd->query_name_consts= (uint)rewritables.elements();
for (Rewritable_query_parameter **rqp= rewritables.front();
rqp <= rewritables.back(); rqp++)
@@ -1050,14 +1083,14 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
<db_name> Name of current database
<flags> Flags struct
*/
- int buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
- thd->db_length + QUERY_CACHE_FLAGS_SIZE + 1);
+ size_t buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
+ thd->db.length + QUERY_CACHE_FLAGS_SIZE + 1);
if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len)))
{
char *ptr= pbuf + qbuf.length();
memcpy(pbuf, qbuf.ptr(), qbuf.length());
*ptr= 0;
- int2store(ptr+1, thd->db_length);
+ int2store(ptr+1, thd->db.length);
}
else
DBUG_RETURN(TRUE);
@@ -1068,30 +1101,15 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
}
-/**
- Return appropriate error about recursion limit reaching
-
- @param thd Thread handle
-
- @remark For functions and triggers we return error about
- prohibited recursion. For stored procedures we
- return about reaching recursion limit.
-*/
-
-void sp_head::recursion_level_error(THD *thd)
+void Sp_handler_procedure::recursion_level_error(THD *thd,
+ const sp_head *sp) const
{
- if (m_type == TYPE_ENUM_PROCEDURE)
- {
- my_error(ER_SP_RECURSION_LIMIT, MYF(0),
- static_cast<int>(thd->variables.max_sp_recursion_depth),
- m_name.str);
- }
- else
- my_error(ER_SP_NO_RECURSION, MYF(0));
+ my_error(ER_SP_RECURSION_LIMIT, MYF(0),
+ static_cast<int>(thd->variables.max_sp_recursion_depth),
+ sp->m_name.str);
}
-
/**
Execute the routine. The main instruction jump loop is there.
Assume the parameters already set.
@@ -1123,7 +1141,13 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
sp_rcontext *ctx= thd->spcont;
bool err_status= FALSE;
uint ip= 0;
- ulonglong save_sql_mode;
+ sql_mode_t save_sql_mode;
+
+ // TODO(cvicentiu) See if you can drop this bit. This is used to resume
+ // execution from where we left off.
+ if (m_chistics.agg_type == GROUP_AGGREGATE)
+ ip= thd->spcont->instr_ptr;
+
bool save_abort_on_warning;
Query_arena *old_arena;
/* per-instruction arena */
@@ -1149,10 +1173,13 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
DBUG_RETURN(TRUE);
/* init per-instruction memroot */
- init_sql_alloc(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&execute_mem_root, "per_instruction_memroot",
+ MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
DBUG_ASSERT(!(m_flags & IS_INVOKED));
m_flags|= IS_INVOKED;
+ if (m_parent)
+ m_parent->m_invoked_subroutine_count++;
m_first_instance->m_first_free_instance= m_next_cached_sp;
if (m_next_cached_sp)
{
@@ -1268,11 +1295,10 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
/* Discard the initial part of executing routines. */
thd->profiling.discard_current_query();
#endif
+ sp_instr *i;
DEBUG_SYNC(thd, "sp_head_execute_before_loop");
do
{
- sp_instr *i;
-
#if defined(ENABLED_PROFILING)
/*
Treat each "instr" of a routine as discrete unit that could be profiled.
@@ -1290,6 +1316,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
#if defined(ENABLED_PROFILING)
thd->profiling.discard_current_query();
#endif
+ thd->spcont->quit_func= TRUE;
break;
}
@@ -1352,7 +1379,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
errors are not catchable by SQL handlers) or the connection has been
killed during execution.
*/
- if (!thd->is_fatal_error && !thd->killed_errno() &&
+ if (likely(!thd->is_fatal_error) && likely(!thd->killed_errno()) &&
ctx->handle_sql_condition(thd, &ip, i))
{
err_status= FALSE;
@@ -1361,7 +1388,9 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
/* Reset sp_rcontext::end_partial_result_set flag. */
ctx->end_partial_result_set= FALSE;
- } while (!err_status && !thd->killed && !thd->is_fatal_error);
+ } while (!err_status && likely(!thd->killed) &&
+ likely(!thd->is_fatal_error) &&
+ !thd->spcont->pause_state);
#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
@@ -1377,9 +1406,14 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
thd->restore_active_arena(&execute_arena, &backup_arena);
- thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error
+ /* Only pop cursors when we're done with group aggregate running. */
+ if (m_chistics.agg_type != GROUP_AGGREGATE ||
+ (m_chistics.agg_type == GROUP_AGGREGATE && thd->spcont->quit_func))
+ thd->spcont->pop_all_cursors(thd); // To avoid memory leaks after an error
/* Restore all saved */
+ if (m_chistics.agg_type == GROUP_AGGREGATE)
+ thd->spcont->instr_ptr= ip;
thd->server_status= (thd->server_status & ~status_backup_mask) | old_server_status;
old_packet.swap(thd->packet);
DBUG_ASSERT(thd->Item_change_list::is_empty());
@@ -1434,6 +1468,13 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
da->opt_clear_warning_info(thd->query_id);
da->copy_sql_conditions_from_wi(thd, &sp_wi);
da->remove_marked_sql_conditions();
+ if (i != NULL)
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_SP_STACK_TRACE,
+ ER_THD(thd, ER_SP_STACK_TRACE),
+ i->m_lineno,
+ m_qname.str != NULL ? m_qname.str :
+ "anonymous block");
}
}
@@ -1455,9 +1496,11 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
NULL. In this case, mysql_change_db() would generate an error.
*/
- err_status|= mysql_change_db(thd, &saved_cur_db_name, TRUE);
+ err_status|= mysql_change_db(thd, (LEX_CSTRING*)&saved_cur_db_name, TRUE) != 0;
}
m_flags&= ~IS_INVOKED;
+ if (m_parent)
+ m_parent->m_invoked_subroutine_count--;
DBUG_PRINT("info",
("first free for %p --: %p->%p, level: %lu, flags %x",
m_first_instance,
@@ -1496,7 +1539,6 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
@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
@@ -1511,13 +1553,12 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
*/
bool
-set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
- Security_context **save_ctx)
+set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
{
*save_ctx= 0;
- 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,
+ if (sp->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;
@@ -1533,8 +1574,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
statement that 'may' affect this.
*/
if (*save_ctx &&
- check_routine_access(thd, EXECUTE_ACL,
- sp->m_db.str, sp->m_name.str, is_proc, FALSE))
+ sp->check_execute_access(thd))
{
sp->m_security_ctx.restore_security_context(thd, *save_ctx);
*save_ctx= 0;
@@ -1546,6 +1586,75 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
#endif // ! NO_EMBEDDED_ACCESS_CHECKS
+bool sp_head::check_execute_access(THD *thd) const
+{
+ return m_parent ? m_parent->check_execute_access(thd) :
+ check_routine_access(thd, EXECUTE_ACL,
+ &m_db, &m_name,
+ m_handler, false);
+}
+
+
+/**
+ Create rcontext optionally using the routine security.
+ This is important for sql_mode=ORACLE to make sure that the invoker has
+ access to the tables mentioned in the %TYPE references.
+
+ In non-Oracle sql_modes we do not need access to any tables,
+ so we can omit the security context switch for performance purposes.
+
+ @param thd
+ @param ret_value
+ @retval NULL - error (access denided or EOM)
+ @retval !NULL - success (the invoker has rights to all %TYPE tables)
+*/
+
+sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value,
+ Row_definition_list *defs,
+ bool switch_security_ctx)
+{
+ if (!(m_flags & HAS_COLUMN_TYPE_REFS))
+ return sp_rcontext::create(thd, this, m_pcont, ret_value, *defs);
+ sp_rcontext *res= NULL;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_security_ctx;
+ if (switch_security_ctx &&
+ set_routine_security_ctx(thd, this, &save_security_ctx))
+ return NULL;
+#endif
+ if (!defs->resolve_type_refs(thd))
+ res= sp_rcontext::create(thd, this, m_pcont, ret_value, *defs);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (switch_security_ctx)
+ m_security_ctx.restore_security_context(thd, save_security_ctx);
+#endif
+ return res;
+}
+
+
+sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value,
+ List<Item> *args)
+{
+ DBUG_ASSERT(args);
+ Row_definition_list defs;
+ m_pcont->retrieve_field_definitions(&defs);
+ if (defs.adjust_formal_params_to_actual_params(thd, args))
+ return NULL;
+ return rcontext_create(thd, ret_value, &defs, true);
+}
+
+
+sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value,
+ Item **args, uint arg_count)
+{
+ Row_definition_list defs;
+ m_pcont->retrieve_field_definitions(&defs);
+ if (defs.adjust_formal_params_to_actual_params(thd, args, arg_count))
+ return NULL;
+ return rcontext_create(thd, ret_value, &defs, true);
+}
+
+
/**
Execute trigger stored program.
@@ -1574,8 +1683,8 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
bool
sp_head::execute_trigger(THD *thd,
- const LEX_STRING *db_name,
- const LEX_STRING *table_name,
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name,
GRANT_INFO *grant_info)
{
sp_rcontext *octx = thd->spcont;
@@ -1584,7 +1693,6 @@ sp_head::execute_trigger(THD *thd,
MEM_ROOT call_mem_root;
Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP);
Query_arena backup_arena;
-
DBUG_ENTER("sp_head::execute_trigger");
DBUG_PRINT("info", ("trigger %s", m_name.str));
@@ -1592,10 +1700,10 @@ sp_head::execute_trigger(THD *thd,
Security_context *save_ctx= NULL;
- if (m_chistics->suid != SP_IS_NOT_SUID &&
+ if (suid() != SP_IS_NOT_SUID &&
m_security_ctx.change_security_context(thd,
- &m_definer_user,
- &m_definer_host,
+ &m_definer.user,
+ &m_definer.host,
&m_db,
&save_ctx))
DBUG_RETURN(TRUE);
@@ -1640,19 +1748,18 @@ sp_head::execute_trigger(THD *thd,
TODO: we should create sp_rcontext once per command and reuse it
on subsequent executions of a trigger.
*/
- init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&call_mem_root, "execute_trigger", MEM_ROOT_BLOCK_SIZE, 0,
+ MYF(0));
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
+ Row_definition_list defs;
+ m_pcont->retrieve_field_definitions(&defs);
+ if (!(nctx= rcontext_create(thd, NULL, &defs, false)))
{
err_status= TRUE;
goto err_with_cleanup;
}
-#ifndef DBUG_OFF
- nctx->sp= this;
-#endif
-
thd->spcont= nctx;
err_status= execute(thd, FALSE);
@@ -1676,6 +1783,43 @@ err_with_cleanup:
}
+/*
+ Execute the package initialization section.
+*/
+
+bool sp_package::instantiate_if_needed(THD *thd)
+{
+ List<Item> args;
+ if (m_is_instantiated)
+ return false;
+ /*
+ Set m_is_instantiated to true early, to avoid recursion in case if
+ the package initialization section calls routines from the same package.
+ */
+ m_is_instantiated= true;
+ /*
+ Check that the initialization section doesn't contain Dynamic SQL
+ and doesn't return result sets: such stored procedures can't
+ be called from a function or trigger.
+ */
+ if (thd->in_sub_stmt)
+ {
+ const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
+ "trigger" : "function");
+ if (is_not_allowed_in_function(where))
+ goto err;
+ }
+
+ args.elements= 0;
+ if (execute_procedure(thd, &args))
+ goto err;
+ return false;
+err:
+ m_is_instantiated= false;
+ return true;
+}
+
+
/**
Execute a function.
@@ -1711,22 +1855,23 @@ err_with_cleanup:
bool
sp_head::execute_function(THD *thd, Item **argp, uint argcount,
- Field *return_value_fld)
+ Field *return_value_fld, sp_rcontext **func_ctx,
+ Query_arena *call_arena)
{
ulonglong UNINIT_VAR(binlog_save_options);
bool need_binlog_call= FALSE;
uint arg_no;
sp_rcontext *octx = thd->spcont;
- sp_rcontext *nctx = NULL;
char buf[STRING_BUFFER_USUAL_SIZE];
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::STMT_INITIALIZED_FOR_SP);
Query_arena backup_arena;
DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("info", ("function %s", m_name.str));
+ if (m_parent && m_parent->instantiate_if_needed(thd))
+ DBUG_RETURN(true);
+
/*
Check that the function is called with all specified arguments.
@@ -1740,7 +1885,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
invoking query properly.
*/
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0),
- "FUNCTION", m_qname.str, m_pcont->context_var_count(), argcount);
+ "FUNCTION", ErrConvDQName(this).ptr(),
+ m_pcont->context_var_count(), argcount);
DBUG_RETURN(TRUE);
}
/*
@@ -1754,27 +1900,25 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
TODO: we should create sp_rcontext once per command and reuse
it on subsequent executions of a function/trigger.
*/
- init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
- thd->set_n_backup_active_arena(&call_arena, &backup_arena);
-
- if (!(nctx= sp_rcontext::create(thd, m_pcont, return_value_fld)))
+ if (!(*func_ctx))
{
- thd->restore_active_arena(&call_arena, &backup_arena);
- err_status= TRUE;
- goto err_with_cleanup;
- }
+ thd->set_n_backup_active_arena(call_arena, &backup_arena);
- /*
- We have to switch temporarily back to callers arena/memroot.
- Function arguments belong to the caller and so the may reference
- memory which they will allocate during calculation long after
- this function call will be finished (e.g. in Item::cleanup()).
- */
- thd->restore_active_arena(&call_arena, &backup_arena);
+ if (!(*func_ctx= rcontext_create(thd, return_value_fld, argp, argcount)))
+ {
+ thd->restore_active_arena(call_arena, &backup_arena);
+ err_status= TRUE;
+ goto err_with_cleanup;
+ }
-#ifndef DBUG_OFF
- nctx->sp= this;
-#endif
+ /*
+ We have to switch temporarily back to callers arena/memroot.
+ Function arguments belong to the caller and so the may reference
+ memory which they will allocate during calculation long after
+ this function call will be finished (e.g. in Item::cleanup()).
+ */
+ thd->restore_active_arena(call_arena, &backup_arena);
+ }
/* Pass arguments. */
for (arg_no= 0; arg_no < argcount; arg_no++)
@@ -1782,7 +1926,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
/* Arguments must be fixed in Item_func_sp::fix_fields */
DBUG_ASSERT(argp[arg_no]->fixed);
- if ((err_status= nctx->set_variable(thd, arg_no, &(argp[arg_no]))))
+ if ((err_status= (*func_ctx)->set_parameter(thd, arg_no, &(argp[arg_no]))))
goto err_with_cleanup;
}
@@ -1802,9 +1946,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
{
binlog_buf.length(0);
binlog_buf.append(STRING_WITH_LEN("SELECT "));
- append_identifier(thd, &binlog_buf, m_db.str, m_db.length);
+ append_identifier(thd, &binlog_buf, &m_db);
binlog_buf.append('.');
- append_identifier(thd, &binlog_buf, m_name.str, m_name.length);
+ append_identifier(thd, &binlog_buf, &m_name);
binlog_buf.append('(');
for (arg_no= 0; arg_no < argcount; arg_no++)
{
@@ -1814,9 +1958,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
if (arg_no)
binlog_buf.append(',');
- str_value= sp_get_item_value(thd, nctx->get_item(arg_no),
- &str_value_holder);
-
+ Item_field *item= (*func_ctx)->get_parameter(arg_no);
+ str_value= item->type_handler()->print_item_value(thd, item,
+ &str_value_holder);
if (str_value)
binlog_buf.append(*str_value);
else
@@ -1824,11 +1968,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
binlog_buf.append(')');
}
- thd->spcont= nctx;
+ thd->spcont= *func_ctx;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx;
- if (set_routine_security_ctx(thd, this, FALSE, &save_security_ctx))
+ if (set_routine_security_ctx(thd, this, &save_security_ctx))
{
err_status= TRUE;
goto err_with_cleanup;
@@ -1865,11 +2009,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
sp_rcontext and allocate all these objects (and sp_rcontext
itself) on it directly rather than juggle with arenas.
*/
- thd->set_n_backup_active_arena(&call_arena, &backup_arena);
+ thd->set_n_backup_active_arena(call_arena, &backup_arena);
err_status= execute(thd, TRUE);
- thd->restore_active_arena(&call_arena, &backup_arena);
+ thd->restore_active_arena(call_arena, &backup_arena);
if (need_binlog_call)
{
@@ -1895,11 +2039,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
}
- if (!err_status)
+ if (!err_status && thd->spcont->quit_func)
{
/* We need result only in function but not in trigger */
- if (!nctx->is_return_value_set())
+ if (!(*func_ctx)->is_return_value_set())
{
my_error(ER_SP_NORETURNEND, MYF(0), m_name.str);
err_status= TRUE;
@@ -1911,9 +2055,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
#endif
err_with_cleanup:
- delete nctx;
- call_arena.free_items();
- free_root(&call_mem_root, MYF(0));
thd->spcont= octx;
/*
@@ -1958,13 +2099,17 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
sp_rcontext *nctx = NULL;
bool save_enable_slow_log;
bool save_log_general= false;
+ sp_package *pkg= get_package();
DBUG_ENTER("sp_head::execute_procedure");
DBUG_PRINT("info", ("procedure %s", m_name.str));
+ if (m_parent && m_parent->instantiate_if_needed(thd))
+ DBUG_RETURN(true);
+
if (args->elements != params)
{
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE",
- m_qname.str, params, args->elements);
+ ErrConvDQName(this).ptr(), params, args->elements);
DBUG_RETURN(TRUE);
}
@@ -1972,30 +2117,45 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (! octx)
{
/* Create a temporary old context. */
- if (!(octx= sp_rcontext::create(thd, m_pcont, NULL)))
+ if (!(octx= rcontext_create(thd, NULL, args)))
{
DBUG_PRINT("error", ("Could not create octx"));
DBUG_RETURN(TRUE);
}
-#ifndef DBUG_OFF
- octx->sp= 0;
-#endif
thd->spcont= octx;
/* set callers_arena to thd, for upper-level function to work */
thd->spcont->callers_arena= thd;
}
- if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
+ if (!pkg)
{
- delete nctx; /* Delete nctx if it was init() that failed. */
- thd->spcont= save_spcont;
- DBUG_RETURN(TRUE);
+ if (!(nctx= rcontext_create(thd, NULL, args)))
+ {
+ delete nctx; /* Delete nctx if it was init() that failed. */
+ thd->spcont= save_spcont;
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else
+ {
+ if (!pkg->m_rcontext)
+ {
+ Query_arena backup_arena;
+ thd->set_n_backup_active_arena(this, &backup_arena);
+ nctx= pkg->rcontext_create(thd, NULL, args);
+ thd->restore_active_arena(this, &backup_arena);
+ if (!nctx)
+ {
+ thd->spcont= save_spcont;
+ DBUG_RETURN(TRUE);
+ }
+ pkg->m_rcontext= nctx;
+ }
+ else
+ nctx= pkg->m_rcontext;
}
-#ifndef DBUG_OFF
- nctx->sp= this;
-#endif
if (params > 0)
{
@@ -2022,7 +2182,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!srp)
{
- my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str);
+ my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, ErrConvDQName(this).ptr());
err_status= TRUE;
break;
}
@@ -2036,7 +2196,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
Item *tmp_item= null_item;
if (!null_item ||
- nctx->set_variable(thd, i, &tmp_item))
+ nctx->set_parameter(thd, i, &tmp_item))
{
DBUG_PRINT("error", ("set variable failed"));
err_status= TRUE;
@@ -2045,7 +2205,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
else
{
- if (nctx->set_variable(thd, i, it_args.ref()))
+ if (nctx->set_parameter(thd, i, it_args.ref()))
{
DBUG_PRINT("error", ("set variable 2 failed"));
err_status= TRUE;
@@ -2070,7 +2230,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->get_stmt_da()->set_overwrite_status(false);
}
- thd_proc_info(thd, "closing tables");
close_thread_tables(thd);
thd_proc_info(thd, 0);
@@ -2092,13 +2251,32 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
DBUG_PRINT("info",(" %.*s: eval args done", (int) m_name.length,
m_name.str));
}
+
save_enable_slow_log= thd->enable_slow_log;
- if (!(m_flags & LOG_SLOW_STATEMENTS) && save_enable_slow_log)
+
+ /*
+ Disable slow log if:
+ - Slow logging is enabled (no change needed)
+ - This is a normal SP (not event log)
+ - If we have not explicitely disabled logging of SP
+ */
+ if (save_enable_slow_log &&
+ ((!(m_flags & LOG_SLOW_STATEMENTS) &&
+ (thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SP))))
{
DBUG_PRINT("info", ("Disabling slow log for the execution"));
thd->enable_slow_log= FALSE;
}
- if (!(m_flags & LOG_GENERAL_LOG) && !(thd->variables.option_bits & OPTION_LOG_OFF))
+
+ /*
+ Disable general log if:
+ - If general log is enabled (no change needed)
+ - This is a normal SP (not event log)
+ - If we have not explicitely disabled logging of SP
+ */
+ if (!(thd->variables.option_bits & OPTION_LOG_OFF) &&
+ (!(m_flags & LOG_GENERAL_LOG) &&
+ (thd->variables.log_disabled_statements & LOG_DISABLE_SP)))
{
DBUG_PRINT("info", ("Disabling general log for the execution"));
save_log_general= true;
@@ -2110,7 +2288,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx= 0;
if (!err_status)
- err_status= set_routine_security_ctx(thd, this, TRUE, &save_security_ctx);
+ err_status= set_routine_security_ctx(thd, this, &save_security_ctx);
#endif
if (!err_status)
@@ -2121,6 +2299,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (save_log_general)
thd->variables.option_bits &= ~OPTION_LOG_OFF;
thd->enable_slow_log= save_enable_slow_log;
+
/*
In the case when we weren't able to employ reuse mechanism for
OUT/INOUT paranmeters, we should reallocate memory. This
@@ -2154,7 +2333,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
DBUG_ASSERT(srp);
- if (srp->set_value(thd, octx, nctx->get_item_addr(i)))
+ if (srp->set_value(thd, octx, nctx->get_variable_addr(i)))
{
DBUG_PRINT("error", ("set value failed"));
err_status= TRUE;
@@ -2162,12 +2341,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
Send_field *out_param_info= new (thd->mem_root) Send_field();
- nctx->get_item(i)->make_field(thd, out_param_info);
+ nctx->get_parameter(i)->make_send_field(thd, out_param_info);
out_param_info->db_name= m_db.str;
out_param_info->table_name= m_name.str;
out_param_info->org_table_name= m_name.str;
- out_param_info->col_name= spvar->name.str;
- out_param_info->org_col_name= spvar->name.str;
+ out_param_info->col_name= spvar->name;
+ out_param_info->org_col_name= spvar->name;
srp->set_out_param_info(out_param_info);
}
@@ -2181,7 +2360,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!save_spcont)
delete octx;
- delete nctx;
+ if (!pkg)
+ delete nctx;
thd->spcont= save_spcont;
thd->utime_after_lock= utime_before_sp_exec;
@@ -2211,34 +2391,23 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
*/
bool
-sp_head::reset_lex(THD *thd)
+sp_head::reset_lex(THD *thd, sp_lex_local *sublex)
{
DBUG_ENTER("sp_head::reset_lex");
- LEX *sublex;
LEX *oldlex= thd->lex;
- sublex= new (thd->mem_root)st_lex_local;
- if (sublex == 0)
- DBUG_RETURN(TRUE);
-
- thd->lex= sublex;
- (void)m_lex.push_front(oldlex);
+ thd->set_local_lex(sublex);
- /* Reset most stuff. */
- lex_start(thd);
-
- /* And keep the SP stuff too */
- sublex->sphead= oldlex->sphead;
- sublex->spcont= oldlex->spcont;
- /* And trigger related stuff too */
- sublex->trg_chistics= oldlex->trg_chistics;
- sublex->trg_table_fields.empty();
- sublex->sp_lex_in_use= FALSE;
+ DBUG_RETURN(m_lex.push_front(oldlex));
+}
- /* Reset part of parser state which needs this. */
- thd->m_parser_state->m_yacc.reset_before_substatement();
- DBUG_RETURN(FALSE);
+bool
+sp_head::reset_lex(THD *thd)
+{
+ DBUG_ENTER("sp_head::reset_lex");
+ sp_lex_local *sublex= new (thd->mem_root) sp_lex_local(thd, thd->lex);
+ DBUG_RETURN(sublex ? reset_lex(thd, sublex) : true);
}
@@ -2246,6 +2415,8 @@ sp_head::reset_lex(THD *thd)
Restore lex during parsing, after we have parsed a sub statement.
@param thd Thread handle
+ @param oldlex The upper level lex we're near to restore to
+ @param sublex The local lex we're near to restore from
@return
@retval TRUE failure
@@ -2253,23 +2424,17 @@ sp_head::reset_lex(THD *thd)
*/
bool
-sp_head::restore_lex(THD *thd)
+sp_head::merge_lex(THD *thd, LEX *oldlex, LEX *sublex)
{
- DBUG_ENTER("sp_head::restore_lex");
- LEX *sublex= thd->lex;
- LEX *oldlex;
+ DBUG_ENTER("sp_head::merge_lex");
sublex->set_trg_event_type_for_tables();
- oldlex= (LEX *)m_lex.pop();
- if (! oldlex)
- DBUG_RETURN(FALSE); // Nothing to restore
-
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
/* If this substatement is unsafe, the entire routine is too. */
- DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags: 0x%x",
- thd->lex->get_stmt_unsafe_flags()));
+ DBUG_PRINT("info", ("sublex->get_stmt_unsafe_flags: 0x%x",
+ sublex->get_stmt_unsafe_flags()));
unsafe_flags|= sublex->get_stmt_unsafe_flags();
/*
@@ -2291,21 +2456,16 @@ sp_head::restore_lex(THD *thd)
/* Merge lists of PS parameters. */
oldlex->param_list.append(&sublex->param_list);
- if (! sublex->sp_lex_in_use)
- {
- sublex->sphead= NULL;
- lex_end(sublex);
- delete sublex;
- }
- thd->lex= oldlex;
DBUG_RETURN(FALSE);
}
/**
Put the instruction on the backpatch list, associated with the label.
*/
+
int
-sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
+sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab,
+ List<bp_t> *list, backpatch_instr_type itype)
{
bp_t *bp= (bp_t *) thd->alloc(sizeof(bp_t));
@@ -2313,13 +2473,52 @@ sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
return 1;
bp->lab= lab;
bp->instr= i;
- return m_backpatch.push_front(bp);
+ bp->instr_type= itype;
+ return list->push_front(bp);
+}
+
+int
+sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
+{
+ return push_backpatch(thd, i, lab, &m_backpatch, GOTO);
+}
+
+int
+sp_head::push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab)
+{
+ uint ip= instructions();
+
+ /*
+ Add cpop/hpop : they will be removed or updated later if target is in
+ the same block or not
+ */
+ sp_instr_hpop *hpop= new (thd->mem_root) sp_instr_hpop(ip++, ctx, 0);
+ if (hpop == NULL || add_instr(hpop))
+ return true;
+ if (push_backpatch(thd, hpop, lab, &m_backpatch_goto, HPOP))
+ return true;
+
+ sp_instr_cpop *cpop= new (thd->mem_root) sp_instr_cpop(ip++, ctx, 0);
+ if (cpop == NULL || add_instr(cpop))
+ return true;
+ if (push_backpatch(thd, cpop, lab, &m_backpatch_goto, CPOP))
+ return true;
+
+ // Add jump with ip=0. IP will be updated when label is found.
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx);
+ if (i == NULL || add_instr(i))
+ return true;
+ if (push_backpatch(thd, i, lab, &m_backpatch_goto, GOTO))
+ return true;
+
+ return false;
}
/**
Update all instruction with this label in the backpatch list to
the current position.
*/
+
void
sp_head::backpatch(sp_label *lab)
{
@@ -2340,44 +2539,95 @@ sp_head::backpatch(sp_label *lab)
DBUG_VOID_RETURN;
}
-/**
- Prepare an instance of Column_definition for field creation
- (fill all necessary attributes).
-
- @param[in] thd Thread handle
- @param[in] lex Yacc parsing context
- @param[out] field_def An instance of create_field to be filled
+void
+sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block)
+{
+ bp_t *bp;
+ uint dest= instructions();
+ List_iterator<bp_t> li(m_backpatch_goto);
- @retval
- FALSE on success
- @retval
- TRUE on error
-*/
+ DBUG_ENTER("sp_head::backpatch_goto");
+ while ((bp= li++))
+ {
+ if (bp->instr->m_ip < lab_begin_block->ip || bp->instr->m_ip > lab->ip)
+ {
+ /*
+ Update only jump target from the beginning of the block where the
+ label is defined.
+ */
+ continue;
+ }
+ if (lex_string_cmp(system_charset_info, &bp->lab->name, &lab->name) == 0)
+ {
+ if (bp->instr_type == GOTO)
+ {
+ DBUG_PRINT("info",
+ ("backpatch_goto: (m_ip %d, label %p <%s>) to dest %d",
+ bp->instr->m_ip, lab, lab->name.str, dest));
+ bp->instr->backpatch(dest, lab->ctx);
+ // Jump resolved, remove from the list
+ li.remove();
+ continue;
+ }
+ if (bp->instr_type == CPOP)
+ {
+ uint n= lab->ctx->diff_cursors(lab_begin_block->ctx, true);
+ if (n == 0)
+ {
+ // Remove cpop instr
+ replace_instr_to_nop(thd,bp->instr->m_ip);
+ }
+ else
+ {
+ // update count of cpop
+ static_cast<sp_instr_cpop*>(bp->instr)->update_count(n);
+ n= 1;
+ }
+ li.remove();
+ continue;
+ }
+ if (bp->instr_type == HPOP)
+ {
+ uint n= lab->ctx->diff_handlers(lab_begin_block->ctx, true);
+ if (n == 0)
+ {
+ // Remove hpop instr
+ replace_instr_to_nop(thd,bp->instr->m_ip);
+ }
+ else
+ {
+ // update count of cpop
+ static_cast<sp_instr_hpop*>(bp->instr)->update_count(n);
+ n= 1;
+ }
+ li.remove();
+ continue;
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
bool
-sp_head::fill_field_definition(THD *thd, LEX *lex,
- Column_definition *field_def)
+sp_head::check_unresolved_goto()
{
- uint unused1= 0;
-
- if (field_def->check(thd))
- return TRUE;
-
- if (field_def->interval_list.elements)
- field_def->interval= create_typelib(mem_root, field_def,
- &field_def->interval_list);
-
- sp_prepare_create_field(thd, field_def);
-
- if (prepare_create_field(field_def, &unused1, HA_CAN_GEOMETRY))
+ DBUG_ENTER("sp_head::check_unresolved_goto");
+ bool has_unresolved_label=false;
+ if (m_backpatch_goto.elements > 0)
{
- return TRUE;
+ List_iterator_fast<bp_t> li(m_backpatch_goto);
+ while (bp_t* bp= li++)
+ {
+ if (bp->instr_type == GOTO)
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "GOTO", bp->lab->name.str);
+ has_unresolved_label=true;
+ }
+ }
}
-
- return FALSE;
+ DBUG_RETURN(has_unresolved_label);
}
-
int
sp_head::new_cont_backpatch(sp_instr_opt_meta *i)
{
@@ -2413,53 +2663,43 @@ sp_head::do_cont_backpatch()
}
}
-void
-sp_head::set_info(longlong created, longlong modified,
- st_sp_chistics *chistics, sql_mode_t sql_mode)
-{
- m_created= created;
- m_modified= modified;
- m_chistics= (st_sp_chistics *) memdup_root(mem_root, (char*) chistics,
- sizeof(*chistics));
- if (m_chistics->comment.length == 0)
- m_chistics->comment.str= 0;
- else
- m_chistics->comment.str= strmake_root(mem_root,
- m_chistics->comment.str,
- m_chistics->comment.length);
- m_sql_mode= sql_mode;
-}
-
-void
-sp_head::set_definer(const char *definer, uint definerlen)
+bool
+sp_head::sp_add_instr_cpush_for_cursors(THD *thd, sp_pcontext *pcontext)
{
- char user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING user_name= { user_name_holder, USERNAME_LENGTH };
-
- char host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH };
-
- if (parse_user(definer, definerlen, user_name.str, &user_name.length,
- host_name.str, &host_name.length) &&
- user_name.length && !host_name.length)
+ for (uint i= 0; i < pcontext->frame_cursor_count(); i++)
{
- // 'user@' -> 'user@%'
- host_name= host_not_specified;
+ const sp_pcursor *c= pcontext->get_cursor_by_local_frame_offset(i);
+ sp_instr_cpush *instr= new (thd->mem_root)
+ sp_instr_cpush(instructions(), pcontext, c->lex(),
+ pcontext->cursor_offset() + i);
+ if (instr == NULL || add_instr(instr))
+ return true;
}
-
- set_definer(&user_name, &host_name);
+ return false;
}
void
-sp_head::set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name)
+sp_head::set_chistics(const st_sp_chistics &chistics)
{
- m_definer_user.str= strmake_root(mem_root, user_name->str, user_name->length);
- m_definer_user.length= user_name->length;
+ m_chistics.set(chistics);
+ if (m_chistics.comment.length == 0)
+ m_chistics.comment.str= 0;
+ else
+ m_chistics.comment.str= strmake_root(mem_root,
+ m_chistics.comment.str,
+ m_chistics.comment.length);
+}
- m_definer_host.str= strmake_root(mem_root, host_name->str, host_name->length);
- m_definer_host.length= host_name->length;
+void
+sp_head::set_info(longlong created, longlong modified,
+ const st_sp_chistics &chistics, sql_mode_t sql_mode)
+{
+ m_created= created;
+ m_modified= modified;
+ set_chistics(chistics);
+ m_sql_mode= sql_mode;
}
@@ -2526,26 +2766,28 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
{
TABLE_LIST tables;
bzero((char*) &tables,sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "proc";
+ tables.db= MYSQL_SCHEMA_NAME;
+ tables.table_name= MYSQL_PROC_NAME;
+ tables.alias= MYSQL_PROC_NAME;
+
*full_access= ((!check_table_access(thd, SELECT_ACL, &tables, FALSE,
1, TRUE) &&
(tables.grant.privilege & SELECT_ACL) != 0) ||
/* Check if user owns the routine. */
- (!strcmp(sp->m_definer_user.str,
+ (!strcmp(sp->m_definer.user.str,
thd->security_ctx->priv_user) &&
- !strcmp(sp->m_definer_host.str,
+ !strcmp(sp->m_definer.host.str,
thd->security_ctx->priv_host)) ||
/* Check if current role or any of the sub-granted roles
own the routine. */
- (sp->m_definer_host.length == 0 &&
- (!strcmp(sp->m_definer_user.str,
+ (sp->m_definer.host.length == 0 &&
+ (!strcmp(sp->m_definer.user.str,
thd->security_ctx->priv_role) ||
check_role_is_granted(thd->security_ctx->priv_role, NULL,
- sp->m_definer_user.str))));
+ sp->m_definer.user.str))));
if (!*full_access)
return check_some_routine_access(thd, sp->m_db.str, sp->m_name.str,
- sp->m_type == TYPE_ENUM_PROCEDURE);
+ sp->m_handler);
return 0;
}
@@ -2554,9 +2796,8 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
Collect metadata for SHOW CREATE statement for stored routines.
@param thd Thread context.
- @param type Stored routine type
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+ @param sph Stored routine handler
+ @param fields Item list to populate
@return Error status.
@retval FALSE on success
@@ -2564,13 +2805,11 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
*/
void
-sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields)
+sp_head::show_create_routine_get_fields(THD *thd, const Sp_handler *sph,
+ List<Item> *fields)
{
- const char *col1_caption= type == TYPE_ENUM_PROCEDURE ?
- "Procedure" : "Function";
-
- const char *col3_caption= type == TYPE_ENUM_PROCEDURE ?
- "Create Procedure" : "Create Function";
+ const char *col1_caption= sph->show_create_routine_col1_caption();
+ const char *col3_caption= sph->show_create_routine_col3_caption();
MEM_ROOT *mem_root= thd->mem_root;
@@ -2617,8 +2856,7 @@ sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields)
Implement SHOW CREATE statement for stored routines.
@param thd Thread context.
- @param type Stored routine type
- (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+ @param sph Stored routine handler
@return Error status.
@retval FALSE on success
@@ -2626,20 +2864,17 @@ sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields)
*/
bool
-sp_head::show_create_routine(THD *thd, int type)
+sp_head::show_create_routine(THD *thd, const Sp_handler *sph)
{
- const char *col1_caption= type == TYPE_ENUM_PROCEDURE ?
- "Procedure" : "Function";
-
- const char *col3_caption= type == TYPE_ENUM_PROCEDURE ?
- "Create Procedure" : "Create Function";
+ const char *col1_caption= sph->show_create_routine_col1_caption();
+ const char *col3_caption= sph->show_create_routine_col3_caption();
bool err_status;
Protocol *protocol= thd->protocol;
List<Item> fields;
- LEX_STRING sql_mode;
+ LEX_CSTRING sql_mode;
bool full_access;
MEM_ROOT *mem_root= thd->mem_root;
@@ -2647,9 +2882,6 @@ sp_head::show_create_routine(THD *thd, int type)
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(TRUE);
@@ -2661,7 +2893,7 @@ sp_head::show_create_routine(THD *thd, int type)
Item_empty_string(thd, col1_caption, NAME_CHAR_LEN),
thd->mem_root);
fields.push_back(new (mem_root)
- Item_empty_string(thd, "sql_mode", sql_mode.length),
+ Item_empty_string(thd, "sql_mode", (uint)sql_mode.length),
thd->mem_root);
{
@@ -2672,7 +2904,7 @@ sp_head::show_create_routine(THD *thd, int type)
Item_empty_string *stmt_fld=
new (mem_root) Item_empty_string(thd, col3_caption,
- MY_MAX(m_defstr.length, 1024));
+ (uint)MY_MAX(m_defstr.length, 1024));
stmt_fld->maybe_null= TRUE;
@@ -2744,10 +2976,90 @@ int sp_head::add_instr(sp_instr *instr)
entire stored procedure, as their life span is equal.
*/
instr->mem_root= &main_mem_root;
+ instr->m_lineno= m_thd->m_parser_state->m_lip.yylineno;
return insert_dynamic(&m_instr, (uchar*)&instr);
}
+bool sp_head::add_instr_jump(THD *thd, sp_pcontext *spcont)
+{
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(), spcont);
+ return i == NULL || add_instr(i);
+}
+
+
+bool sp_head::add_instr_jump(THD *thd, sp_pcontext *spcont, uint dest)
+{
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(),
+ spcont, dest);
+ return i == NULL || add_instr(i);
+}
+
+
+bool sp_head::add_instr_jump_forward_with_backpatch(THD *thd,
+ sp_pcontext *spcont,
+ sp_label *lab)
+{
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(), spcont);
+ if (i == NULL || add_instr(i))
+ return true;
+ push_backpatch(thd, i, lab);
+ return false;
+}
+
+
+bool sp_head::add_instr_freturn(THD *thd, sp_pcontext *spcont,
+ Item *item, LEX *lex)
+{
+ sp_instr_freturn *i= new (thd->mem_root)
+ sp_instr_freturn(instructions(), spcont, item,
+ m_return_field_def.type_handler(), thd->lex);
+ if (i == NULL || add_instr(i))
+ return true;
+ m_flags|= sp_head::HAS_RETURN;
+ return false;
+}
+
+
+bool sp_head::add_instr_preturn(THD *thd, sp_pcontext *spcont)
+{
+ sp_instr_preturn *i= new (thd->mem_root)
+ sp_instr_preturn(instructions(), spcont);
+ if (i == NULL || add_instr(i))
+ return true;
+ return false;
+}
+
+
+/*
+ Replace an instruction at position to "no operation".
+
+ @param thd - use mem_root of this THD for "new".
+ @param ip - position of the operation
+ @returns - true on error, false on success
+
+ When we need to remove an instruction that during compilation
+ appeared to be useless (typically as useless jump), we replace
+ it to a jump to exactly the next instruction.
+ Such jumps are later removed during sp_head::optimize().
+
+ QQ: Perhaps we need a dedicated sp_instr_nop for this purpose.
+*/
+
+bool sp_head::replace_instr_to_nop(THD *thd, uint ip)
+{
+ sp_instr *instr= get_instr(ip);
+ sp_instr_jump *nop= new (thd->mem_root) sp_instr_jump(instr->m_ip,
+ instr->m_ctx,
+ instr->m_ip + 1);
+ if (!nop)
+ return true;
+ delete instr;
+ set_dynamic(&m_instr, (uchar *) &nop, ip);
+ return false;
+}
+
+
/**
Do some minimal optimization of the code:
-# Mark used instructions
@@ -2858,6 +3170,7 @@ sp_head::opt_mark()
@return
0 if ok, !=0 on error.
*/
+
int
sp_head::show_routine_code(THD *thd)
{
@@ -2905,7 +3218,7 @@ sp_head::show_routine_code(THD *thd)
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, tmp);
}
protocol->prepare_for_resend();
- protocol->store((longlong)ip);
+ protocol->store_long(ip);
buffer.set("", 0, system_charset_info);
i->print(&buffer);
@@ -3006,7 +3319,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
res= check_dependencies_in_with_clauses(m_lex->with_clauses_list) ||
instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
- if (!res)
+ if (likely(!res))
{
res= instr->exec_core(thd, nextp);
DBUG_PRINT("info",("exec_core returned: %d", res));
@@ -3026,7 +3339,6 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
thd->get_stmt_da()->set_overwrite_status(false);
}
- thd_proc_info(thd, "closing tables");
close_thread_tables(thd);
thd_proc_info(thd, 0);
@@ -3067,7 +3379,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
Update the state of the active arena if no errors on
open_tables stage.
*/
- if (!res || !thd->is_error() ||
+ if (likely(!res) || likely(!thd->is_error()) ||
(thd->get_stmt_da()->sql_errno() != ER_CANT_REOPEN_TABLE &&
thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE &&
thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE &&
@@ -3096,6 +3408,25 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
}
+int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp,
+ bool open_tables,
+ sp_instr *instr)
+{
+ Query_arena *old_arena= thd->stmt_arena;
+ /*
+ Get the Query_arena from the cursor statement LEX, which contains
+ the free_list of the query, so new items (if any) are stored in
+ the right free_list, and we can cleanup after each cursor operation,
+ e.g. open or cursor_copy_struct (for cursor%ROWTYPE variables).
+ */
+ thd->stmt_arena= m_lex->query_arena();
+ int res= reset_lex_and_exec_core(thd, nextp, open_tables, instr);
+ cleanup_items(thd->stmt_arena->free_list);
+ thd->stmt_arena= old_arena;
+ return res;
+}
+
+
/*
sp_instr class functions
*/
@@ -3141,14 +3472,20 @@ int
sp_instr_stmt::execute(THD *thd, uint *nextp)
{
int res;
+ bool save_enable_slow_log;
+ const CSET_STRING query_backup= thd->query_string;
+ Sub_statement_state backup_state;
DBUG_ENTER("sp_instr_stmt::execute");
DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
- 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);
#endif
+
+ save_enable_slow_log= thd->enable_slow_log;
+ thd->store_slow_query_state(&backup_state);
+
if (!(res= alloc_query(thd, m_query.str, m_query.length)) &&
!(res=subst_spvars(thd, this, &m_query)))
{
@@ -3161,6 +3498,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
if (query_cache_send_result_to_client(thd, thd->query(),
thd->query_length()) <= 0)
{
+ thd->reset_slow_query_state();
res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
bool log_slow= !res && thd->enable_slow_log;
@@ -3180,6 +3518,15 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
if (log_slow)
log_slow_statement(thd);
+
+ /*
+ Restore enable_slow_log, that can be changed by a admin or call
+ command
+ */
+ thd->enable_slow_log= save_enable_slow_log;
+
+ /* Add the number of rows to thd for the 'call' statistics */
+ thd->add_slow_query_state(&backup_state);
}
else
{
@@ -3194,12 +3541,13 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
thd->set_query(query_backup);
thd->query_name_consts= 0;
- if (!thd->is_error())
+ if (likely(!thd->is_error()))
{
res= 0;
thd->get_stmt_da()->reset_diagnostics_area();
}
}
+
DBUG_RETURN(res || thd->is_error());
}
@@ -3207,7 +3555,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
void
sp_instr_stmt::print(String *str)
{
- uint i, len;
+ size_t i, len;
/* stmt CMD "..." */
if (str->reserve(SP_STMT_PRINT_MAXLEN+SP_INSTR_UINT_MAXLEN+8))
@@ -3241,7 +3589,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->get_db(),
&thd->security_ctx->priv_user[0],
(char *)thd->security_ctx->host_or_ip,
3);
@@ -3266,23 +3614,17 @@ sp_instr_set::execute(THD *thd, uint *nextp)
}
-int
-sp_instr_set::exec_core(THD *thd, uint *nextp)
+sp_rcontext *sp_instr_set::get_rcontext(THD *thd) const
{
- int res= thd->spcont->set_variable(thd, m_offset, &m_value);
+ return m_rcontext_handler->get_rcontext(thd->spcont);
+}
- if (res)
- {
- /* Failed to evaluate the value. Reset the variable to NULL. */
- if (thd->spcont->set_variable(thd, m_offset, 0))
- {
- /* If this also failed, let's abort. */
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
- }
- }
+int
+sp_instr_set::exec_core(THD *thd, uint *nextp)
+{
+ int res= get_rcontext(thd)->set_variable(thd, m_offset, &m_value);
delete_explain_query(thd->lex);
-
*nextp = m_ip+1;
return res;
}
@@ -3291,18 +3633,20 @@ void
sp_instr_set::print(String *str)
{
/* set name@offset ... */
- int rsrv = SP_INSTR_UINT_MAXLEN+6;
+ size_t rsrv = SP_INSTR_UINT_MAXLEN+6;
sp_variable *var = m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
/* 'var' should always be non-null, but just in case... */
if (var)
- rsrv+= var->name.length;
+ rsrv+= var->name.length + prefix->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix->str, prefix->length);
if (var)
{
- str->qs_append(var->name.str, var->name.length);
+ str->qs_append(&var->name);
str->qs_append('@');
}
str->qs_append(m_offset);
@@ -3313,6 +3657,100 @@ sp_instr_set::print(String *str)
/*
+ sp_instr_set_field class functions
+*/
+
+int
+sp_instr_set_row_field::exec_core(THD *thd, uint *nextp)
+{
+ int res= get_rcontext(thd)->set_variable_row_field(thd, m_offset,
+ m_field_offset,
+ &m_value);
+ delete_explain_query(thd->lex);
+ *nextp= m_ip + 1;
+ return res;
+}
+
+
+void
+sp_instr_set_row_field::print(String *str)
+{
+ /* set name@offset[field_offset] ... */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3;
+ sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ DBUG_ASSERT(var);
+ DBUG_ASSERT(var->field_def.is_row());
+ const Column_definition *def=
+ var->field_def.row_field_definitions()->elem(m_field_offset);
+ DBUG_ASSERT(def);
+
+ rsrv+= var->name.length + def->field_name.length + prefix->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
+ str->qs_append(&var->name);
+ str->qs_append('.');
+ str->qs_append(&def->field_name);
+ str->qs_append('@');
+ str->qs_append(m_offset);
+ str->qs_append('[');
+ str->qs_append(m_field_offset);
+ str->qs_append(']');
+ str->qs_append(' ');
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+
+/*
+ sp_instr_set_field_by_name class functions
+*/
+
+int
+sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
+{
+ int res= get_rcontext(thd)->set_variable_row_field_by_name(thd, m_offset,
+ m_field_name,
+ &m_value);
+ delete_explain_query(thd->lex);
+ *nextp= m_ip + 1;
+ return res;
+}
+
+
+void
+sp_instr_set_row_field_by_name::print(String *str)
+{
+ /* set name.field@offset["field"] ... */
+ size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
+ sp_variable *var= m_ctx->find_variable(m_offset);
+ const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
+ DBUG_ASSERT(var);
+ DBUG_ASSERT(var->field_def.is_table_rowtype_ref() ||
+ var->field_def.is_cursor_rowtype_ref());
+
+ rsrv+= var->name.length + 2 * m_field_name.length + prefix->length;
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("set "));
+ str->qs_append(prefix);
+ str->qs_append(&var->name);
+ str->qs_append('.');
+ str->qs_append(&m_field_name);
+ str->qs_append('@');
+ str->qs_append(m_offset);
+ str->qs_append("[\"",2);
+ str->qs_append(&m_field_name);
+ str->qs_append("\"]",2);
+ str->qs_append(' ');
+ m_value->print(str, enum_query_type(QT_ORDINARY |
+ QT_ITEM_ORIGINAL_FUNC_NULLIF));
+}
+
+
+/*
sp_instr_set_trigger_field class functions
*/
@@ -3441,7 +3879,7 @@ sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp)
Item *it;
int res;
- it= sp_prepare_func_item(thd, &m_expr);
+ it= thd->sp_prepare_func_item(&m_expr);
if (! it)
{
res= -1;
@@ -3537,8 +3975,21 @@ sp_instr_freturn::exec_core(THD *thd, uint *nextp)
That means, Diagnostics Area should be clean before its execution.
*/
- Diagnostics_area *da= thd->get_stmt_da();
- da->clear_warning_info(da->warning_info_id());
+ if (!(thd->variables.sql_mode & MODE_ORACLE))
+ {
+ /*
+ Don't clean warnings in ORACLE mode,
+ as they are needed for SQLCODE and SQLERRM:
+ BEGIN
+ SELECT a INTO a FROM t1;
+ RETURN 'No exception ' || SQLCODE || ' ' || SQLERRM;
+ EXCEPTION WHEN NO_DATA_FOUND THEN
+ RETURN 'Exception ' || SQLCODE || ' ' || SQLERRM;
+ END;
+ */
+ Diagnostics_area *da= thd->get_stmt_da();
+ da->clear_warning_info(da->warning_info_id());
+ }
/*
Change <next instruction pointer>, so that this will be the last
@@ -3565,7 +4016,7 @@ sp_instr_freturn::print(String *str)
if (str->reserve(1024+8+32)) // Add some for the expr. too
return;
str->qs_append(STRING_WITH_LEN("freturn "));
- str->qs_append((uint)m_type);
+ str->qs_append(m_type_handler->name().ptr());
str->qs_append(' ');
m_value->print(str, enum_query_type(QT_ORDINARY |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
@@ -3580,7 +4031,7 @@ sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_hpush_jump::execute");
- int ret= thd->spcont->push_handler(m_handler, m_ip + 1);
+ int ret= thd->spcont->push_handler(this);
*nextp= m_dest;
@@ -3739,21 +4190,23 @@ sp_instr_cpush::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_cpush::execute");
- int ret= thd->spcont->push_cursor(thd, &m_lex_keeper, this);
+ sp_cursor::reset(thd, &m_lex_keeper);
+ m_lex_keeper.disable_query_cache();
+ thd->spcont->push_cursor(this);
*nextp= m_ip+1;
- DBUG_RETURN(ret);
+ DBUG_RETURN(false);
}
void
sp_instr_cpush::print(String *str)
{
- const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
/* cpush name@offset */
- uint rsrv= SP_INSTR_UINT_MAXLEN+7;
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+7;
if (cursor_name)
rsrv+= cursor_name->length;
@@ -3777,7 +4230,7 @@ int
sp_instr_cpop::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_cpop::execute");
- thd->spcont->pop_cursors(m_count);
+ thd->spcont->pop_cursors(thd, m_count);
*nextp= m_ip+1;
DBUG_RETURN(0);
}
@@ -3819,19 +4272,7 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
else
{
sp_lex_keeper *lex_keeper= c->get_lex_keeper();
- Query_arena *old_arena= thd->stmt_arena;
-
- /*
- Get the Query_arena from the cpush instruction, which contains
- the free_list of the query, so new items (if any) are stored in
- the right free_list, and we can cleanup after each open.
- */
- thd->stmt_arena= c->get_instr();
- res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this);
- /* Cleanup the query's items */
- if (thd->stmt_arena->free_list)
- cleanup_items(thd->stmt_arena->free_list);
- thd->stmt_arena= old_arena;
+ res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this);
/* TODO: Assert here that we either have an error or a cursor */
}
DBUG_RETURN(res);
@@ -3850,10 +4291,10 @@ sp_instr_copen::exec_core(THD *thd, uint *nextp)
void
sp_instr_copen::print(String *str)
{
- const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
/* copen name@offset */
- uint rsrv= SP_INSTR_UINT_MAXLEN+7;
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+7;
if (cursor_name)
rsrv+= cursor_name->length;
@@ -3892,10 +4333,10 @@ sp_instr_cclose::execute(THD *thd, uint *nextp)
void
sp_instr_cclose::print(String *str)
{
- const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
/* cclose name@offset */
- uint rsrv= SP_INSTR_UINT_MAXLEN+8;
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+8;
if (cursor_name)
rsrv+= cursor_name->length;
@@ -3923,7 +4364,7 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp)
Query_arena backup_arena;
DBUG_ENTER("sp_instr_cfetch::execute");
- res= c ? c->fetch(thd, &m_varlist) : -1;
+ res= c ? c->fetch(thd, &m_varlist, m_error_on_no_data) : -1;
*nextp= m_ip+1;
DBUG_RETURN(res);
@@ -3935,10 +4376,10 @@ sp_instr_cfetch::print(String *str)
{
List_iterator_fast<sp_variable> li(m_varlist);
sp_variable *pv;
- const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+ const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor);
/* cfetch name@offset vars... */
- uint rsrv= SP_INSTR_UINT_MAXLEN+8;
+ size_t rsrv= SP_INSTR_UINT_MAXLEN+8;
if (cursor_name)
rsrv+= cursor_name->length;
@@ -3956,12 +4397,131 @@ sp_instr_cfetch::print(String *str)
if (str->reserve(pv->name.length+SP_INSTR_UINT_MAXLEN+2))
return;
str->qs_append(' ');
- str->qs_append(pv->name.str, pv->name.length);
+ str->qs_append(&pv->name);
str->qs_append('@');
str->qs_append(pv->offset);
}
}
+int
+sp_instr_agg_cfetch::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_agg_cfetch::execute");
+ int res= 0;
+ if (!thd->spcont->instr_ptr)
+ {
+ *nextp= m_ip+1;
+ thd->spcont->instr_ptr= m_ip + 1;
+ }
+ else if (!thd->spcont->pause_state)
+ thd->spcont->pause_state= TRUE;
+ else
+ {
+ thd->spcont->pause_state= FALSE;
+ if (thd->server_status == SERVER_STATUS_LAST_ROW_SENT)
+ {
+ my_message(ER_SP_FETCH_NO_DATA,
+ ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0));
+ res= -1;
+ thd->spcont->quit_func= TRUE;
+ }
+ else
+ *nextp= m_ip + 1;
+ }
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_agg_cfetch::print(String *str)
+{
+
+ uint rsrv= SP_INSTR_UINT_MAXLEN+11;
+
+ if (str->reserve(rsrv))
+ return;
+ str->qs_append(STRING_WITH_LEN("agg_cfetch"));
+}
+
+/*
+ sp_instr_cursor_copy_struct class functions
+*/
+
+/**
+ This methods processes cursor %ROWTYPE declarations, e.g.:
+ CURSOR cur IS SELECT * FROM t1;
+ rec cur%ROWTYPE;
+ and does the following:
+ - opens the cursor without copying data (materialization).
+ - copies the cursor structure to the associated %ROWTYPE variable.
+*/
+
+int
+sp_instr_cursor_copy_struct::exec_core(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cursor_copy_struct::exec_core");
+ int ret= 0;
+ Item_field_row *row= (Item_field_row*) thd->spcont->get_variable(m_var);
+ DBUG_ASSERT(row->type_handler() == &type_handler_row);
+
+ /*
+ Copy structure only once. If the cursor%ROWTYPE variable is declared
+ inside a LOOP block, it gets its structure on the first loop interation
+ and remembers the structure for all consequent loop iterations.
+ It we recreated the structure on every iteration, we would get
+ potential memory leaks, and it would be less efficient.
+ */
+ if (!row->arguments())
+ {
+ sp_cursor tmp(thd, &m_lex_keeper, true);
+ // Open the cursor without copying data
+ if (!(ret= tmp.open(thd)))
+ {
+ Row_definition_list defs;
+ /*
+ Create row elements on the caller arena.
+ It's the same arena that was used during sp_rcontext::create().
+ This puts cursor%ROWTYPE elements on the same mem_root
+ where explicit ROW elements and table%ROWTYPE reside:
+ - tmp.export_structure() allocates new Spvar_definition instances
+ and their components (such as TYPELIBs).
+ - row->row_create_items() creates new Item_field instances.
+ They all are created on the same mem_root.
+ */
+ Query_arena current_arena;
+ thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
+ if (!(ret= tmp.export_structure(thd, &defs)))
+ row->row_create_items(thd, &defs);
+ thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
+ tmp.close(thd);
+ }
+ }
+ *nextp= m_ip + 1;
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_instr_cursor_copy_struct::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cursor_copy_struct::execute");
+ int ret= m_lex_keeper.cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this);
+ DBUG_RETURN(ret);
+}
+
+
+void
+sp_instr_cursor_copy_struct::print(String *str)
+{
+ sp_variable *var= m_ctx->find_variable(m_var);
+ const LEX_CSTRING *name= m_ctx->find_cursor(m_cursor);
+ str->append(STRING_WITH_LEN("cursor_copy_struct "));
+ str->append(name);
+ str->append(' ');
+ str->append(&var->name);
+ str->append('@');
+ str->append_ulonglong(m_var);
+}
+
/*
sp_instr_error class functions
@@ -4086,7 +4646,7 @@ typedef struct st_sp_table
db_name\0table_name\0 - for temporary tables
*/
LEX_STRING qname;
- uint db_length, table_name_length;
+ size_t db_length, table_name_length;
bool temp; /* true if corresponds to a temporary table */
thr_lock_type lock_type; /* lock type used for prelocking */
uint lock_count;
@@ -4127,7 +4687,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
{
SP_TABLE *tab;
- if (lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE &&
+ if ((lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE ||
+ lex_for_tmp_check->sql_command == SQLCOM_DROP_SEQUENCE) &&
lex_for_tmp_check->tmp_table())
return TRUE;
@@ -4153,12 +4714,12 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
uint temp_table_key_length;
tname.length(0);
- tname.append(table->db, table->db_length);
+ tname.append(&table->db);
tname.append('\0');
- tname.append(table->table_name, table->table_name_length);
+ tname.append(&table->table_name);
tname.append('\0');
temp_table_key_length= tname.length();
- tname.append(table->alias);
+ tname.append(&table->alias);
tname.append('\0');
/*
@@ -4191,7 +4752,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
{
if (!(tab= (SP_TABLE *)thd->calloc(sizeof(SP_TABLE))))
return FALSE;
- if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE &&
+ if ((lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE ||
+ lex_for_tmp_check->sql_command == SQLCOM_CREATE_SEQUENCE) &&
lex_for_tmp_check->query_tables == table &&
lex_for_tmp_check->tmp_table())
{
@@ -4203,8 +4765,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
tab->qname.str= (char*) thd->memdup(tname.ptr(), tab->qname.length);
if (!tab->qname.str)
return FALSE;
- tab->table_name_length= table->table_name_length;
- tab->db_length= table->db_length;
+ tab->table_name_length= table->table_name.length;
+ tab->db_length= table->db.length;
tab->lock_type= table->lock_type;
tab->lock_count= tab->query_lock_count= 1;
tab->trg_event_map= table->trg_event_map;
@@ -4261,8 +4823,8 @@ sp_head::add_used_tables_to_table_list(THD *thd,
for (i=0 ; i < m_sptabs.records ; i++)
{
char *tab_buff, *key_buff;
- TABLE_LIST *table;
SP_TABLE *stab= (SP_TABLE*) my_hash_element(&m_sptabs, i);
+ LEX_CSTRING db_name;
if (stab->temp)
continue;
@@ -4272,15 +4834,26 @@ sp_head::add_used_tables_to_table_list(THD *thd,
stab->qname.length)))
DBUG_RETURN(FALSE);
+ db_name.str= key_buff;
+ db_name.length= stab->db_length;
+
+
for (uint j= 0; j < stab->lock_count; j++)
{
- table= (TABLE_LIST *)tab_buff;
- table->init_one_table_for_prelocking(key_buff, stab->db_length,
- key_buff + stab->db_length + 1, stab->table_name_length,
- key_buff + stab->db_length + stab->table_name_length + 2,
- stab->lock_type, true, belong_to_view, stab->trg_event_map,
- query_tables_last_ptr);
-
+ TABLE_LIST *table= (TABLE_LIST *)tab_buff;
+ LEX_CSTRING table_name= { key_buff + stab->db_length + 1,
+ stab->table_name_length };
+ LEX_CSTRING alias= { table_name.str + table_name.length + 1,
+ strlen(table_name.str + table_name.length + 1) };
+
+ table->init_one_table_for_prelocking(&db_name,
+ &table_name,
+ &alias,
+ stab->lock_type,
+ TABLE_LIST::PRELOCK_ROUTINE,
+ belong_to_view,
+ stab->trg_event_map,
+ query_tables_last_ptr);
tab_buff+= ALIGN_SIZE(sizeof(TABLE_LIST));
result= TRUE;
}
@@ -4300,7 +4873,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
- const char *db, const char *name,
+ const LEX_CSTRING *db, const LEX_CSTRING *name,
thr_lock_type locktype,
enum_mdl_type mdl_type)
{
@@ -4308,17 +4881,280 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST))))
return NULL;
- table->db_length= strlen(db);
- table->db= thd->strmake(db, table->db_length);
- table->table_name_length= strlen(name);
- table->table_name= thd->strmake(name, table->table_name_length);
- table->alias= thd->strdup(name);
+ if (!thd->make_lex_string(&table->db, db->str, db->length) ||
+ !thd->make_lex_string(&table->table_name, name->str, name->length) ||
+ !thd->make_lex_string(&table->alias, name->str, name->length))
+ return NULL;
+
table->lock_type= locktype;
table->select_lex= lex->current_select;
table->cacheable_table= 1;
- table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
+ table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str,
mdl_type, MDL_TRANSACTION);
lex->add_to_query_tables(table);
return table;
}
+
+
+Item *sp_head::adjust_assignment_source(THD *thd, Item *val, Item *val2)
+{
+ return val ? val : val2 ? val2 : new (thd->mem_root) Item_null(thd);
+}
+
+/**
+ Helper action for a SET statement.
+ Used to push a SP local variable into the assignment list.
+
+ @param var_type the SP local variable
+ @param val the value being assigned to the variable
+
+ @return TRUE if error, FALSE otherwise.
+*/
+
+bool
+sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv, Item *val, LEX *lex,
+ bool responsible_to_free_lex)
+{
+ if (!(val= adjust_assignment_source(thd, val, spv->default_value)))
+ return true;
+
+ sp_instr_set *sp_set= new (thd->mem_root)
+ sp_instr_set(instructions(), spcont, rh,
+ spv->offset, val, lex,
+ responsible_to_free_lex);
+
+ return sp_set == NULL || add_instr(sp_set);
+}
+
+
+/**
+ Similar to set_local_variable(), but for ROW variable fields.
+*/
+
+bool
+sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv, uint field_idx,
+ Item *val, LEX *lex)
+{
+ if (!(val= adjust_assignment_source(thd, val, NULL)))
+ return true;
+
+ sp_instr_set_row_field *sp_set= new (thd->mem_root)
+ sp_instr_set_row_field(instructions(),
+ spcont, rh,
+ spv->offset,
+ field_idx, val,
+ lex, true);
+ return sp_set == NULL || add_instr(sp_set);
+}
+
+
+bool
+sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
+ const Sp_rcontext_handler *rh,
+ sp_variable *spv,
+ const LEX_CSTRING *field_name,
+ Item *val, LEX *lex)
+{
+ if (!(val= adjust_assignment_source(thd, val, NULL)))
+ return true;
+
+ sp_instr_set_row_field_by_name *sp_set=
+ new (thd->mem_root) sp_instr_set_row_field_by_name(instructions(),
+ spcont, rh,
+ spv->offset,
+ *field_name,
+ val,
+ lex, true);
+ return sp_set == NULL || add_instr(sp_set);
+}
+
+
+bool sp_head::add_open_cursor(THD *thd, sp_pcontext *spcont, uint offset,
+ sp_pcontext *param_spcont,
+ List<sp_assignment_lex> *parameters)
+{
+ /*
+ The caller must make sure that the number of formal parameters matches
+ the number of actual parameters.
+ */
+ DBUG_ASSERT((param_spcont ? param_spcont->context_var_count() : 0) ==
+ (parameters ? parameters->elements : 0));
+
+ if (parameters &&
+ add_set_cursor_param_variables(thd, param_spcont, parameters))
+ return true;
+
+ sp_instr_copen *i= new (thd->mem_root)
+ sp_instr_copen(instructions(), spcont, offset);
+ return i == NULL || add_instr(i);
+}
+
+
+bool sp_head::add_for_loop_open_cursor(THD *thd, sp_pcontext *spcont,
+ sp_variable *index,
+ const sp_pcursor *pcursor, uint coffset,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters)
+{
+ if (parameters &&
+ add_set_for_loop_cursor_param_variables(thd, pcursor->param_context(),
+ param_lex, parameters))
+ return true;
+
+ sp_instr *instr_copy_struct=
+ new (thd->mem_root) sp_instr_cursor_copy_struct(instructions(),
+ spcont, coffset,
+ pcursor->lex(),
+ index->offset);
+ if (instr_copy_struct == NULL || add_instr(instr_copy_struct))
+ return true;
+
+ sp_instr_copen *instr_copen=
+ new (thd->mem_root) sp_instr_copen(instructions(), spcont, coffset);
+ if (instr_copen == NULL || add_instr(instr_copen))
+ return true;
+
+ sp_instr_cfetch *instr_cfetch=
+ new (thd->mem_root) sp_instr_cfetch(instructions(),
+ spcont, coffset, false);
+ if (instr_cfetch == NULL || add_instr(instr_cfetch))
+ return true;
+ instr_cfetch->add_to_varlist(index);
+ return false;
+}
+
+
+bool
+sp_head::add_set_for_loop_cursor_param_variables(THD *thd,
+ sp_pcontext *param_spcont,
+ sp_assignment_lex *param_lex,
+ Item_args *parameters)
+{
+ DBUG_ASSERT(param_spcont->context_var_count() == parameters->argument_count());
+ for (uint idx= 0; idx < parameters->argument_count(); idx ++)
+ {
+ /*
+ param_lex is shared between multiple items (cursor parameters).
+ Only the last sp_instr_set is responsible for freeing param_lex.
+ See more comments in LEX::sp_for_loop_cursor_declarations in sql_lex.cc.
+ */
+ bool last= idx + 1 == parameters->argument_count();
+ sp_variable *spvar= param_spcont->get_context_variable(idx);
+ if (set_local_variable(thd, param_spcont,
+ &sp_rcontext_handler_local,
+ spvar, parameters->arguments()[idx],
+ param_lex, last))
+ return true;
+ }
+ return false;
+}
+
+
+bool sp_head::spvar_fill_row(THD *thd,
+ sp_variable *spvar,
+ Row_definition_list *defs)
+{
+ spvar->field_def.set_row_field_definitions(defs);
+ spvar->field_def.field_name= spvar->name;
+ if (fill_spvar_definition(thd, &spvar->field_def))
+ return true;
+ row_fill_field_definitions(thd, defs);
+ return false;
+}
+
+
+bool sp_head::spvar_fill_type_reference(THD *thd,
+ sp_variable *spvar,
+ const LEX_CSTRING &table,
+ const LEX_CSTRING &col)
+{
+ Qualified_column_ident *ref;
+ if (!(ref= new (thd->mem_root) Qualified_column_ident(&table, &col)))
+ return true;
+ fill_spvar_using_type_reference(spvar, ref);
+ return false;
+}
+
+
+bool sp_head::spvar_fill_type_reference(THD *thd,
+ sp_variable *spvar,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table,
+ const LEX_CSTRING &col)
+{
+ Qualified_column_ident *ref;
+ if (!(ref= new (thd->mem_root) Qualified_column_ident(thd, &db, &table, &col)))
+ return true;
+ fill_spvar_using_type_reference(spvar, ref);
+ return false;
+}
+
+
+bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
+ sp_variable *spvar,
+ const LEX_CSTRING &table)
+{
+ Table_ident *ref;
+ if (!(ref= new (thd->mem_root) Table_ident(&table)))
+ return true;
+ fill_spvar_using_table_rowtype_reference(thd, spvar, ref);
+ return false;
+}
+
+
+bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
+ sp_variable *spvar,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table)
+{
+ Table_ident *ref;
+ if (!(ref= new (thd->mem_root) Table_ident(thd, &db, &table, false)))
+ return true;
+ fill_spvar_using_table_rowtype_reference(thd, spvar, ref);
+ return false;
+}
+
+
+/*
+ In Oracle mode stored routines have an optional name
+ at the end of a declaration:
+ PROCEDURE p1 AS
+ BEGIN
+ NULL
+ END p1;
+ Check that the first p1 and the last p1 match.
+*/
+
+bool sp_head::check_package_routine_end_name(const LEX_CSTRING &end_name) const
+{
+ LEX_CSTRING non_qualified_name= m_name;
+ const char *errpos;
+ size_t ofs;
+ if (!end_name.length)
+ return false; // No end name
+ if (!(errpos= strrchr(m_name.str, '.')))
+ {
+ errpos= m_name.str;
+ goto err;
+ }
+ errpos++;
+ ofs= errpos - m_name.str;
+ non_qualified_name.str+= ofs;
+ non_qualified_name.length-= ofs;
+ if (Sp_handler::eq_routine_name(end_name, non_qualified_name))
+ return false;
+err:
+ my_error(ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0), end_name.str, errpos);
+ return true;
+}
+
+
+ulong sp_head::sp_cache_version() const
+{
+ return m_parent ? m_parent->sp_cache_version() : m_sp_cache_version;
+}