diff options
Diffstat (limited to 'sql/sp_head.cc')
-rw-r--r-- | sql/sp_head.cc | 361 |
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; + +} |