diff options
author | Alexander Barkov <bar@mariadb.org> | 2017-07-31 23:00:59 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-07-31 23:00:59 +0400 |
commit | c431eafd62bae41dd89a6b71e4554facbad1040b (patch) | |
tree | f8e64bee7cba1d3a70f88e57be85a23e890b16be /sql | |
parent | e67b816451cb1003fc42755e40327411fd8b7a35 (diff) | |
parent | c9218ff43989bf2385d1f62b45ed1f6229cbc5a5 (diff) | |
download | mariadb-git-c431eafd62bae41dd89a6b71e4554facbad1040b.tar.gz |
Merge remote-tracking branch 'origin/bb-10.2-ext' into 10.3
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.cc | 19 | ||||
-rw-r--r-- | sql/field.h | 15 | ||||
-rw-r--r-- | sql/item_create.cc | 2 | ||||
-rw-r--r-- | sql/item_func.cc | 11 | ||||
-rw-r--r-- | sql/sp.cc | 618 | ||||
-rw-r--r-- | sql/sp.h | 331 | ||||
-rw-r--r-- | sql/sp_head.cc | 132 | ||||
-rw-r--r-- | sql/sp_head.h | 38 | ||||
-rw-r--r-- | sql/sql_acl.cc | 173 | ||||
-rw-r--r-- | sql/sql_acl.h | 11 | ||||
-rw-r--r-- | sql/sql_base.cc | 4 | ||||
-rw-r--r-- | sql/sql_class.cc | 30 | ||||
-rw-r--r-- | sql/sql_lex.cc | 22 | ||||
-rw-r--r-- | sql/sql_lex.h | 17 | ||||
-rw-r--r-- | sql/sql_parse.cc | 71 | ||||
-rw-r--r-- | sql/sql_parse.h | 11 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 9 | ||||
-rw-r--r-- | sql/sql_select.cc | 8 | ||||
-rw-r--r-- | sql/sql_show.cc | 141 | ||||
-rw-r--r-- | sql/sql_string.h | 1 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 27 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 42 | ||||
-rw-r--r-- | sql/structs.h | 2 | ||||
-rw-r--r-- | sql/wsrep_mysqld.cc | 21 |
24 files changed, 985 insertions, 771 deletions
diff --git a/sql/field.cc b/sql/field.cc index 6320121d1c3..fbb2ad79ba9 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -10773,3 +10773,22 @@ void Field::register_field_in_read_map() } bitmap_set_bit(table->read_set, field_index); } + + +bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to) +{ + StringBuffer<MAX_FIELD_WIDTH> str; + bool rc= false; + THD *thd= get_thd(); + sql_mode_t sql_mode_backup= thd->variables.sql_mode; + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + + val_str(&str); + if (!(to->length= str.length())) + *to= empty_clex_str; + else if ((rc= !(to->str= strmake_root(mem_root, str.ptr(), str.length())))) + to->length= 0; + + thd->variables.sql_mode= sql_mode_backup; + return rc; +} diff --git a/sql/field.h b/sql/field.h index 208941b3daa..cb8ade55090 100644 --- a/sql/field.h +++ b/sql/field.h @@ -839,6 +839,21 @@ public: */ virtual String *val_str(String*,String *)=0; String *val_int_as_str(String *val_buffer, bool unsigned_flag); + /* + Return the field value as a LEX_CSTRING, without padding to full length + (MODE_PAD_CHAR_TO_FULL_LENGTH is temporarily suppressed during the call). + + In case of an empty value, to[0] is assigned to empty_clex_string, + memory is not allocated. + In case of a non-empty value, the memory is allocated on mem_root. + In case of a memory allocation failure, to[0] is assigned to {NULL,0}. + + @param [IN] mem_root store non-empty values here + @param [OUT to return the string here + @retval false (success) + @retval true (EOM) + */ + bool val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to); fast_field_copier get_fast_field_copier(const Field *from); /* str_needs_quotes() returns TRUE if the value returned by val_str() needs diff --git a/sql/item_create.cc b/sql/item_create.cc index 8ab45e5dbe1..50f524bad40 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -3424,7 +3424,7 @@ Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name, arg_count= item_list->elements; qname= new (thd->mem_root) sp_name(db, name, use_explicit_name); - sp_add_used_routine(lex, thd, qname, TYPE_ENUM_FUNCTION); + sp_handler_function.add_used_routine(lex, thd, qname); if (arg_count > 0) func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), qname, diff --git a/sql/item_func.cc b/sql/item_func.cc index 10c5944995b..8ab5fd5f739 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -6265,8 +6265,7 @@ Item_func_sp::init_result_field(THD *thd) DBUG_ASSERT(m_sp == NULL); DBUG_ASSERT(sp_result_field == NULL); - if (!(m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name, - &thd->sp_func_cache, TRUE))) + if (!(m_sp= sp_handler_function.sp_find_routine(thd, m_name, true))) { my_missing_function_error (m_name->m_name, ErrConvDQName(m_name).ptr()); context->process_error(thd); @@ -6515,7 +6514,8 @@ Item_func_sp::sp_check_access(THD *thd) DBUG_ENTER("Item_func_sp::sp_check_access"); DBUG_ASSERT(m_sp); if (check_routine_access(thd, EXECUTE_ACL, - m_sp->m_db.str, m_sp->m_name.str, 0, FALSE)) + m_sp->m_db.str, m_sp->m_name.str, + &sp_handler_function, false)) DBUG_RETURN(TRUE); DBUG_RETURN(FALSE); @@ -6541,7 +6541,8 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) thd->security_ctx= context->security_ctx; res= check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str, - m_name->m_name.str, 0, FALSE); + m_name->m_name.str, + &sp_handler_function, false); thd->security_ctx= save_security_ctx; if (res) @@ -6584,7 +6585,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) Try to set and restore the security context to see whether it's valid */ Security_context *save_secutiry_ctx; - res= set_routine_security_ctx(thd, m_sp, false, &save_secutiry_ctx); + res= set_routine_security_ctx(thd, m_sp, &save_secutiry_ctx); if (!res) m_sp->m_security_ctx.restore_security_context(thd, save_secutiry_ctx); diff --git a/sql/sp.cc b/sql/sp.cc index c80078ebe19..8aefb8b3a34 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -20,6 +20,7 @@ #include "unireg.h" #include "sp.h" #include "sql_base.h" // close_thread_tables +#include "sql_lex.h" // empty_clex_str #include "sql_parse.h" // parse_sql #include "key.h" // key_copy #include "sql_show.h" // append_definer, append_identifier @@ -34,18 +35,59 @@ #include <my_user.h> -/* Used in error handling only */ -#define SP_TYPE_STRING(type) stored_procedure_type_to_str(type) - -static int -db_load_routine(THD *thd, stored_procedure_type type, const sp_name *name, - sp_head **sphp, - sql_mode_t sql_mode, const char *params, const char *returns, - const char *body, const st_sp_chistics &chistics, - LEX_CSTRING *definer_user_name, - LEX_CSTRING *definer_host_name, - longlong created, longlong modified, - Stored_program_creation_ctx *creation_ctx); + +sp_cache **Sp_handler_procedure::get_cache(THD *thd) const +{ + return &thd->sp_proc_cache; +} + +sp_cache **Sp_handler_function::get_cache(THD *thd) const +{ + return &thd->sp_func_cache; +} + +ulong Sp_handler_procedure::recursion_depth(THD *thd) const +{ + return thd->variables.max_sp_recursion_depth; +} + + +bool Sp_handler::add_instr_freturn(THD *thd, sp_head *sp, + sp_pcontext *spcont, + Item *item, LEX *lex) const +{ + my_error(ER_SP_BADRETURN, MYF(0)); + return true; +} + + +bool Sp_handler::add_instr_preturn(THD *thd, sp_head *sp, + sp_pcontext *spcont) const +{ + thd->parse_error(); + return true; +} + + +bool Sp_handler_function::add_instr_freturn(THD *thd, sp_head *sp, + sp_pcontext *spcont, + Item *item, LEX *lex) const +{ + return sp->add_instr_freturn(thd, spcont, item, lex); +} + + +bool Sp_handler_procedure::add_instr_preturn(THD *thd, sp_head *sp, + sp_pcontext *spcont) const +{ + return sp->add_instr_preturn(thd, spcont); +} + + +Sp_handler_procedure sp_handler_procedure; +Sp_handler_function sp_handler_function; +Sp_handler_trigger sp_handler_trigger; + static const TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] = @@ -176,7 +218,7 @@ class Stored_routine_creation_ctx : public Stored_program_creation_ctx, { public: static Stored_routine_creation_ctx * - load_from_db(THD *thd, const sp_name *name, TABLE *proc_tbl); + load_from_db(THD *thd, const Database_qualified_name *name, TABLE *proc_tbl); public: virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) @@ -214,15 +256,16 @@ bool load_charset(MEM_ROOT *mem_root, CHARSET_INFO *dflt_cs, CHARSET_INFO **cs) { - String cs_name; + LEX_CSTRING cs_name; - if (get_field(mem_root, field, &cs_name)) + if (field->val_str_nopad(mem_root, &cs_name)) { *cs= dflt_cs; return TRUE; } - *cs= get_charset_by_csname(cs_name.c_ptr(), MY_CS_PRIMARY, MYF(0)); + DBUG_ASSERT(cs_name.str[cs_name.length] == 0); + *cs= get_charset_by_csname(cs_name.str, MY_CS_PRIMARY, MYF(0)); if (*cs == NULL) { @@ -240,15 +283,16 @@ bool load_collation(MEM_ROOT *mem_root, CHARSET_INFO *dflt_cl, CHARSET_INFO **cl) { - String cl_name; + LEX_CSTRING cl_name; - if (get_field(mem_root, field, &cl_name)) + if (field->val_str_nopad(mem_root, &cl_name)) { *cl= dflt_cl; return TRUE; } - *cl= get_charset_by_name(cl_name.c_ptr(), MYF(0)); + DBUG_ASSERT(cl_name.str[cl_name.length] == 0); + *cl= get_charset_by_name(cl_name.str, MYF(0)); if (*cl == NULL) { @@ -263,8 +307,8 @@ bool load_collation(MEM_ROOT *mem_root, Stored_routine_creation_ctx * Stored_routine_creation_ctx::load_from_db(THD *thd, - const sp_name *name, - TABLE *proc_tbl) + const Database_qualified_name *name, + TABLE *proc_tbl) { /* Load character set/collation attributes. */ @@ -459,7 +503,6 @@ static TABLE *open_proc_table_for_update(THD *thd) Find row in open mysql.proc table representing stored routine. @param thd Thread context - @param type Type of routine to find (function or procedure) @param name Name of routine @param table TABLE object for open mysql.proc table. @@ -469,15 +512,16 @@ static TABLE *open_proc_table_for_update(THD *thd) SP_KEY_NOT_FOUND No routine with given name */ -static int -db_find_routine_aux(THD *thd, stored_procedure_type type, - const Database_qualified_name *name, - TABLE *table) +int +Sp_handler::db_find_routine_aux(THD *thd, + const Database_qualified_name *name, + TABLE *table) const { uchar key[MAX_KEY_LENGTH]; // db, name, optional key length type DBUG_ENTER("db_find_routine_aux"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), + (int) name->m_name.length, name->m_name.str)); /* Create key to find row. We have to use field->store() to be able to @@ -490,7 +534,7 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, DBUG_RETURN(SP_KEY_NOT_FOUND); table->field[0]->store(name->m_db, &my_charset_bin); table->field[1]->store(name->m_name, &my_charset_bin); - table->field[2]->store((longlong) type, TRUE); + table->field[2]->store((longlong) type(), true); key_copy(key, table->record[0], table->key_info, table->key_info->key_length); @@ -503,12 +547,67 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, } +bool st_sp_chistics::read_from_mysql_proc_row(THD *thd, TABLE *table) +{ + LEX_CSTRING str; + + if (table->field[MYSQL_PROC_FIELD_ACCESS]->val_str_nopad(thd->mem_root, + &str)) + return true; + + switch (str.str[0]) { + case 'N': + daccess= SP_NO_SQL; + break; + case 'C': + daccess= SP_CONTAINS_SQL; + break; + case 'R': + daccess= SP_READS_SQL_DATA; + break; + case 'M': + daccess= SP_MODIFIES_SQL_DATA; + break; + default: + daccess= SP_DEFAULT_ACCESS_MAPPING; + } + + if (table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->val_str_nopad(thd->mem_root, + &str)) + return true; + detistic= str.str[0] == 'N' ? false : true; + + if (table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->val_str_nopad(thd->mem_root, + &str)) + return true; + suid= str.str[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID; + + if (table->field[MYSQL_PROC_FIELD_COMMENT]->val_str_nopad(thd->mem_root, + &comment)) + return true; + + return false; +} + + +bool AUTHID::read_from_mysql_proc_row(THD *thd, TABLE *table) +{ + LEX_CSTRING str; + if (table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, + &str)) + return true; + parse(str.str, str.length); + if (user.str[user.length]) + ((char *) user.str)[user.length]= '\0'; // 0-terminate if was truncated + return false; +} + + /** Find routine definition in mysql.proc table and create corresponding sp_head object for it. @param thd Thread context - @param type Type of routine (TYPE_ENUM_PROCEDURE/...) @param name Name of routine @param sphp Out parameter in which pointer to created sp_head object is returned (0 in case of error). @@ -523,33 +622,27 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, non-0 Error (may be one of special codes like SP_KEY_NOT_FOUND) */ -static int -db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, - sp_head **sphp) +int +Sp_handler::db_find_routine(THD *thd, + const Database_qualified_name *name, + sp_head **sphp) const { TABLE *table; - const char *params, *returns, *body; + LEX_CSTRING params, returns, body; int ret; - const char *definer; longlong created; longlong modified; Sp_chistics chistics; - char *ptr; - uint length; - char buff[65]; - String str(buff, sizeof(buff), &my_charset_bin); bool saved_time_zone_used= thd->time_zone_used; sql_mode_t sql_mode, saved_mode= thd->variables.sql_mode; Open_tables_backup open_tables_state_backup; Stored_program_creation_ctx *creation_ctx; - char definer_user_name_holder[USERNAME_LENGTH + 1]; - LEX_CSTRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH }; - char definer_host_name_holder[HOSTNAME_LENGTH + 1]; - LEX_CSTRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH }; + AUTHID definer; DBUG_ENTER("db_find_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), + (int) name->m_name.length, name->m_name.str)); *sphp= 0; // In case of errors if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) @@ -558,7 +651,7 @@ db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, /* Reset sql_mode during data dictionary operations. */ thd->variables.sql_mode= 0; - if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) + if ((ret= db_find_routine_aux(thd, name, table)) != SP_OK) goto done; if (table->s->fields < MYSQL_PROC_FIELD_COUNT) @@ -567,106 +660,43 @@ db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, goto done; } - if ((ptr= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_ACCESS])) == NULL) - { - ret= SP_GET_FIELD_FAILED; - goto done; - } - switch (ptr[0]) { - case 'N': - chistics.daccess= SP_NO_SQL; - break; - case 'C': - chistics.daccess= SP_CONTAINS_SQL; - break; - case 'R': - chistics.daccess= SP_READS_SQL_DATA; - break; - case 'M': - chistics.daccess= SP_MODIFIES_SQL_DATA; - break; - default: - chistics.daccess= SP_DEFAULT_ACCESS_MAPPING; - } - - if ((ptr= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_DETERMINISTIC])) == NULL) - { - ret= SP_GET_FIELD_FAILED; - goto done; - } - chistics.detistic= (ptr[0] == 'N' ? FALSE : TRUE); - - if ((ptr= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_SECURITY_TYPE])) == NULL) + if (chistics.read_from_mysql_proc_row(thd, table) || + definer.read_from_mysql_proc_row(thd, table)) { ret= SP_GET_FIELD_FAILED; goto done; } - chistics.suid= (ptr[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID); - if ((params= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_PARAM_LIST])) == NULL) - { - params= ""; - } - - if (type == TYPE_ENUM_PROCEDURE) - returns= ""; - else if ((returns= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_RETURNS])) == NULL) + table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root, + ¶ms); + if (type() == TYPE_ENUM_PROCEDURE) + returns= empty_clex_str; + else if (table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, + &returns)) { ret= SP_GET_FIELD_FAILED; goto done; } - if ((body= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_BODY])) == NULL) + if (table->field[MYSQL_PROC_FIELD_BODY]->val_str_nopad(thd->mem_root, + &body)) { ret= SP_GET_FIELD_FAILED; goto done; } // Get additional information - if ((definer= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_DEFINER])) == NULL) - { - ret= SP_GET_FIELD_FAILED; - goto done; - } - modified= table->field[MYSQL_PROC_FIELD_MODIFIED]->val_int(); created= table->field[MYSQL_PROC_FIELD_CREATED]->val_int(); - sql_mode= (ulong) table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int(); - table->field[MYSQL_PROC_FIELD_COMMENT]->val_str(&str, &str); - - ptr= 0; - if ((length= str.length())) - ptr= thd->strmake(str.ptr(), length); - chistics.comment.str= ptr; - chistics.comment.length= length; - creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, name, table); close_system_tables(thd, &open_tables_state_backup); table= 0; - /* It's ok to cast to char* here as the pointers are to local buffers */ - if (parse_user(definer, strlen(definer), - (char*) definer_user_name.str, &definer_user_name.length, - (char*) definer_host_name.str, &definer_host_name.length) && - definer_user_name.length && !definer_host_name.length) - { - // 'user@' -> 'user@%' - definer_host_name= host_not_specified; - } - - ret= db_load_routine(thd, type, name, sphp, - sql_mode, params, returns, body, chistics, - &definer_user_name, &definer_host_name, + ret= db_load_routine(thd, name, sphp, + sql_mode, params, returns, body, chistics, definer, created, modified, creation_ctx); done: /* @@ -808,15 +838,17 @@ Bad_db_error_handler::handle_condition(THD *thd, } -static int -db_load_routine(THD *thd, stored_procedure_type type, - const sp_name *name, sp_head **sphp, - sql_mode_t sql_mode, const char *params, const char *returns, - const char *body, const st_sp_chistics &chistics, - LEX_CSTRING *definer_user_name, - LEX_CSTRING *definer_host_name, - longlong created, longlong modified, - Stored_program_creation_ctx *creation_ctx) +int +Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name, + sp_head **sphp, + sql_mode_t sql_mode, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + longlong created, longlong modified, + Stored_program_creation_ctx *creation_ctx) const { LEX *old_lex= thd->lex, newlex; String defstr; @@ -842,14 +874,9 @@ db_load_routine(THD *thd, stored_procedure_type type, */ if (!show_create_sp(thd, &defstr, - type, - NULL, 0, - name->m_name.str, name->m_name.length, - params, strlen(params), - returns, strlen(returns), - body, strlen(body), - chistics, definer_user_name, definer_host_name, - sql_mode)) + null_clex_str, name->m_name, + params, returns, body, + chistics, definer, sql_mode)) { ret= SP_INTERNAL_ERROR; goto end; @@ -900,7 +927,7 @@ db_load_routine(THD *thd, stored_procedure_type type, goto end; } - (*sphp)->set_definer(definer_user_name, definer_host_name); + (*sphp)->set_definer(&definer.user, &definer.host); (*sphp)->set_info(created, modified, chistics, sql_mode); (*sphp)->set_creation_ctx(creation_ctx); (*sphp)->optimize(); @@ -924,7 +951,7 @@ end: void -sp_returns_type(THD *thd, String &result, sp_head *sp) +sp_returns_type(THD *thd, String &result, const sp_head *sp) { TABLE table; TABLE_SHARE share; @@ -960,7 +987,6 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) and invalidates the stored-routine cache. @param thd Thread context. - @param type Stored routine type (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @param table A pointer to the opened mysql.proc table @@ -968,9 +994,10 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) @return SP_OK on success, or SP_DELETE_ROW_FAILED on error. used to indicate about errors. */ -static int -sp_drop_routine_internal(THD *thd, stored_procedure_type type, - const Database_qualified_name *name, TABLE *table) +int +Sp_handler::sp_drop_routine_internal(THD *thd, + const Database_qualified_name *name, + TABLE *table) const { DBUG_ENTER("sp_drop_routine_internal"); @@ -987,10 +1014,9 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type, local cache. */ sp_head *sp; - sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? - &thd->sp_func_cache : &thd->sp_proc_cache); - sp= sp_cache_lookup(spc, name); - if (sp) + sp_cache **spc= get_cache(thd); + DBUG_ASSERT(spc); + if ((sp= sp_cache_lookup(spc, name))) sp_cache_flush_obsolete(spc, &sp); DBUG_RETURN(SP_OK); } @@ -1003,8 +1029,6 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type, the mysql.proc. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION). @param sp Stored routine object to store. @note Opens and closes the thread tables. Therefore assumes @@ -1021,7 +1045,7 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type, */ bool -sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) +Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const { LEX *lex= thd->lex; bool ret= TRUE; @@ -1029,8 +1053,6 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) char definer_buf[USER_HOST_BUFF_SIZE]; LEX_CSTRING definer; sql_mode_t saved_mode= thd->variables.sql_mode; - MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? - MDL_key::FUNCTION : MDL_key::PROCEDURE; CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str); @@ -1038,15 +1060,15 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) bool store_failed= FALSE; DBUG_ENTER("sp_create_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type, + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), (int) sp->m_name.length, sp->m_name.str)); + MDL_key::enum_mdl_namespace mdl_type= get_mdl_type(); + LEX_CSTRING returns= empty_clex_str; String retstr(64); retstr.set_charset(system_charset_info); - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); - /* Grab an exclusive MDL lock. */ if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str)) { @@ -1074,17 +1096,17 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (!(table= open_proc_table_for_update(thd))) { - my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(type),sp->m_name.str); + my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str); goto done; } else { /* Checking if the routine already exists */ - if (db_find_routine_aux(thd, type, sp, table) == SP_OK) + if (db_find_routine_aux(thd, sp, table) == SP_OK) { if (lex->create_info.or_replace()) { - if ((ret= sp_drop_routine_internal(thd, type, sp, table))) + if ((ret= sp_drop_routine_internal(thd, sp, table))) goto done; } else if (lex->create_info.if_not_exists()) @@ -1092,20 +1114,21 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SP_ALREADY_EXISTS, ER_THD(thd, ER_SP_ALREADY_EXISTS), - SP_TYPE_STRING(type), - sp->m_name.str); + type_str(), sp->m_name.str); ret= FALSE; // Setting retstr as it is used for logging. - if (sp->m_type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) + { sp_returns_type(thd, retstr, sp); + returns= retstr.lex_cstring(); + } goto log; } else { - my_error(ER_SP_ALREADY_EXISTS, MYF(0), - SP_TYPE_STRING(type), sp->m_name.str); + my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str); goto done; } } @@ -1117,8 +1140,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (table->s->fields < MYSQL_PROC_FIELD_COUNT) { - my_error(ER_SP_STORE_FAILED, MYF(0), - SP_TYPE_STRING(type), sp->m_name.str); + my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str); goto done; } @@ -1146,7 +1168,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) store_failed= store_failed || table->field[MYSQL_PROC_MYSQL_TYPE]-> - store((longlong)type, TRUE); + store((longlong) type(), true); store_failed= store_failed || table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]-> @@ -1174,9 +1196,10 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) table->field[MYSQL_PROC_FIELD_PARAM_LIST]-> store(sp->m_params, system_charset_info); - if (sp->m_type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); + returns= retstr.lex_cstring(); store_failed= store_failed || table->field[MYSQL_PROC_FIELD_RETURNS]-> @@ -1205,7 +1228,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) store(sp->comment(), system_charset_info); } - if ((sp->m_type == TYPE_ENUM_FUNCTION) && + if (type() == TYPE_ENUM_FUNCTION && !trust_function_creators && mysql_bin_log.is_open()) { if (!sp->detistic()) @@ -1263,8 +1286,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (table->file->ha_write_row(table->record[0])) { - my_error(ER_SP_ALREADY_EXISTS, MYF(0), - SP_TYPE_STRING(type), sp->m_name.str); + my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str); goto done; } /* Make change permanent and avoid 'table is marked as crashed' errors */ @@ -1282,16 +1304,11 @@ log: log_query.set_charset(system_charset_info); if (!show_create_sp(thd, &log_query, - sp->m_type, - (sp->m_explicit_name ? sp->m_db.str : NULL), - (sp->m_explicit_name ? sp->m_db.length : 0), - sp->m_name.str, sp->m_name.length, - sp->m_params.str, sp->m_params.length, - retstr.ptr(), retstr.length(), - sp->m_body.str, sp->m_body.length, - sp->chistics(), &(thd->lex->definer->user), - &(thd->lex->definer->host), - saved_mode)) + sp->m_explicit_name ? sp->m_db : null_clex_str, + sp->m_name, + sp->m_params, returns, sp->m_body, + sp->chistics(), thd->lex->definer[0], + saved_mode)) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); goto done; @@ -1326,8 +1343,6 @@ done: from the mysql.proc table and invalidates the stored-routine cache. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @return Error code. SP_OK is returned on success. Other SP_ constants are @@ -1335,18 +1350,16 @@ done: */ int -sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name) +Sp_handler::sp_drop_routine(THD *thd, + const Database_qualified_name *name) const { TABLE *table; int ret; - MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? - MDL_key::FUNCTION : MDL_key::PROCEDURE; DBUG_ENTER("sp_drop_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); - - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), + (int) name->m_name.length, name->m_name.str)); + MDL_key::enum_mdl_namespace mdl_type= get_mdl_type(); /* Grab an exclusive MDL lock. */ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str)) @@ -1355,8 +1368,8 @@ sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name) if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) - ret= sp_drop_routine_internal(thd, type, name, table); + if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK) + ret= sp_drop_routine_internal(thd, name, table); if (ret == SP_OK && write_bin_log(thd, TRUE, thd->query(), thd->query_length())) @@ -1379,8 +1392,6 @@ sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name) successful update, the cache is invalidated. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @param chistics New values of stored routine attributes to write. @@ -1389,20 +1400,16 @@ sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name) */ int -sp_update_routine(THD *thd, stored_procedure_type type, const sp_name *name, - const st_sp_chistics *chistics) +Sp_handler::sp_update_routine(THD *thd, const Database_qualified_name *name, + const st_sp_chistics *chistics) const { TABLE *table; int ret; - MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? - MDL_key::FUNCTION : MDL_key::PROCEDURE; DBUG_ENTER("sp_update_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - (int) type, + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), (int) name->m_name.length, name->m_name.str)); - - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); + MDL_key::enum_mdl_namespace mdl_type= get_mdl_type(); /* Grab an exclusive MDL lock. */ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str)) @@ -1411,9 +1418,9 @@ sp_update_routine(THD *thd, stored_procedure_type type, const sp_name *name, if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) + if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK) { - if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators && + if (type() == TYPE_ENUM_FUNCTION && ! trust_function_creators && mysql_bin_log.is_open() && (chistics->daccess == SP_CONTAINS_SQL || chistics->daccess == SP_MODIFIES_SQL_DATA)) @@ -1662,8 +1669,6 @@ err: calls sp_head::show_create_routine() for the object. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @return Error status. @@ -1672,19 +1677,16 @@ err: */ bool -sp_show_create_routine(THD *thd, - stored_procedure_type type, const sp_name *name) +Sp_handler::sp_show_create_routine(THD *thd, + const Database_qualified_name *name) const { sp_head *sp; DBUG_ENTER("sp_show_create_routine"); - DBUG_PRINT("enter", ("name: %.*s", + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), (int) name->m_name.length, name->m_name.str)); - - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); - /* @todo: Consider using prelocking for this code as well. Currently SHOW CREATE PROCEDURE/FUNCTION is a dirty read of the data @@ -1692,17 +1694,16 @@ sp_show_create_routine(THD *thd, It is "safe" to do as long as it doesn't affect the results of the binary log or the query cache, which currently it does not. */ - if (sp_cache_routine(thd, type, name, FALSE, &sp)) + if (sp_cache_routine(thd, name, false, &sp)) DBUG_RETURN(TRUE); - if (sp == NULL || sp->show_create_routine(thd, type)) + if (sp == NULL || sp->show_create_routine(thd, this)) { /* If we have insufficient privileges, pretend the routine does not exist. */ - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), stored_procedure_type_to_str(type), - name->m_name.str); + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), type_str(), name->m_name.str); DBUG_RETURN(TRUE); } @@ -1715,7 +1716,6 @@ sp_show_create_routine(THD *thd, stored procedures cache and looking into mysql.proc if needed. @param thd thread context - @param type type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE) @param name name of procedure @param cp hash to look routine in @param cache_only if true perform cache-only lookup @@ -1728,24 +1728,22 @@ sp_show_create_routine(THD *thd, */ sp_head * -sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, - sp_cache **cp, bool cache_only) +Sp_handler::sp_find_routine(THD *thd, const Database_qualified_name *name, + bool cache_only) const { + sp_cache **cp= get_cache(thd); sp_head *sp; - ulong depth= (type == TYPE_ENUM_PROCEDURE ? - thd->variables.max_sp_recursion_depth : - 0); DBUG_ENTER("sp_find_routine"); - DBUG_PRINT("enter", ("name: %.*s.%.*s type: %d cache only %d", + DBUG_PRINT("enter", ("name: %.*s.%.*s type: %s cache only %d", (int) name->m_db.length, name->m_db.str, (int) name->m_name.length, name->m_name.str, - type, cache_only)); + type_str(), cache_only)); if ((sp= sp_cache_lookup(cp, name))) { ulong level; sp_head *new_sp; - const char *returns= ""; + LEX_CSTRING returns= empty_clex_str; /* String buffer for RETURNS data type must have system charset; @@ -1762,9 +1760,9 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, sp->m_first_free_instance->m_recursion_level, sp->m_first_free_instance->m_flags)); DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED)); - if (sp->m_first_free_instance->m_recursion_level > depth) + if (sp->m_first_free_instance->m_recursion_level > recursion_depth(thd)) { - sp->recursion_level_error(thd); + recursion_level_error(thd, sp); DBUG_RETURN(0); } DBUG_RETURN(sp->m_first_free_instance); @@ -1776,21 +1774,21 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, */ level= sp->m_last_cached_sp->m_recursion_level + 1; - if (level > depth) + if (level > recursion_depth(thd)) { - sp->recursion_level_error(thd); + recursion_level_error(thd, sp); DBUG_RETURN(0); } - if (type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); - returns= retstr.ptr(); + returns= retstr.lex_cstring(); } - if (db_load_routine(thd, type, name, &new_sp, - sp->m_sql_mode, sp->m_params.str, returns, - sp->m_body.str, sp->chistics(), - &sp->m_definer.user, &sp->m_definer.host, + if (db_load_routine(thd, name, &new_sp, + sp->m_sql_mode, sp->m_params, returns, + sp->m_body, sp->chistics(), + sp->m_definer, sp->m_created, sp->m_modified, sp->get_creation_ctx()) == SP_OK) { @@ -1807,7 +1805,7 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, } if (!cache_only) { - if (db_find_routine(thd, type, name, &sp) == SP_OK) + if (db_find_routine(thd, name, &sp) == SP_OK) { sp_cache_insert(cp, sp); DBUG_PRINT("info", ("added new: 0x%lx, level: %lu, flags %x", @@ -1825,8 +1823,6 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, @param thd Thread handler @param routines List of needles in the hay stack - @param is_proc Indicates whether routines in the list are procedures - or functions. @return @retval FALSE Found. @@ -1834,7 +1830,7 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, */ bool -sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc) +Sp_handler::sp_exist_routines(THD *thd, TABLE_LIST *routines) const { TABLE_LIST *routine; bool sp_object_found; @@ -1848,12 +1844,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc) thd->make_lex_string(&lex_name, routine->table_name, strlen(routine->table_name)); name= new sp_name(&lex_db, &lex_name, true); - sp_object_found= is_proc ? sp_find_routine(thd, TYPE_ENUM_PROCEDURE, - name, &thd->sp_proc_cache, - FALSE) != NULL : - sp_find_routine(thd, TYPE_ENUM_FUNCTION, - name, &thd->sp_func_cache, - FALSE) != NULL; + sp_object_found= sp_find_routine(thd, name, false) != NULL; thd->get_stmt_da()->clear_warning_info(thd->query_id); if (! sp_object_found) { @@ -1944,20 +1935,18 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, @param arena Arena in which memory for new element of the set will be allocated @param rt Routine name - @param rt_type Routine type (one of TYPE_ENUM_PROCEDURE/...) @note Will also add element to end of 'Query_tables_list::sroutines_list' list (and will take into account that this is an explicitly used routine). */ -void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, - const sp_name *rt, enum stored_procedure_type rt_type) +void Sp_handler::add_used_routine(Query_tables_list *prelocking_ctx, + Query_arena *arena, + const Database_qualified_name *rt) const { - MDL_key key((rt_type == TYPE_ENUM_FUNCTION) ? MDL_key::FUNCTION : - MDL_key::PROCEDURE, - rt->m_db.str, rt->m_name.str); - (void)sp_add_used_routine(prelocking_ctx, arena, &key, 0); + MDL_key key(get_mdl_type(), rt->m_db.str, rt->m_name.str); + (void) sp_add_used_routine(prelocking_ctx, arena, &key, 0); prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next; prelocking_ctx->sroutines_list_own_elements= prelocking_ctx->sroutines_list.elements; @@ -2084,24 +2073,24 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, prelocking until 'sp_name' is eradicated as a class. */ -int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, - bool lookup_only, sp_head **sp) +int Sroutine_hash_entry::sp_cache_routine(THD *thd, + bool lookup_only, + sp_head **sp) const { char qname_buff[NAME_LEN*2+1+1]; - sp_name name(&rt->mdl_request.key, qname_buff); - MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace(); - stored_procedure_type type= ((mdl_type == MDL_key::FUNCTION) ? - TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE); - + sp_name name(&mdl_request.key, qname_buff); + MDL_key::enum_mdl_namespace mdl_type= mdl_request.key.mdl_namespace(); + const Sp_handler *sph= Sp_handler::handler(mdl_type); + DBUG_ASSERT(sph); /* Check that we have an MDL lock on this routine, unless it's a top-level CALL. The assert below should be unambiguous: the first element in sroutines_list has an MDL lock unless it's a top-level call, or a trigger, but triggers can't occur here (see the preceding assert). */ - DBUG_ASSERT(rt->mdl_request.ticket || rt == thd->lex->sroutines_list.first); + DBUG_ASSERT(mdl_request.ticket || this == thd->lex->sroutines_list.first); - return sp_cache_routine(thd, type, &name, lookup_only, sp); + return sph->sp_cache_routine(thd, &name, lookup_only, sp); } @@ -2112,7 +2101,6 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, loading. @param[in] thd Thread context. - @param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE). @param[in] name Name of routine. @param[in] lookup_only Only check that the routine is in the cache. If it's not, don't try to load. If it is present, @@ -2125,17 +2113,17 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, @retval non-0 Error while loading routine from mysql,proc table. */ -int sp_cache_routine(THD *thd, enum stored_procedure_type type, - const sp_name *name, - bool lookup_only, sp_head **sp) +int Sp_handler::sp_cache_routine(THD *thd, + const Database_qualified_name *name, + bool lookup_only, + sp_head **sp) const { int ret= 0; - sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? - &thd->sp_func_cache : &thd->sp_proc_cache); + sp_cache **spc= get_cache(thd); DBUG_ENTER("sp_cache_routine"); - DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE); + DBUG_ASSERT(spc); *sp= sp_cache_lookup(spc, name); @@ -2149,7 +2137,7 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, DBUG_RETURN(SP_OK); } - switch ((ret= db_find_routine(thd, type, name, sp))) + switch ((ret= db_find_routine(thd, name, sp))) { case SP_OK: sp_cache_insert(spc, *sp); @@ -2191,21 +2179,20 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, Returns TRUE on success, FALSE on (alloc) failure. */ bool -show_create_sp(THD *thd, String *buf, - stored_procedure_type type, - const char *db, ulong dblen, - const char *name, ulong namelen, - const char *params, ulong paramslen, - const char *returns, ulong returnslen, - const char *body, ulong bodylen, - const st_sp_chistics &chistics, - const LEX_CSTRING *definer_user, - const LEX_CSTRING *definer_host, - sql_mode_t sql_mode) +Sp_handler::show_create_sp(THD *thd, String *buf, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + sql_mode_t sql_mode) const { sql_mode_t old_sql_mode= thd->variables.sql_mode; /* Make some room to begin with */ - if (buf->alloc(100 + dblen + 1 + namelen + paramslen + returnslen + bodylen + + if (buf->alloc(100 + db.length + 1 + name.length + + params.length + returns.length + chistics.comment.length + 10 /* length of " DEFINER= "*/ + USER_HOST_BUFF_SIZE)) return FALSE; @@ -2214,30 +2201,28 @@ show_create_sp(THD *thd, String *buf, buf->append(STRING_WITH_LEN("CREATE ")); if (thd->lex->create_info.or_replace()) buf->append(STRING_WITH_LEN("OR REPLACE ")); - append_definer(thd, buf, definer_user, definer_host); - if (type == TYPE_ENUM_FUNCTION) - buf->append(STRING_WITH_LEN("FUNCTION ")); - else - buf->append(STRING_WITH_LEN("PROCEDURE ")); + append_definer(thd, buf, &definer.user, &definer.host); + buf->append(type_lex_cstring()); + buf->append(STRING_WITH_LEN(" ")); if (thd->lex->create_info.if_not_exists()) buf->append(STRING_WITH_LEN("IF NOT EXISTS ")); - if (dblen > 0) + if (db.length > 0) { - append_identifier(thd, buf, db, dblen); + append_identifier(thd, buf, db.str, db.length); buf->append('.'); } - append_identifier(thd, buf, name, namelen); + append_identifier(thd, buf, name.str, name.length); buf->append('('); - buf->append(params, paramslen); + buf->append(params); buf->append(')'); - if (type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) { if (sql_mode & MODE_ORACLE) buf->append(STRING_WITH_LEN(" RETURN ")); else buf->append(STRING_WITH_LEN(" RETURNS ")); - buf->append(returns, returnslen); + buf->append(returns); } buf->append('\n'); switch (chistics.daccess) { @@ -2265,7 +2250,7 @@ show_create_sp(THD *thd, String *buf, append_unescaped(buf, chistics.comment.str, chistics.comment.length); buf->append('\n'); } - buf->append(body, bodylen); + buf->append(body); thd->variables.sql_mode= old_sql_mode; return TRUE; } @@ -2292,26 +2277,19 @@ show_create_sp(THD *thd, String *buf, */ sp_head * -sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, - String *name, sql_mode_t sql_mode, - stored_procedure_type type, - const char *returns, const char *params, - bool *free_sp_head) +Sp_handler::sp_load_for_information_schema(THD *thd, TABLE *proc_table, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + sql_mode_t sql_mode, + bool *free_sp_head) const { - const char *sp_body; String defstr; - const LEX_CSTRING definer_user= {STRING_WITH_LEN("")}; - const LEX_CSTRING definer_host= {STRING_WITH_LEN("")}; - LEX_CSTRING sp_db_str; - LEX_CSTRING sp_name_str; + const AUTHID definer= {{STRING_WITH_LEN("")}, {STRING_WITH_LEN("")}}; sp_head *sp; - sp_cache **spc= ((type == TYPE_ENUM_PROCEDURE) ? - &thd->sp_proc_cache : &thd->sp_func_cache); - sp_db_str.str= db->c_ptr(); - sp_db_str.length= db->length(); - sp_name_str.str= name->c_ptr(); - sp_name_str.length= name->length(); - sp_name sp_name_obj(&sp_db_str, &sp_name_str, true); + sp_cache **spc= get_cache(thd); + sp_name sp_name_obj(&db, &name, true); // This can change "name" *free_sp_head= 0; if ((sp= sp_cache_lookup(spc, &sp_name_obj))) { @@ -2321,15 +2299,11 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, LEX *old_lex= thd->lex, newlex; Stored_program_creation_ctx *creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, &sp_name_obj, proc_table); - sp_body= (type == TYPE_ENUM_FUNCTION ? "RETURN NULL" : "BEGIN END"); defstr.set_charset(creation_ctx->get_client_cs()); - if (!show_create_sp(thd, &defstr, type, - sp_db_str.str, sp_db_str.length, - sp_name_obj.m_name.str, sp_name_obj.m_name.length, - params, strlen(params), - returns, strlen(returns), - sp_body, strlen(sp_body), - Sp_chistics(), &definer_user, &definer_host, sql_mode)) + if (!show_create_sp(thd, &defstr, + sp_name_obj.m_db, sp_name_obj.m_name, + params, returns, empty_body_lex_cstring(), + Sp_chistics(), definer, sql_mode)) return 0; thd->lex= &newlex; @@ -17,7 +17,10 @@ #ifndef _SP_H_ #define _SP_H_ +#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_string.h" // LEX_STRING +#include "sql_cmd.h" +#include "mdl.h" class Field; class Open_tables_backup; @@ -28,8 +31,10 @@ class Sroutine_hash_entry; class THD; class sp_cache; class sp_head; -class sp_name; +class sp_pcontext; +class Database_qualified_name; struct st_sp_chistics; +class Stored_program_creation_ctx; struct LEX; struct TABLE; struct TABLE_LIST; @@ -49,19 +54,268 @@ enum stored_procedure_type }; -static inline const char * -stored_procedure_type_to_str(enum stored_procedure_type type) +class Sp_handler +{ + int db_find_routine_aux(THD *thd, const Database_qualified_name *name, + TABLE *table) const; + int db_find_routine(THD *thd, const Database_qualified_name *name, + sp_head **sphp) const; + int db_load_routine(THD *thd, const Database_qualified_name *name, + sp_head **sphp, + sql_mode_t sql_mode, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + longlong created, longlong modified, + Stored_program_creation_ctx *creation_ctx) const; + int sp_drop_routine_internal(THD *thd, + const Database_qualified_name *name, + TABLE *table) const; +public: + static const Sp_handler *handler(enum enum_sql_command cmd); + static const Sp_handler *handler(stored_procedure_type type); + static const Sp_handler *handler(MDL_key::enum_mdl_namespace ns); + const char *type_str() const { return type_lex_cstring().str; } + virtual const char *show_create_routine_col1_caption() const + { + DBUG_ASSERT(0); + return ""; + } + virtual const char *show_create_routine_col3_caption() const + { + DBUG_ASSERT(0); + return ""; + } + virtual stored_procedure_type type() const= 0; + virtual LEX_CSTRING type_lex_cstring() const= 0; + virtual LEX_CSTRING empty_body_lex_cstring() const + { + static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("???")}; + DBUG_ASSERT(0); + return m_empty_body; + } + virtual MDL_key::enum_mdl_namespace get_mdl_type() const= 0; + virtual sp_cache **get_cache(THD *) const { return NULL; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS + virtual HASH *get_priv_hash() const { return NULL; } +#endif + virtual ulong recursion_depth(THD *thd) const { return 0; } + /** + Return appropriate error about recursion limit reaching + + @param thd Thread handle + @param sp SP routine + + @remark For functions and triggers we return error about + prohibited recursion. For stored procedures we + return about reaching recursion limit. + */ + virtual void recursion_level_error(THD *thd, const sp_head *sp) const + { + my_error(ER_SP_NO_RECURSION, MYF(0)); + } + virtual bool add_instr_freturn(THD *thd, sp_head *sp, + sp_pcontext *spcont, + Item *item, LEX *lex) const; + virtual bool add_instr_preturn(THD *thd, sp_head *sp, + sp_pcontext *spcont) const; + + void add_used_routine(Query_tables_list *prelocking_ctx, + Query_arena *arena, + const Database_qualified_name *rt) const; + + sp_head *sp_find_routine(THD *thd, const Database_qualified_name *name, + bool cache_only) const; + int sp_cache_routine(THD *thd, const Database_qualified_name *name, + bool lookup_only, sp_head **sp) const; + + bool sp_exist_routines(THD *thd, TABLE_LIST *procs) const; + bool sp_show_create_routine(THD *thd, + const Database_qualified_name *name) const; + + bool sp_create_routine(THD *thd, const sp_head *sp) const; + + int sp_update_routine(THD *thd, const Database_qualified_name *name, + const st_sp_chistics *chistics) const; + + int sp_drop_routine(THD *thd, const Database_qualified_name *name) const; + + sp_head *sp_load_for_information_schema(THD *thd, TABLE *proc_table, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + sql_mode_t sql_mode, + bool *free_sp_head) const; + + bool show_create_sp(THD *thd, String *buf, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + sql_mode_t sql_mode) const; +}; + + +class Sp_handler_procedure: public Sp_handler +{ +public: + stored_procedure_type type() const { return TYPE_ENUM_PROCEDURE; } + LEX_CSTRING type_lex_cstring() const + { + static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("PROCEDURE")}; + return m_type_str; + } + LEX_CSTRING empty_body_lex_cstring() const + { + static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("BEGIN END")}; + return m_empty_body; + } + const char *show_create_routine_col1_caption() const + { + return "Procedure"; + } + const char *show_create_routine_col3_caption() const + { + return "Create Procedure"; + } + MDL_key::enum_mdl_namespace get_mdl_type() const + { + return MDL_key::PROCEDURE; + } + sp_cache **get_cache(THD *) const; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + HASH *get_priv_hash() const; +#endif + ulong recursion_depth(THD *thd) const; + void recursion_level_error(THD *thd, const sp_head *sp) const; + bool add_instr_preturn(THD *thd, sp_head *sp, sp_pcontext *spcont) const; +}; + + +class Sp_handler_function: public Sp_handler +{ +public: + stored_procedure_type type() const { return TYPE_ENUM_FUNCTION; } + LEX_CSTRING type_lex_cstring() const + { + static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("FUNCTION")}; + return m_type_str; + } + LEX_CSTRING empty_body_lex_cstring() const + { + static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("RETURN NULL")}; + return m_empty_body; + } + const char *show_create_routine_col1_caption() const + { + return "Function"; + } + const char *show_create_routine_col3_caption() const + { + return "Create Function"; + } + MDL_key::enum_mdl_namespace get_mdl_type() const + { + return MDL_key::FUNCTION; + } + sp_cache **get_cache(THD *) const; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + HASH *get_priv_hash() const; +#endif + bool add_instr_freturn(THD *thd, sp_head *sp, sp_pcontext *spcont, + Item *item, LEX *lex) const; +}; + + +class Sp_handler_trigger: public Sp_handler +{ +public: + stored_procedure_type type() const { return TYPE_ENUM_TRIGGER; } + LEX_CSTRING type_lex_cstring() const + { + static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("TRIGGER")}; + return m_type_str; + } + MDL_key::enum_mdl_namespace get_mdl_type() const + { + DBUG_ASSERT(0); + return MDL_key::TRIGGER; + } +}; + + +extern MYSQL_PLUGIN_IMPORT Sp_handler_function sp_handler_function; +extern MYSQL_PLUGIN_IMPORT Sp_handler_procedure sp_handler_procedure; +extern MYSQL_PLUGIN_IMPORT Sp_handler_trigger sp_handler_trigger; + + +inline const Sp_handler *Sp_handler::handler(enum_sql_command cmd) +{ + switch (cmd) { + case SQLCOM_CREATE_PROCEDURE: + case SQLCOM_ALTER_PROCEDURE: + case SQLCOM_DROP_PROCEDURE: + case SQLCOM_SHOW_PROC_CODE: + case SQLCOM_SHOW_CREATE_PROC: + case SQLCOM_SHOW_STATUS_PROC: + return &sp_handler_procedure; + case SQLCOM_CREATE_SPFUNCTION: + case SQLCOM_ALTER_FUNCTION: + case SQLCOM_DROP_FUNCTION: + case SQLCOM_SHOW_FUNC_CODE: + case SQLCOM_SHOW_CREATE_FUNC: + case SQLCOM_SHOW_STATUS_FUNC: + return &sp_handler_function; + default: + break; + } + return NULL; +} + + +inline const Sp_handler *Sp_handler::handler(stored_procedure_type type) { switch (type) { - case TYPE_ENUM_PROCEDURE: return "PROCEDURE"; - case TYPE_ENUM_FUNCTION: return "FUNCTION"; - case TYPE_ENUM_TRIGGER: return "TRIGGER"; - case TYPE_ENUM_PROXY: return "PROXY"; + case TYPE_ENUM_PROCEDURE: + return &sp_handler_procedure; + case TYPE_ENUM_FUNCTION: + return &sp_handler_function; + case TYPE_ENUM_TRIGGER: + return &sp_handler_trigger; + case TYPE_ENUM_PROXY: + break; } - DBUG_ASSERT(0); - return "UNKNOWN_STORED_"; + return NULL; } + +inline const Sp_handler *Sp_handler::handler(MDL_key::enum_mdl_namespace type) +{ + switch (type) { + case MDL_key::FUNCTION: + return &sp_handler_function; + case MDL_key::PROCEDURE: + return &sp_handler_procedure; + case MDL_key::GLOBAL: + case MDL_key::SCHEMA: + case MDL_key::TABLE: + case MDL_key::TRIGGER: + case MDL_key::EVENT: + case MDL_key::COMMIT: + case MDL_key::USER_LOCK: + case MDL_key::NAMESPACE_END: + break; + } + return NULL; +} + + /* Tells what SP_DEFAULT_ACCESS should be mapped to */ #define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL @@ -121,36 +375,6 @@ sp_drop_db_routines(THD *thd, const char *db); */ bool lock_db_routines(THD *thd, const char *db); -sp_head * -sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, - sp_cache **cp, bool cache_only); - -int -sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, - bool lookup_only, sp_head **sp); - - -int -sp_cache_routine(THD *thd, stored_procedure_type type, const sp_name *name, - bool lookup_only, sp_head **sp); - -bool -sp_exist_routines(THD *thd, TABLE_LIST *procs, bool is_proc); - -bool -sp_show_create_routine(THD *thd, stored_procedure_type type, const sp_name *name); - -bool -sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp); - -int -sp_update_routine(THD *thd, stored_procedure_type type, const sp_name *name, - const st_sp_chistics *chistics); - -int -sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name); - - /** Structure that represents element in the set of stored routines used by statement or routine. @@ -185,14 +409,11 @@ public: changes. */ ulong m_sp_cache_version; + + int sp_cache_routine(THD *thd, bool lookup_only, sp_head **sp) const; }; -/* - Procedures for handling sets of stored routines used by statement or routine. -*/ -void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, - const sp_name *rt, stored_procedure_type rt_type); bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, const MDL_key *key, TABLE_LIST *belong_to_view); void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx); @@ -212,13 +433,6 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, */ TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup); -sp_head * -sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, - String *name, sql_mode_t sql_mode, - stored_procedure_type type, - const char *returns, const char *params, - bool *free_sp_head); - bool load_charset(MEM_ROOT *mem_root, Field *field, CHARSET_INFO *dflt_cs, @@ -231,17 +445,6 @@ bool load_collation(MEM_ROOT *mem_root, void sp_returns_type(THD *thd, String &result, - sp_head *sp); - -bool show_create_sp(THD *thd, String *buf, - stored_procedure_type type, - const char *db, ulong dblen, - const char *name, ulong namelen, - const char *params, ulong paramslen, - const char *returns, ulong returnslen, - const char *body, ulong bodylen, - const st_sp_chistics &chistics, - const LEX_CSTRING *definer_user, - const LEX_CSTRING *definer_host, - sql_mode_t sql_mode); + const sp_head *sp); + #endif /* _SP_H_ */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index bb3275316cc..9737169ede6 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -540,10 +540,10 @@ sp_head::operator delete(void *ptr, size_t size) throw() } -sp_head::sp_head(stored_procedure_type type) +sp_head::sp_head(const Sp_handler *sph) :Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP), Database_qualified_name(&null_clex_str, &null_clex_str), - m_type(type), + m_handler(sph), m_flags(0), m_explicit_name(false), /* @@ -610,7 +610,7 @@ sp_head::init(LEX *lex) void -sp_head::init_sp_name(THD *thd, sp_name *spname) +sp_head::init_sp_name(THD *thd, const sp_name *spname) { DBUG_ENTER("sp_head::init_sp_name"); @@ -733,7 +733,7 @@ sp_head::~sp_head() Field * sp_head::create_result_field(uint field_max_length, const LEX_CSTRING *field_name, - TABLE *table) + TABLE *table) const { Field *field; LEX_CSTRING name; @@ -960,30 +960,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. @@ -1391,7 +1376,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 @@ -1406,8 +1390,7 @@ 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->suid() != SP_IS_NOT_SUID && @@ -1429,7 +1412,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, */ if (*save_ctx && check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, is_proc, FALSE)) + sp->m_db.str, sp->m_name.str, sp->m_handler, false)) { sp->m_security_ctx.restore_security_context(thd, *save_ctx); *save_ctx= 0; @@ -1450,20 +1433,17 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, so we can omit the security context switch for performance purposes. @param thd - @param sphead - @param is_proc - @param root_pctx @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, bool is_proc, Field *ret_value) +sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value) { bool has_column_type_refs= m_flags & HAS_COLUMN_TYPE_REFS; #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_security_ctx; if (has_column_type_refs && - set_routine_security_ctx(thd, this, is_proc, &save_security_ctx)) + set_routine_security_ctx(thd, this, &save_security_ctx)) return NULL; #endif sp_rcontext *res= sp_rcontext::create(thd, m_pcont, ret_value, @@ -1689,7 +1669,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, 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= rcontext_create(thd, false, return_value_fld))) + if (!(nctx= rcontext_create(thd, return_value_fld))) { thd->restore_active_arena(&call_arena, &backup_arena); err_status= TRUE; @@ -1760,7 +1740,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, #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; @@ -1904,7 +1884,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (! octx) { /* Create a temporary old context. */ - if (!(octx= rcontext_create(thd, true, NULL))) + if (!(octx= rcontext_create(thd, NULL))) { DBUG_PRINT("error", ("Could not create octx")); DBUG_RETURN(TRUE); @@ -1919,7 +1899,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) thd->spcont->callers_arena= thd; } - if (!(nctx= rcontext_create(thd, true, NULL))) + if (!(nctx= rcontext_create(thd, NULL))) { delete nctx; /* Delete nctx if it was init() that failed. */ thd->spcont= save_spcont; @@ -2042,7 +2022,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) @@ -2459,27 +2439,6 @@ sp_head::set_info(longlong created, longlong modified, void -sp_head::set_definer(const char *definer, uint definerlen) -{ - char user_name_holder[USERNAME_LENGTH + 1]; - LEX_CSTRING user_name= { user_name_holder, USERNAME_LENGTH }; - - char host_name_holder[HOSTNAME_LENGTH + 1]; - LEX_CSTRING host_name= { host_name_holder, HOSTNAME_LENGTH }; - - if (parse_user(definer, definerlen, user_name_holder, &user_name.length, - host_name_holder, &host_name.length) && - user_name.length && !host_name.length) - { - // 'user@' -> 'user@%' - host_name= host_not_specified; - } - - set_definer(&user_name, &host_name); -} - - -void sp_head::reset_thd_mem_root(THD *thd) { DBUG_ENTER("sp_head::reset_thd_mem_root"); @@ -2553,7 +2512,7 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) thd->security_ctx->priv_host))); 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; } @@ -2562,9 +2521,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 @@ -2572,13 +2530,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; @@ -2625,8 +2581,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 @@ -2634,13 +2589,10 @@ 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; @@ -2655,9 +2607,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); @@ -2784,6 +2733,29 @@ bool sp_head::add_instr_jump_forward_with_backpatch(THD *thd, } +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". diff --git a/sql/sp_head.h b/sql/sp_head.h index 1435bb460c3..f1d89a083ab 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -40,11 +40,6 @@ @{ */ -// Values for the type enum. This reflects the order of the enum declaration -// in the CREATE TABLE command. -//#define TYPE_ENUM_FUNCTION 1 #define TYPE_ENUM_PROCEDURE 2 #define -//TYPE_ENUM_TRIGGER 3 #define TYPE_ENUM_PROXY 4 - Item::Type sp_map_item_type(enum enum_field_types type); @@ -173,7 +168,7 @@ public: HAS_COLUMN_TYPE_REFS= 8192 }; - stored_procedure_type m_type; + const Sp_handler *m_handler; uint m_flags; // Boolean attributes of a stored routine Column_definition m_return_field_def; /**< This is used for FUNCTIONs only. */ @@ -220,7 +215,7 @@ public: m_sp_cache_version= version_arg; } - sp_rcontext *rcontext_create(THD *thd, bool is_proc, Field *ret_value); + sp_rcontext *rcontext_create(THD *thd, Field *retval); private: /** @@ -317,7 +312,7 @@ public: static void operator delete(void *ptr, size_t size) throw (); - sp_head(stored_procedure_type type); + sp_head(const Sp_handler *handler); /// Initialize after we have reset mem_root void @@ -325,7 +320,7 @@ public: /** Copy sp name from parser. */ void - init_sp_name(THD *thd, sp_name *spname); + init_sp_name(THD *thd, const sp_name *spname); /** Set the body-definition start position. */ void @@ -350,10 +345,11 @@ public: execute_procedure(THD *thd, List<Item> *args); static void - show_create_routine_get_fields(THD *thd, int type, List<Item> *fields); + show_create_routine_get_fields(THD *thd, const Sp_handler *sph, + List<Item> *fields); bool - show_create_routine(THD *thd, int type); + show_create_routine(THD *thd, const Sp_handler *sph); MEM_ROOT *get_main_mem_root() { return &main_mem_root; } @@ -376,6 +372,12 @@ public: spcont->last_label()); } + bool + add_instr_freturn(THD *thd, sp_pcontext *spcont, Item *item, LEX *lex); + + bool + add_instr_preturn(THD *thd, sp_pcontext *spcont); + Item *adjust_assignment_source(THD *thd, Item *val, Item *val2); /** @param thd - the current thd @@ -622,7 +624,7 @@ public: char *create_string(THD *thd, ulong *lenp); Field *create_result_field(uint field_max_length, const LEX_CSTRING *field_name, - TABLE *table); + TABLE *table) const; /** @@ -689,7 +691,12 @@ public: void set_info(longlong created, longlong modified, const st_sp_chistics &chistics, sql_mode_t sql_mode); - void set_definer(const char *definer, uint definerlen); + void set_definer(const char *definer, uint definerlen) + { + AUTHID tmp; + tmp.parse(definer, definerlen); + m_definer.copy(mem_root, &tmp.user, &tmp.host); + } void set_definer(const LEX_CSTRING *user_name, const LEX_CSTRING *host_name) { m_definer.copy(mem_root, user_name, host_name); @@ -713,8 +720,6 @@ public: */ void add_mark_lead(uint ip, List<sp_instr> *leads); - void recursion_level_error(THD *thd); - inline sp_instr * get_instr(uint i) { @@ -1859,8 +1864,7 @@ void sp_restore_security_context(THD *thd, Security_context *backup); 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); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ TABLE_LIST * diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 79acb996740..c2b2bdc54b5 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -754,6 +754,19 @@ static int traverse_role_graph_down(ACL_USER_BASE *, void *, int (*) (ACL_USER_BASE *, void *), int (*) (ACL_USER_BASE *, ACL_ROLE *, void *)); + +HASH *Sp_handler_procedure::get_priv_hash() const +{ + return &proc_priv_hash; +} + + +HASH *Sp_handler_function::get_priv_hash() const +{ + return &func_priv_hash; +} + + /* Enumeration of ACL/GRANT tables in the mysql database */ @@ -4973,10 +4986,11 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, static GRANT_NAME * routine_hash_search(const char *host, const char *ip, const char *db, - const char *user, const char *tname, bool proc, bool exact) + const char *user, const char *tname, const Sp_handler *sph, + bool exact) { return (GRANT_TABLE*) - name_hash_search(proc ? &proc_priv_hash : &func_priv_hash, + name_hash_search(sph->get_priv_hash(), host, ip, db, user, tname, exact, TRUE); } @@ -5351,13 +5365,14 @@ table_error: static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, TABLE *table, const LEX_USER &combo, const char *db, const char *routine_name, - bool is_proc, ulong rights, bool revoke_grant) + const Sp_handler *sph, + ulong rights, bool revoke_grant) { char grantor[USER_HOST_BUFF_SIZE]; int old_row_exists= 1; int error=0; ulong store_proc_rights; - HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; + HASH *hash= sph->get_priv_hash(); DBUG_ENTER("replace_routine_table"); if (revoke_grant && !grant_name->init_privs) // only inherited role privs @@ -5381,9 +5396,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1); table->field[3]->store(routine_name,(uint) strlen(routine_name), &my_charset_latin1); - table->field[4]->store((longlong)(is_proc ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION), - TRUE); + table->field[4]->store((longlong) sph->type(), true); store_record(table,record[1]); // store at pos 1 if (table->file->ha_index_read_idx_map(table->record[0], 0, @@ -5503,6 +5516,23 @@ struct PRIVS_TO_MERGE const char *db, *name; }; + +static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type) +{ + switch (type) { + case TYPE_ENUM_FUNCTION: + return PRIVS_TO_MERGE::FUNC; + case TYPE_ENUM_PROCEDURE: + return PRIVS_TO_MERGE::PROC; + case TYPE_ENUM_TRIGGER: + case TYPE_ENUM_PROXY: + break; + } + DBUG_ASSERT(0); + return PRIVS_TO_MERGE::PROC; +} + + static int init_role_for_merging(ACL_ROLE *role, void *context) { role->counter= 0; @@ -6627,7 +6657,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, @param thd Thread handle @param table_list List of routines to give grant - @param is_proc Is this a list of procedures? + @param sph SP handler @param user_list List of users to give grant @param rights Table level grant @param revoke_grant Is this is a REVOKE command? @@ -6637,7 +6667,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, @retval TRUE An error occurred. */ -bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, +bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, + const Sp_handler *sph, List <LEX_USER> &user_list, ulong rights, bool revoke_grant, bool write_to_binlog) { @@ -6657,7 +6688,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, if (!revoke_grant) { - if (sp_exist_routines(thd, table_list, is_proc)) + if (sph->sp_exist_routines(thd, table_list)) DBUG_RETURN(TRUE); } @@ -6698,7 +6729,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, db_name= table_list->db; table_name= table_list->table_name; grant_name= routine_hash_search(Str->host.str, NullS, db_name, - Str->user.str, table_name, is_proc, 1); + Str->user.str, table_name, sph, 1); if (!grant_name || !grant_name->init_privs) { if (revoke_grant) @@ -6712,8 +6743,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, Str->user.str, table_name, rights, TRUE); if (!grant_name || - my_hash_insert(is_proc ? - &proc_priv_hash : &func_priv_hash,(uchar*) grant_name)) + my_hash_insert(sph->get_priv_hash(), (uchar*) grant_name)) { result= TRUE; continue; @@ -6724,7 +6754,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, instead of TABLE directly. */ if (tables.procs_priv_table().no_such_table() || replace_routine_table(thd, grant_name, tables.procs_priv_table().table(), - *Str, db_name, table_name, is_proc, rights, + *Str, db_name, table_name, sph, rights, revoke_grant) != 0) { result= TRUE; @@ -6732,7 +6762,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } if (Str->is_role()) propagate_role_grants(find_acl_role(Str->user.str), - is_proc ? PRIVS_TO_MERGE::PROC : PRIVS_TO_MERGE::FUNC, + sp_privs_to_merge(sph->type()), db_name, table_name); } thd->mem_root= old_root; @@ -7341,16 +7371,10 @@ static bool grant_load(THD *thd, continue; } } - if (procs_priv.routine_type()->val_int() == TYPE_ENUM_PROCEDURE) - { - hash= &proc_priv_hash; - } - else - if (procs_priv.routine_type()->val_int() == TYPE_ENUM_FUNCTION) - { - hash= &func_priv_hash; - } - else + uint type= procs_priv.routine_type()->val_int(); + const Sp_handler *sph= Sp_handler::handler((stored_procedure_type) + type); + if (!sph || !(hash= sph->get_priv_hash())) { sql_print_warning("'procs_priv' entry '%s' " "ignored, bad routine type", @@ -8088,7 +8112,7 @@ bool check_grant_db(THD *thd, const char *db) thd Thread handler want_access Bits of privileges user needs to have procs List of routines to check. The user should have 'want_access' - is_proc True if the list is all procedures, else functions + sph SP handler no_errors If 0 then we write an error. The error is sent directly to the client @@ -8098,7 +8122,8 @@ bool check_grant_db(THD *thd, const char *db) ****************************************************************************/ bool check_grant_routine(THD *thd, ulong want_access, - TABLE_LIST *procs, bool is_proc, bool no_errors) + TABLE_LIST *procs, const Sp_handler *sph, + bool no_errors) { TABLE_LIST *table; Security_context *sctx= thd->security_ctx; @@ -8116,12 +8141,12 @@ bool check_grant_routine(THD *thd, ulong want_access, { GRANT_NAME *grant_proc; if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user, - table->table_name, is_proc, 0))) + table->table_name, sph, 0))) table->grant.privilege|= grant_proc->privs; if (role[0]) /* current role set check */ { if ((grant_proc= routine_hash_search("", NULL, table->db, role, - table->table_name, is_proc, 0))) + table->table_name, sph, 0))) table->grant.privilege|= grant_proc->privs; } @@ -8170,7 +8195,7 @@ err: */ bool check_routine_level_acl(THD *thd, const char *db, const char *name, - bool is_proc) + const Sp_handler *sph) { bool no_routine_acl= 1; GRANT_NAME *grant_proc; @@ -8179,7 +8204,7 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, if ((grant_proc= routine_hash_search(sctx->priv_host, sctx->ip, db, sctx->priv_user, - name, is_proc, 0))) + name, sph, 0))) no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); if (no_routine_acl && sctx->priv_role[0]) /* current set role check */ @@ -8187,7 +8212,7 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, if ((grant_proc= routine_hash_search("", NULL, db, sctx->priv_role, - name, is_proc, 0))) + name, sph, 0))) no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); } mysql_rwlock_unlock(&LOCK_grant); @@ -10503,6 +10528,45 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list) DBUG_RETURN(result); } + +static bool +mysql_revoke_sp_privs(THD *thd, + Grant_tables *tables, + const Sp_handler *sph, + const LEX_USER *lex_user) +{ + bool rc= false; + uint counter, revoked; + do { + HASH *hash= sph->get_priv_hash(); + for (counter= 0, revoked= 0 ; counter < hash->records ; ) + { + const char *user,*host; + GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); + user= safe_str(grant_proc->user); + host= safe_str(grant_proc->host.hostname); + + if (!strcmp(lex_user->user.str, user) && + !strcmp(lex_user->host.str, host)) + { + if (replace_routine_table(thd, grant_proc, + tables->procs_priv_table().table(), + *lex_user, + grant_proc->db, grant_proc->tname, + sph, ~(ulong)0, 1) == 0) + { + revoked= 1; + continue; + } + rc= true; // Something went wrong + } + counter++; + } + } while (revoked); + return rc; +} + + /* Revoke all privileges from a list of users. @@ -10519,7 +10583,7 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list) bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) { - uint counter, revoked, is_proc; + uint counter, revoked; int result; ACL_DB *acl_db; DBUG_ENTER("mysql_revoke_all"); @@ -10648,32 +10712,9 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) } while (revoked); /* Remove procedure access */ - for (is_proc=0; is_proc<2; is_proc++) do { - HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; - for (counter= 0, revoked= 0 ; counter < hash->records ; ) - { - const char *user,*host; - GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); - user= safe_str(grant_proc->user); - host= safe_str(grant_proc->host.hostname); - - if (!strcmp(lex_user->user.str,user) && - !strcmp(lex_user->host.str, host)) - { - if (replace_routine_table(thd, grant_proc, - tables.procs_priv_table().table(), - *lex_user, - grant_proc->db, grant_proc->tname, - is_proc, ~(ulong)0, 1) == 0) - { - revoked= 1; - continue; - } - result= -1; // Something went wrong - } - counter++; - } - } while (revoked); + if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) || + mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user)) + result= -1; ACL_USER_BASE *user_or_role; /* remove role grants */ @@ -10825,11 +10866,11 @@ Silence_routine_definer_errors::handle_condition( */ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc) + const Sp_handler *sph) { uint counter, revoked; int result; - HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; + HASH *hash= sph->get_priv_hash(); Silence_routine_definer_errors error_handler; DBUG_ENTER("sp_revoke_privileges"); @@ -10865,7 +10906,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, if (replace_routine_table(thd, grant_proc, tables.procs_priv_table().table(), lex_user, grant_proc->db, grant_proc->tname, - is_proc, ~(ulong)0, 1) == 0) + sph, ~(ulong)0, 1) == 0) { revoked= 1; continue; @@ -10890,7 +10931,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, @param thd The current thread. @param sp_db @param sp_name - @param is_proc + @param sph @return @retval FALSE Success @@ -10898,7 +10939,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, */ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc) + const Sp_handler *sph) { Security_context *sctx= thd->security_ctx; LEX_USER *combo; @@ -10960,7 +11001,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, as all errors will be handled later. */ thd->push_internal_handler(&error_handler); - result= mysql_routine_grant(thd, tables, is_proc, user_list, + result= mysql_routine_grant(thd, tables, sph, user_list, DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE); thd->pop_internal_handler(); DBUG_RETURN(result); @@ -11746,7 +11787,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, ****************************************************************************/ bool check_routine_level_acl(THD *thd, const char *db, const char *name, - bool is_proc) + const Sp_handler *sph) { return FALSE; } diff --git a/sql/sql_acl.h b/sql/sql_acl.h index f9646649c93..6164c6fa57d 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -215,7 +215,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list, int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list, List <LEX_COLUMN> &column_list, ulong rights, bool revoke); -bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc, +bool mysql_routine_grant(THD *thd, TABLE_LIST *table, const Sp_handler *sph, List <LEX_USER> &user_list, ulong rights, bool revoke, bool write_to_binlog); bool grant_init(); @@ -231,7 +231,8 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, bool check_grant_all_columns(THD *thd, ulong want_access, Field_iterator_table_ref *fields); bool check_grant_routine(THD *thd, ulong want_access, - TABLE_LIST *procs, bool is_proc, bool no_error); + TABLE_LIST *procs, const Sp_handler *sph, + bool no_error); bool check_grant_db(THD *thd,const char *db); bool check_global_access(THD *thd, ulong want_access, bool no_errors= false); bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, @@ -257,11 +258,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list); void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table); bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc); + const Sp_handler *sph); bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc); + const Sp_handler *sph); bool check_routine_level_acl(THD *thd, const char *db, const char *name, - bool is_proc); + const Sp_handler *sph); bool is_acl_user(const char *host, const char *user); int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond); int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7041e22075b..60247c83a08 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3127,7 +3127,7 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, DEBUG_SYNC(thd, "after_shared_lock_pname"); /* Ensures the routine is up-to-date and cached, if exists. */ - if (sp_cache_routine(thd, rt, has_prelocking_list, &sp)) + if (rt->sp_cache_routine(thd, has_prelocking_list, &sp)) DBUG_RETURN(TRUE); /* Remember the version of the routine in the parse tree. */ @@ -3152,7 +3152,7 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, Validating routine version is unnecessary, since CALL does not affect the prepared statement prelocked list. */ - if (sp_cache_routine(thd, rt, FALSE, &sp)) + if (rt->sp_cache_routine(thd, false, &sp)) DBUG_RETURN(TRUE); } } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 145ccb76d11..3851a258bd9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -7475,4 +7475,34 @@ void AUTHID::copy(MEM_ROOT *mem_root, const LEX_CSTRING *user_name, } +/* + Set from a string in 'user@host' format. + This method resebmles parse_user(), + but does not need temporary buffers. +*/ +void AUTHID::parse(const char *str, size_t length) +{ + const char *p= strrchr(str, '@'); + if (!p) + { + user.str= str; + user.length= length; + host= null_clex_str; + } + else + { + user.str= str; + user.length= (size_t) (p - str); + host.str= p + 1; + host.length= (size_t) (length - user.length - 1); + if (user.length && !host.length) + host= host_not_specified; // 'user@' -> 'user@%' + } + if (user.length > USERNAME_LENGTH) + user.length= USERNAME_LENGTH; + if (host.length > HOSTNAME_LENGTH) + host.length= HOSTNAME_LENGTH; +} + + #endif /* !defined(MYSQL_CLIENT) */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 2fe788264d3..e449fb8c298 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4507,10 +4507,16 @@ void st_select_lex::set_explain_type(bool on_the_fly) if (join) { bool uses_cte= false; - for (JOIN_TAB *tab= first_explain_order_tab(join); tab; - tab= next_explain_order_tab(join, tab)) + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, + WITH_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { - if (tab->table && tab->table->pos_in_table_list->with) + /* + pos_in_table_list=NULL for e.g. post-join aggregation JOIN_TABs. + */ + if (tab->table && tab->table->pos_in_table_list && + tab->table->pos_in_table_list->with) { uses_cte= true; break; @@ -5143,7 +5149,7 @@ bool LEX::init_internal_variable(struct sys_var_with_base *variable, bool LEX::is_trigger_new_or_old_reference(const LEX_CSTRING *name) { - return sphead && sphead->m_type == TYPE_ENUM_TRIGGER && + return sphead && sphead->m_handler->type() == TYPE_ENUM_TRIGGER && name->length == 3 && (!my_strcasecmp(system_charset_info, name->str, "NEW") || !my_strcasecmp(system_charset_info, name->str, "OLD")); @@ -5819,13 +5825,13 @@ sp_name *LEX::make_sp_name(THD *thd, LEX_CSTRING *name1, LEX_CSTRING *name2) } -sp_head *LEX::make_sp_head(THD *thd, sp_name *name, - enum stored_procedure_type type) +sp_head *LEX::make_sp_head(THD *thd, const sp_name *name, + const Sp_handler *sph) { sp_head *sp; /* Order is important here: new - reset - init */ - if ((sp= new sp_head(type))) + if ((sp= new sp_head(sph))) { sp->reset_thd_mem_root(thd); sp->init(this); @@ -6121,7 +6127,7 @@ bool LEX::maybe_start_compound_statement(THD *thd) { if (!sphead) { - if (!make_sp_head(thd, NULL, TYPE_ENUM_PROCEDURE)) + if (!make_sp_head(thd, NULL, &sp_handler_procedure)) return true; sphead->set_suid(SP_IS_NOT_SUID); sphead->set_body_start(thd, thd->m_parser_state->m_lip.get_cpp_ptr()); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 822c2554951..4fe740185d5 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1287,6 +1287,7 @@ struct st_sp_chistics enum enum_sp_data_access daccess; void init() { bzero(this, sizeof(*this)); } void set(const st_sp_chistics &other) { *this= other; } + bool read_from_mysql_proc_row(THD *thd, TABLE *table); }; @@ -3155,24 +3156,22 @@ public: void set_stmt_init(); sp_name *make_sp_name(THD *thd, LEX_CSTRING *name); sp_name *make_sp_name(THD *thd, LEX_CSTRING *name1, LEX_CSTRING *name2); - sp_head *make_sp_head(THD *thd, sp_name *name, - enum stored_procedure_type type); - sp_head *make_sp_head_no_recursive(THD *thd, sp_name *name, - enum stored_procedure_type type) + sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph); + sp_head *make_sp_head_no_recursive(THD *thd, const sp_name *name, + const Sp_handler *sph) { if (!sphead) - return make_sp_head(thd, name, type); - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), - stored_procedure_type_to_str(type)); + return make_sp_head(thd, name, sph); + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str()); return NULL; } sp_head *make_sp_head_no_recursive(THD *thd, DDL_options_st options, sp_name *name, - enum stored_procedure_type type) + const Sp_handler *sph) { if (add_create_options_with_check(options)) return NULL; - return make_sp_head_no_recursive(thd, name, type); + return make_sp_head_no_recursive(thd, name, sph); } bool init_internal_variable(struct sys_var_with_base *variable, const LEX_CSTRING *name); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1d4a2ac8851..0c1c78d59cf 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2937,13 +2937,13 @@ static int mysql_create_routine(THD *thd, LEX *lex) { if (check_routine_access(thd, ALTER_PROC_ACL, lex->sphead->m_db.str, lex->sphead->m_name.str, - lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) + Sp_handler::handler(lex->sql_command), 0)) return true; } const LEX_CSTRING *name= lex->sphead->name(); #ifdef HAVE_DLOPEN - if (lex->sphead->m_type == TYPE_ENUM_FUNCTION) + if (lex->sphead->m_handler->type() == TYPE_ENUM_FUNCTION) { udf_func *udf = find_udf(name->str, name->length); @@ -2959,7 +2959,7 @@ static int mysql_create_routine(THD *thd, LEX *lex) return true; WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) - if (!sp_create_routine(thd, lex->sphead->m_type, lex->sphead)) + if (!lex->sphead->m_handler->sp_create_routine(thd, lex->sphead)) { #ifndef NO_EMBEDDED_ACCESS_CHECKS /* only add privileges if really neccessary */ @@ -3006,10 +3006,10 @@ static int mysql_create_routine(THD *thd, LEX *lex) if (sp_automatic_privileges && !opt_noacl && check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS, lex->sphead->m_db.str, name->str, - lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1)) + Sp_handler::handler(lex->sql_command), 1)) { if (sp_grant_privileges(thd, lex->sphead->m_db.str, name->str, - lex->sql_command == SQLCOM_CREATE_PROCEDURE)) + Sp_handler::handler(lex->sql_command))) push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_PROC_AUTO_GRANT_FAIL, ER_THD(thd, ER_PROC_AUTO_GRANT_FAIL)); thd->clear_error(); @@ -5353,16 +5353,16 @@ end_with_restore_list: if (lex->type == TYPE_ENUM_PROCEDURE || lex->type == TYPE_ENUM_FUNCTION) { + const Sp_handler *sph= Sp_handler::handler((stored_procedure_type) + lex->type); uint grants= lex->all_privileges ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL) : lex->grant; - if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, - lex->type == TYPE_ENUM_PROCEDURE, 0)) + if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, sph, 0)) goto error; /* Conditionally writes to binlog */ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) - res= mysql_routine_grant(thd, all_tables, - lex->type == TYPE_ENUM_PROCEDURE, + res= mysql_routine_grant(thd, all_tables, sph, lex->users_list, grants, lex->sql_command == SQLCOM_REVOKE, TRUE); if (!res) @@ -5768,15 +5768,15 @@ end_with_restore_list: goto error; if (check_routine_access(thd, EXECUTE_ACL, lex->spname->m_db.str, - lex->spname->m_name.str, TRUE, FALSE)) + lex->spname->m_name.str, &sp_handler_procedure, + false)) goto error; /* By this moment all needed SPs should be in cache so no need to look into DB. */ - if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname, - &thd->sp_proc_cache, TRUE))) + if (!(sp= sp_handler_procedure.sp_find_routine(thd, lex->spname, true))) { /* sp_find_routine can have issued an ER_SP_RECURSION_LIMIT error. @@ -5821,13 +5821,9 @@ end_with_restore_list: case SQLCOM_ALTER_FUNCTION: { int sp_result; - enum stored_procedure_type type; - type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str, - lex->spname->m_name.str, - lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0)) + lex->spname->m_name.str, sph, 0)) goto error; /* @@ -5837,7 +5833,7 @@ end_with_restore_list: already puts on CREATE FUNCTION. */ /* Conditionally writes to binlog */ - sp_result= sp_update_routine(thd, type, lex->spname, &lex->sp_chistics); + sp_result= sph->sp_update_routine(thd, lex->spname, &lex->sp_chistics); switch (sp_result) { case SP_OK: @@ -5899,19 +5895,17 @@ end_with_restore_list: #endif int sp_result; - enum stored_procedure_type type; - type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); const char *db= lex->spname->m_db.str; const char *name= lex->spname->m_name.str; if (check_routine_access(thd, ALTER_PROC_ACL, db, name, - lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) + Sp_handler::handler(lex->sql_command), 0)) goto error; WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) /* Conditionally writes to binlog */ - sp_result= sp_drop_routine(thd, type, lex->spname); + sp_result= sph->sp_drop_routine(thd, lex->spname); #ifndef NO_EMBEDDED_ACCESS_CHECKS /* @@ -5935,7 +5929,7 @@ end_with_restore_list: if (sp_result != SP_KEY_NOT_FOUND && sp_automatic_privileges && !opt_noacl && sp_revoke_privileges(thd, db, name, - lex->sql_command == SQLCOM_DROP_PROCEDURE)) + Sp_handler::handler(lex->sql_command))) { push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_PROC_AUTO_REVOKE_FAIL, @@ -5973,21 +5967,14 @@ end_with_restore_list: break; } case SQLCOM_SHOW_CREATE_PROC: - { -#ifdef WITH_WSREP - if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error; -#endif /* WITH_WSREP */ - if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname)) - goto error; - break; - } case SQLCOM_SHOW_CREATE_FUNC: { + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); #ifdef WITH_WSREP if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error; #endif /* WITH_WSREP */ - if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname)) - goto error; + if (sph->sp_show_create_routine(thd, lex->spname)) + goto error; break; } case SQLCOM_SHOW_PROC_CODE: @@ -5995,13 +5982,11 @@ end_with_restore_list: { #ifndef DBUG_OFF sp_head *sp; - stored_procedure_type type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); #ifdef WITH_WSREP if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error; #endif /* WITH_WSREP */ - if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp)) + if (sph->sp_cache_routine(thd, lex->spname, false, &sp)) goto error; if (!sp || sp->show_routine_code(thd)) { @@ -7061,7 +7046,7 @@ deny: bool check_routine_access(THD *thd, ulong want_access, const char *db, const char *name, - bool is_proc, bool no_errors) + const Sp_handler *sph, bool no_errors) { TABLE_LIST tables[1]; @@ -7090,7 +7075,7 @@ check_routine_access(THD *thd, ulong want_access, const char *db, 0, no_errors)) return TRUE; - return check_grant_routine(thd, want_access, tables, is_proc, no_errors); + return check_grant_routine(thd, want_access, tables, sph, no_errors); } @@ -7108,7 +7093,7 @@ check_routine_access(THD *thd, ulong want_access, const char *db, */ bool check_some_routine_access(THD *thd, const char *db, const char *name, - bool is_proc) + const Sp_handler *sph) { ulong save_priv; /* @@ -7125,7 +7110,7 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name, if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, NULL, 0, 1) || (save_priv & SHOW_PROC_ACLS)) return FALSE; - return check_routine_level_acl(thd, db, name, is_proc); + return check_routine_level_acl(thd, db, name, sph); } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index bf241d04e7b..c97d824ffa1 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -152,9 +152,10 @@ bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, bool no_errors); bool check_routine_access(THD *thd,ulong want_access,const char *db, const char *name, - bool is_proc, bool no_errors); + const Sp_handler *sph, bool no_errors); bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); -bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc); +bool check_some_routine_access(THD *thd, const char *db, const char *name, + const Sp_handler *sph); bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, bool any_combination_of_privileges_will_do, uint number, @@ -166,7 +167,8 @@ inline bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, bool no_errors) { return false; } inline bool check_routine_access(THD *thd,ulong want_access, const char *db, - const char *name, bool is_proc, bool no_errors) + const char *name, + const Sp_handler *sph, bool no_errors) { return false; } inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) { @@ -174,7 +176,8 @@ inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) return false; } inline bool check_some_routine_access(THD *thd, const char *db, - const char *name, bool is_proc) + const char *name, + const Sp_handler *sph) { return false; } inline bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index dfc1e3add37..4aef8adff09 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2040,13 +2040,14 @@ static int mysql_test_show_binlogs(Prepared_statement *stmt) TRUE error, error message is set in THD */ -static int mysql_test_show_create_routine(Prepared_statement *stmt, int type) +static int mysql_test_show_create_routine(Prepared_statement *stmt, + const Sp_handler *sph) { DBUG_ENTER("mysql_test_show_binlogs"); THD *thd= stmt->thd; List<Item> fields; - sp_head::show_create_routine_get_fields(thd, type, &fields); + sp_head::show_create_routine_get_fields(thd, sph, &fields); DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); } @@ -2436,14 +2437,14 @@ static bool check_prepared_statement(Prepared_statement *stmt) break; #endif /* EMBEDDED_LIBRARY */ case SQLCOM_SHOW_CREATE_PROC: - if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_PROCEDURE)) == 2) + if ((res= mysql_test_show_create_routine(stmt, &sp_handler_procedure)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); } break; case SQLCOM_SHOW_CREATE_FUNC: - if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_FUNCTION)) == 2) + if ((res= mysql_test_show_create_routine(stmt, &sp_handler_function)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 438c276c096..d27d4c943df 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3405,8 +3405,14 @@ void JOIN::exec_inner() if (zero_result_cause) { - if (select_lex->have_window_funcs()) + if (select_lex->have_window_funcs() && send_row_on_empty_set()) { + /* + The query produces just one row but it has window functions. + + The only way to compute the value of window function(s) is to + run the entire window function computation step (there is no shortcut). + */ const_tables= table_count; first_select= sub_select_postjoin_aggr; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index fd0d11bf1bb..ed0d9ae15e9 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -5876,16 +5876,11 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, TABLE_SHARE share; TABLE tbl; CHARSET_INFO *cs= system_charset_info; - char params_buff[MAX_FIELD_WIDTH], returns_buff[MAX_FIELD_WIDTH], - sp_db_buff[NAME_LEN], sp_name_buff[NAME_LEN], path[FN_REFLEN], - definer_buff[DEFINER_LENGTH + 1]; - String params(params_buff, sizeof(params_buff), cs); - String returns(returns_buff, sizeof(returns_buff), cs); - String sp_db(sp_db_buff, sizeof(sp_db_buff), cs); - String sp_name(sp_name_buff, sizeof(sp_name_buff), cs); - String definer(definer_buff, sizeof(definer_buff), cs); + LEX_CSTRING definer, params, returns= empty_clex_str; + LEX_CSTRING db, name; + char path[FN_REFLEN]; sp_head *sp; - stored_procedure_type routine_type; + const Sp_handler *sph; bool free_sp_head; bool error= 0; DBUG_ENTER("store_schema_params"); @@ -5894,48 +5889,44 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, (void) build_table_filename(path, sizeof(path), "", "", "", 0); init_tmp_table_share(thd, &share, "", 0, "", path); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_DB], &sp_db); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_NAME], &sp_name); - get_field(thd->mem_root,proc_table->field[MYSQL_PROC_FIELD_DEFINER],&definer); - routine_type= (stored_procedure_type) proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int(); + proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); + proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); + proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); + sph= Sp_handler::handler((stored_procedure_type) proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int()); + if (!sph) + sph= &sp_handler_procedure; if (!full_access) - full_access= !strcmp(sp_user, definer.ptr()); + full_access= !strcmp(sp_user, definer.str); if (!full_access && - check_some_routine_access(thd, sp_db.ptr(),sp_name.ptr(), - routine_type == TYPE_ENUM_PROCEDURE)) + check_some_routine_access(thd, db.str, name.str, sph)) DBUG_RETURN(0); - params.length(0); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST], - ¶ms); - returns.length(0); - if (routine_type == TYPE_ENUM_FUNCTION) - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_RETURNS], - &returns); - - sp= sp_load_for_information_schema(thd, proc_table, &sp_db, &sp_name, - (ulong) proc_table-> - field[MYSQL_PROC_FIELD_SQL_MODE]->val_int(), - routine_type, - returns.c_ptr_safe(), - params.c_ptr_safe(), - &free_sp_head); - + proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root, + ¶ms); + if (sph->type() == TYPE_ENUM_FUNCTION) + proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, + &returns); + sp= sph->sp_load_for_information_schema(thd, proc_table, db, name, + params, returns, + (ulong) proc_table-> + field[MYSQL_PROC_FIELD_SQL_MODE]-> + val_int(), + &free_sp_head); if (sp) { Field *field; - String tmp_string; - if (routine_type == TYPE_ENUM_FUNCTION) + LEX_CSTRING tmp_string; + if (sph->type() == TYPE_ENUM_FUNCTION) { restore_record(table, s->default_values); table->field[0]->store(STRING_WITH_LEN("def"), cs); - table->field[1]->store(sp_db.ptr(), sp_db.length(), cs); - table->field[2]->store(sp_name.ptr(), sp_name.length(), cs); + table->field[1]->store(db, cs); + table->field[2]->store(name, cs); table->field[3]->store((longlong) 0, TRUE); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_MYSQL_TYPE], - &tmp_string); - table->field[15]->store(tmp_string.ptr(), tmp_string.length(), cs); + proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root, + &tmp_string); + table->field[15]->store(tmp_string, cs); field= sp->m_return_field_def.make_field(&share, thd->mem_root, &empty_clex_str); field->table= &tbl; @@ -5973,16 +5964,16 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, restore_record(table, s->default_values); table->field[0]->store(STRING_WITH_LEN("def"), cs); - table->field[1]->store(sp_db.ptr(), sp_db.length(), cs); - table->field[2]->store(sp_name.ptr(), sp_name.length(), cs); + table->field[1]->store(db, cs); + table->field[2]->store(name, cs); table->field[3]->store((longlong) i + 1, TRUE); table->field[4]->store(tmp_buff, strlen(tmp_buff), cs); table->field[4]->set_notnull(); table->field[5]->store(spvar->name.str, spvar->name.length, cs); table->field[5]->set_notnull(); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_MYSQL_TYPE], - &tmp_string); - table->field[15]->store(tmp_string.ptr(), tmp_string.length(), cs); + proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root, + &tmp_string); + table->field[15]->store(tmp_string, cs); field= spvar->field_def.make_field(&share, thd->mem_root, &spvar->name); @@ -6009,63 +6000,57 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, MYSQL_TIME time; LEX *lex= thd->lex; CHARSET_INFO *cs= system_charset_info; - char sp_db_buff[SAFE_NAME_LEN + 1], sp_name_buff[NAME_LEN + 1], - definer_buff[DEFINER_LENGTH + 1], - returns_buff[MAX_FIELD_WIDTH]; - - String sp_db(sp_db_buff, sizeof(sp_db_buff), cs); - String sp_name(sp_name_buff, sizeof(sp_name_buff), cs); - String definer(definer_buff, sizeof(definer_buff), cs); - String returns(returns_buff, sizeof(returns_buff), cs); + const Sp_handler *sph; + LEX_CSTRING db, name, definer, returns= empty_clex_str; - proc_table->field[MYSQL_PROC_FIELD_DB]->val_str(&sp_db); - proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str(&sp_name); - proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str(&definer); + proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); + proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); + proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); + sph= Sp_handler::handler((stored_procedure_type) + proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int()); + if (!sph) + sph= &sp_handler_procedure; if (!full_access) - full_access= !strcmp(sp_user, definer.c_ptr_safe()); + full_access= !strcmp(sp_user, definer.str); if (!full_access && - check_some_routine_access(thd, sp_db.c_ptr_safe(), sp_name.c_ptr_safe(), - proc_table->field[MYSQL_PROC_MYSQL_TYPE]-> - val_int() == TYPE_ENUM_PROCEDURE)) + check_some_routine_access(thd, db.str, name.str, sph)) return 0; if ((lex->sql_command == SQLCOM_SHOW_STATUS_PROC && - proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == - TYPE_ENUM_PROCEDURE) || + sph->type() == TYPE_ENUM_PROCEDURE) || (lex->sql_command == SQLCOM_SHOW_STATUS_FUNC && - proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == - TYPE_ENUM_FUNCTION) || + sph->type() == TYPE_ENUM_FUNCTION) || (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0) { restore_record(table, s->default_values); if (!wild || !wild[0] || !wild_case_compare(system_charset_info, - sp_name.c_ptr_safe(), wild)) + name.str, wild)) { int enum_idx= (int) proc_table->field[MYSQL_PROC_FIELD_ACCESS]->val_int(); - table->field[3]->store(sp_name.ptr(), sp_name.length(), cs); + table->field[3]->store(name, cs); copy_field_as_string(table->field[0], proc_table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]); table->field[1]->store(STRING_WITH_LEN("def"), cs); - table->field[2]->store(sp_db.ptr(), sp_db.length(), cs); + table->field[2]->store(db, cs); copy_field_as_string(table->field[4], proc_table->field[MYSQL_PROC_MYSQL_TYPE]); - if (proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == - TYPE_ENUM_FUNCTION) + if (sph->type() == TYPE_ENUM_FUNCTION) { sp_head *sp; bool free_sp_head; - proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str(&returns); - sp= sp_load_for_information_schema(thd, proc_table, &sp_db, &sp_name, - (ulong) proc_table-> - field[MYSQL_PROC_FIELD_SQL_MODE]-> - val_int(), - TYPE_ENUM_FUNCTION, - returns.c_ptr_safe(), - "", &free_sp_head); - + proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, + &returns); + sp= sph->sp_load_for_information_schema(thd, proc_table, + db, name, + empty_clex_str /*params*/, + returns, + (ulong) proc_table-> + field[MYSQL_PROC_FIELD_SQL_MODE]-> + val_int(), + &free_sp_head); if (sp) { char path[FN_REFLEN]; @@ -6115,7 +6100,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, copy_field_as_string(table->field[26], proc_table->field[MYSQL_PROC_FIELD_COMMENT]); - table->field[27]->store(definer.ptr(), definer.length(), cs); + table->field[27]->store(definer, cs); copy_field_as_string(table->field[28], proc_table-> field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]); diff --git a/sql/sql_string.h b/sql/sql_string.h index 845ed4c9746..adaef04fee4 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -471,6 +471,7 @@ public: bool append(const char *s); bool append(const LEX_STRING *ls) { return append(ls->str, ls->length); } bool append(const LEX_CSTRING *ls) { return append(ls->str, ls->length); } + bool append(const LEX_CSTRING &ls) { return append(ls.str, ls.length); } bool append(const char *s, uint32 arg_length); bool append(const char *s, uint32 arg_length, CHARSET_INFO *cs); bool append_ulonglong(ulonglong val); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b3f56f314d2..92cce0666bc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2948,7 +2948,7 @@ ev_sql_stmt: my_yyabort_error((ER_EVENT_RECURSION_FORBIDDEN, MYF(0))); if (!lex->make_sp_head(thd, lex->event_parse_data->identifier, - TYPE_ENUM_PROCEDURE)) + &sp_handler_procedure)) MYSQL_YYABORT; lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); @@ -3046,7 +3046,7 @@ call: lex->sql_command= SQLCOM_CALL; lex->spname= $2; lex->value_list.empty(); - sp_add_used_routine(lex, thd, $2, TYPE_ENUM_PROCEDURE); + sp_handler_procedure.add_used_routine(lex, thd, $2); } opt_sp_cparam_list {} ; @@ -3880,20 +3880,9 @@ sp_proc_stmt_return: { LEX *lex= Lex; sp_head *sp= lex->sphead; - - if (sp->m_type != TYPE_ENUM_FUNCTION) - my_yyabort_error((ER_SP_BADRETURN, MYF(0))); - - sp_instr_freturn *i; - - i= new (thd->mem_root) - sp_instr_freturn(sp->instructions(), lex->spcont, $3, - sp->m_return_field_def.type_handler(), lex); - if (i == NULL || sp->add_instr(i)) - MYSQL_YYABORT; - sp->m_flags|= sp_head::HAS_RETURN; - - if (sp->restore_lex(thd)) + if (sp->m_handler->add_instr_freturn(thd, sp, lex->spcont, + $3, lex) || + sp->restore_lex(thd)) MYSQL_YYABORT; } ; @@ -16607,7 +16596,7 @@ trigger_tail: (*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($17); lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr(); - if (!lex->make_sp_head(thd, $4, TYPE_ENUM_TRIGGER)) + if (!lex->make_sp_head(thd, $4, &sp_handler_trigger)) MYSQL_YYABORT; lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); @@ -16681,7 +16670,7 @@ sf_tail: sp_name { if (!Lex->make_sp_head_no_recursive(thd, $1, $2, - TYPE_ENUM_FUNCTION)) + &sp_handler_function)) MYSQL_YYABORT; } sp_parenthesized_fdparam_list @@ -16716,7 +16705,7 @@ sp_tail: opt_if_not_exists sp_name { if (!Lex->make_sp_head_no_recursive(thd, $1, $2, - TYPE_ENUM_PROCEDURE)) + &sp_handler_procedure)) MYSQL_YYABORT; } sp_parenthesized_pdparam_list diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 114b117b7cb..7d55780d26f 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -2389,7 +2389,7 @@ ev_sql_stmt: my_yyabort_error((ER_EVENT_RECURSION_FORBIDDEN, MYF(0))); if (!lex->make_sp_head(thd, lex->event_parse_data->identifier, - TYPE_ENUM_PROCEDURE)) + &sp_handler_procedure)) MYSQL_YYABORT; lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); @@ -2492,7 +2492,7 @@ call: lex->sql_command= SQLCOM_CALL; lex->spname= $2; lex->value_list.empty(); - sp_add_used_routine(lex, thd, $2, TYPE_ENUM_PROCEDURE); + sp_handler_procedure.add_used_routine(lex, thd, $2); } opt_sp_cparam_list {} ; @@ -3380,7 +3380,7 @@ sp_statement: MYSQL_YYABORT; lex->sql_command= SQLCOM_CALL; lex->value_list.empty(); - sp_add_used_routine(lex, thd, lex->spname, TYPE_ENUM_PROCEDURE); + sp_handler_procedure.add_used_routine(lex, thd, lex->spname); } opt_sp_cparam_list | ident_directly_assignable '.' ident @@ -3390,7 +3390,7 @@ sp_statement: MYSQL_YYABORT; lex->sql_command= SQLCOM_CALL; lex->value_list.empty(); - sp_add_used_routine(lex, thd, lex->spname, TYPE_ENUM_PROCEDURE); + sp_handler_procedure.add_used_routine(lex, thd, lex->spname); } opt_sp_cparam_list ; @@ -3454,36 +3454,16 @@ sp_proc_stmt_return: { LEX *lex= Lex; sp_head *sp= lex->sphead; - - if (sp->m_type != TYPE_ENUM_FUNCTION) - my_yyabort_error((ER_SP_BADRETURN, MYF(0))); - - sp_instr_freturn *i; - - i= new (thd->mem_root) - sp_instr_freturn(sp->instructions(), lex->spcont, $3, - sp->m_return_field_def.type_handler(), lex); - if (i == NULL || sp->add_instr(i)) - MYSQL_YYABORT; - sp->m_flags|= sp_head::HAS_RETURN; - - if (sp->restore_lex(thd)) + if (sp->m_handler->add_instr_freturn(thd, sp, lex->spcont, + $3, lex) || + sp->restore_lex(thd)) MYSQL_YYABORT; } | RETURN_SYM { LEX *lex= Lex; sp_head *sp= lex->sphead; - - if (sp->m_type != TYPE_ENUM_PROCEDURE) - { - thd->parse_error(); - MYSQL_YYABORT; - } - sp_instr_preturn *i; - i= new (thd->mem_root) - sp_instr_preturn(sp->instructions(), lex->spcont); - if (i == NULL || sp->add_instr(i)) + if (sp->m_handler->add_instr_preturn(thd, sp, lex->spcont)) MYSQL_YYABORT; } ; @@ -16844,7 +16824,7 @@ trigger_tail: (*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($17); lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr(); - if (!lex->make_sp_head(thd, $4, TYPE_ENUM_TRIGGER)) + if (!lex->make_sp_head(thd, $4, &sp_handler_trigger)) MYSQL_YYABORT; lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); @@ -16920,7 +16900,7 @@ sf_tail: sp_name { if (!Lex->make_sp_head_no_recursive(thd, $1, $2, - TYPE_ENUM_FUNCTION)) + &sp_handler_function)) MYSQL_YYABORT; } opt_sp_parenthesized_fdparam_list @@ -16958,7 +16938,7 @@ sp_tail: opt_if_not_exists sp_name { if (!Lex->make_sp_head_no_recursive(thd, $1, $2, - TYPE_ENUM_PROCEDURE)) + &sp_handler_procedure)) MYSQL_YYABORT; } opt_sp_parenthesized_pdparam_list diff --git a/sql/structs.h b/sql/structs.h index 982eda99d30..97702e5727b 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -221,6 +221,8 @@ struct AUTHID l->length= strxmov(buf, user.str, "@", host.str, NullS) - buf; } } + void parse(const char *str, size_t length); + bool read_from_mysql_proc_row(THD *thd, TABLE *table); }; diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 87eb97e4fec..ba8312d302a 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -2236,26 +2236,23 @@ static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len) sp_head *sp = thd->lex->sphead; sql_mode_t saved_mode= thd->variables.sql_mode; String retstr(64); + LEX_CSTRING returns= empty_clex_str; retstr.set_charset(system_charset_info); log_query.set_charset(system_charset_info); - if (sp->m_type == TYPE_ENUM_FUNCTION) + if (sp->m_handler->type() == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); + returns= retstr.lex_cstring(); } - if (!show_create_sp(thd, &log_query, - sp->m_type, - (sp->m_explicit_name ? sp->m_db.str : NULL), - (sp->m_explicit_name ? sp->m_db.length : 0), - sp->m_name.str, sp->m_name.length, - sp->m_params.str, sp->m_params.length, - retstr.c_ptr(), retstr.length(), - sp->m_body.str, sp->m_body.length, - sp->chistics(), &(thd->lex->definer->user), - &(thd->lex->definer->host), - saved_mode)) + if (!sp->m_handler-> + show_create_sp(thd, &log_query, + sp->m_explicit_name ? sp->m_db : null_clex_str, + sp->m_name, sp->m_params, returns, + sp->m_body, sp->chistics(), thd->lex->definer[0], + saved_mode)) { WSREP_WARN("SP create string failed: schema: %s, query: %s", (thd->db ? thd->db : "(null)"), thd->query()); |