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.cc361
1 files changed, 333 insertions, 28 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index cdb5e256e46..c088dc6ba12 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -192,6 +192,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:
@@ -211,11 +213,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:
@@ -260,6 +265,8 @@ 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:
@@ -276,6 +283,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:
@@ -502,9 +511,10 @@ sp_head::operator delete(void *ptr, size_t size) throw()
}
-sp_head::sp_head(const Sp_handler *sph)
+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),
@@ -529,6 +539,9 @@ sp_head::sp_head(const Sp_handler *sph)
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;
@@ -543,6 +556,7 @@ sp_head::sp_head(const Sp_handler *sph)
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);
@@ -551,12 +565,169 @@ sp_head::sp_head(const Sp_handler *sph)
}
+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)
+{
+ init_sp_name(name);
+}
+
+
+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)
+{
+ /*
+ Check that all routines declared in CREATE PACKAGE
+ have implementations in CREATE PACKAGE BODY.
+ */
+ 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;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+
void
sp_head::init(LEX *lex)
{
DBUG_ENTER("sp_head::init");
- lex->spcont= m_pcont= new sp_pcontext();
+ lex->spcont= m_pcont;
if (!lex->spcont)
DBUG_VOID_RETURN;
@@ -566,7 +737,6 @@ sp_head::init(LEX *lex)
types of stored procedures to simplify reset_lex()/restore_lex() code.
*/
lex->trg_table_fields.empty();
- my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
DBUG_VOID_RETURN;
}
@@ -584,9 +754,6 @@ sp_head::init_sp_name(const sp_name *spname)
/* We have to copy strings to get them into the right memroot. */
Database_qualified_name::copy(&main_mem_root, spname->m_db, spname->m_name);
m_explicit_name= spname->m_explicit_name;
-
- spname->make_qname(&main_mem_root, &m_qname);
-
DBUG_VOID_RETURN;
}
@@ -681,6 +848,17 @@ 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).
@@ -992,6 +1170,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
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)
{
@@ -1312,6 +1492,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
err_status|= mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE);
}
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,
@@ -1385,8 +1567,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
statement that 'may' affect this.
*/
if (*save_ctx &&
- check_routine_access(thd, EXECUTE_ACL,
- &sp->m_db, &sp->m_name, sp->m_handler, false))
+ sp->check_execute_access(thd))
{
sp->m_security_ctx.restore_security_context(thd, *save_ctx);
*save_ctx= 0;
@@ -1400,9 +1581,10 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
bool sp_head::check_execute_access(THD *thd) const
{
- return check_routine_access(thd, EXECUTE_ACL,
- &m_db, &m_name,
- m_handler, false);
+ return m_parent ? m_parent->check_execute_access(thd) :
+ check_routine_access(thd, EXECUTE_ACL,
+ &m_db, &m_name,
+ m_handler, false);
}
@@ -1593,6 +1775,42 @@ 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.
@@ -1642,6 +1860,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
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.
@@ -1869,9 +2090,13 @@ 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",
@@ -1895,11 +2120,32 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->spcont->callers_arena= thd;
}
- if (!(nctx= rcontext_create(thd, NULL, args)))
+ 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;
}
if (params > 0)
@@ -2105,7 +2351,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;
@@ -3366,10 +3613,16 @@ sp_instr_set::execute(THD *thd, uint *nextp)
}
+sp_rcontext *sp_instr_set::get_rcontext(THD *thd) const
+{
+ return m_rcontext_handler->get_rcontext(thd->spcont);
+}
+
+
int
sp_instr_set::exec_core(THD *thd, uint *nextp)
{
- int res= thd->spcont->set_variable(thd, m_offset, &m_value);
+ int res= get_rcontext(thd)->set_variable(thd, m_offset, &m_value);
delete_explain_query(thd->lex);
*nextp = m_ip+1;
return res;
@@ -3381,13 +3634,15 @@ sp_instr_set::print(String *str)
/* set name@offset ... */
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);
@@ -3407,8 +3662,9 @@ sp_instr_set::print(String *str)
int
sp_instr_set_row_field::exec_core(THD *thd, uint *nextp)
{
- int res= thd->spcont->set_variable_row_field(thd, m_offset, m_field_offset,
- &m_value);
+ 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;
@@ -3421,16 +3677,18 @@ 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;
+ 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);
@@ -3452,9 +3710,9 @@ sp_instr_set_row_field::print(String *str)
int
sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
{
- int res= thd->spcont->set_variable_row_field_by_name(thd, m_offset,
- m_field_name,
- &m_value);
+ 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;
@@ -3467,14 +3725,16 @@ 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;
+ 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);
@@ -4639,6 +4899,7 @@ Item *sp_head::adjust_assignment_source(THD *thd, Item *val, Item *val2)
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)
{
@@ -4646,7 +4907,7 @@ sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
return true;
sp_instr_set *sp_set= new (thd->mem_root)
- sp_instr_set(instructions(), spcont,
+ sp_instr_set(instructions(), spcont, rh,
spv->offset, val, lex,
responsible_to_free_lex);
@@ -4659,6 +4920,7 @@ sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
*/
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)
{
@@ -4667,7 +4929,7 @@ sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
sp_instr_set_row_field *sp_set= new (thd->mem_root)
sp_instr_set_row_field(instructions(),
- spcont,
+ spcont, rh,
spv->offset,
field_idx, val,
lex, true);
@@ -4677,6 +4939,7 @@ sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
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)
@@ -4686,7 +4949,7 @@ sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
sp_instr_set_row_field_by_name *sp_set=
new (thd->mem_root) sp_instr_set_row_field_by_name(instructions(),
- spcont,
+ spcont, rh,
spv->offset,
*field_name,
val,
@@ -4766,6 +5029,7 @@ sp_head::add_set_for_loop_cursor_param_variables(THD *thd,
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;
@@ -4837,3 +5101,44 @@ bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
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;
+
+}