diff options
Diffstat (limited to 'sql/sp.cc')
-rw-r--r-- | sql/sp.cc | 1585 |
1 files changed, 790 insertions, 795 deletions
diff --git a/sql/sp.cc b/sql/sp.cc index 3af51b82521..cc545992857 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -13,7 +13,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #include "mysql_priv.h" #include "sp.h" #include "sp_head.h" @@ -37,7 +36,8 @@ static int db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong sql_mode, const char *params, const char *returns, const char *body, st_sp_chistics &chistics, - const char *definer, longlong created, longlong modified); + const char *definer, longlong created, longlong modified, + Stored_program_creation_ctx *creation_ctx); /* * @@ -49,7 +49,7 @@ enum { MYSQL_PROC_FIELD_DB = 0, MYSQL_PROC_FIELD_NAME, - MYSQL_PROC_FIELD_TYPE, + MYSQL_PROC_MYSQL_TYPE, MYSQL_PROC_FIELD_SPECIFIC_NAME, MYSQL_PROC_FIELD_LANGUAGE, MYSQL_PROC_FIELD_ACCESS, @@ -63,143 +63,273 @@ enum MYSQL_PROC_FIELD_MODIFIED, MYSQL_PROC_FIELD_SQL_MODE, MYSQL_PROC_FIELD_COMMENT, + MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT, + MYSQL_PROC_FIELD_COLLATION_CONNECTION, + MYSQL_PROC_FIELD_DB_COLLATION, + MYSQL_PROC_FIELD_BODY_UTF8, MYSQL_PROC_FIELD_COUNT }; /* Tells what SP_DEFAULT_ACCESS should be mapped to */ #define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL +/*************************************************************************/ -/* - Close mysql.proc, opened with open_proc_table_for_read(). - - SYNOPSIS - close_proc_table() - thd Thread context - backup Pointer to Open_tables_state instance which holds - information about tables which were open before we - decided to access mysql.proc. +/** + Stored_routine_creation_ctx -- creation context of stored routines + (stored procedures and functions). */ -void close_proc_table(THD *thd, Open_tables_state *backup) +class Stored_routine_creation_ctx : public Stored_program_creation_ctx, + public Sql_alloc { - close_thread_tables(thd); - thd->restore_backup_open_tables_state(backup); +public: + static Stored_routine_creation_ctx * + load_from_db(THD *thd, const sp_name *name, TABLE *proc_tbl); + +public: + virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) + { + return new (mem_root) Stored_routine_creation_ctx(m_client_cs, + m_connection_cl, + m_db_cl); + } + +protected: + virtual Object_creation_ctx *create_backup_ctx(THD *thd) const + { + DBUG_ENTER("Stored_routine_creation_ctx::create_backup_ctx"); + DBUG_RETURN(new Stored_routine_creation_ctx(thd)); + } + +private: + Stored_routine_creation_ctx(THD *thd) + : Stored_program_creation_ctx(thd) + { } + + Stored_routine_creation_ctx(CHARSET_INFO *client_cs, + CHARSET_INFO *connection_cl, + CHARSET_INFO *db_cl) + : Stored_program_creation_ctx(client_cs, connection_cl, db_cl) + { } +}; + +/************************************************************************** + Stored_routine_creation_ctx implementation. +**************************************************************************/ + +bool load_charset(MEM_ROOT *mem_root, + Field *field, + CHARSET_INFO *dflt_cs, + CHARSET_INFO **cs) +{ + String cs_name; + + if (get_field(mem_root, field, &cs_name)) + { + *cs= dflt_cs; + return TRUE; + } + + *cs= get_charset_by_csname(cs_name.c_ptr(), MY_CS_PRIMARY, MYF(0)); + + if (*cs == NULL) + { + *cs= dflt_cs; + return TRUE; + } + + return FALSE; } +/*************************************************************************/ -/* - Open the mysql.proc table for read. +bool load_collation(MEM_ROOT *mem_root, + Field *field, + CHARSET_INFO *dflt_cl, + CHARSET_INFO **cl) +{ + String cl_name; - SYNOPSIS - open_proc_table_for_read() - thd Thread context - backup Pointer to Open_tables_state instance where information about - currently open tables will be saved, and from which will be - restored when we will end work with mysql.proc. - - NOTES - Thanks to restrictions which we put on opening and locking of - this table for writing, we can open and lock it for reading - even when we already have some other tables open and locked. - One must call close_proc_table() to close table opened with - this call. - - RETURN - 0 Error - # Pointer to TABLE object of mysql.proc -*/ + if (get_field(mem_root, field, &cl_name)) + { + *cl= dflt_cl; + return TRUE; + } -TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) + *cl= get_charset_by_name(cl_name.c_ptr(), MYF(0)); + + if (*cl == NULL) + { + *cl= dflt_cl; + return TRUE; + } + + return FALSE; +} + +/*************************************************************************/ + +Stored_routine_creation_ctx * +Stored_routine_creation_ctx::load_from_db(THD *thd, + const sp_name *name, + TABLE *proc_tbl) { - TABLE_LIST tables; - TABLE *table; - bool not_used; - DBUG_ENTER("open_proc_table"); + /* Load character set/collation attributes. */ + + CHARSET_INFO *client_cs; + CHARSET_INFO *connection_cl; + CHARSET_INFO *db_cl; - thd->reset_n_backup_open_tables_state(backup); + const char *db_name= thd->strmake(name->m_db.str, name->m_db.length); + const char *sr_name= thd->strmake(name->m_name.str, name->m_name.length); - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*)"proc"; - if (!(table= open_table(thd, &tables, thd->mem_root, ¬_used, - MYSQL_LOCK_IGNORE_FLUSH))) + bool invalid_creation_ctx= FALSE; + + if (load_charset(thd->mem_root, + proc_tbl->field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT], + thd->variables.character_set_client, + &client_cs)) { - thd->restore_backup_open_tables_state(backup); - DBUG_RETURN(0); + sql_print_warning("Stored routine '%s'.'%s': invalid value " + "in column mysql.proc.character_set_client.", + (const char *) db_name, + (const char *) sr_name); + + invalid_creation_ctx= TRUE; + } + + if (load_collation(thd->mem_root, + proc_tbl->field[MYSQL_PROC_FIELD_COLLATION_CONNECTION], + thd->variables.collation_connection, + &connection_cl)) + { + sql_print_warning("Stored routine '%s'.'%s': invalid value " + "in column mysql.proc.collation_connection.", + (const char *) db_name, + (const char *) sr_name); + + invalid_creation_ctx= TRUE; + } + + if (load_collation(thd->mem_root, + proc_tbl->field[MYSQL_PROC_FIELD_DB_COLLATION], + NULL, + &db_cl)) + { + sql_print_warning("Stored routine '%s'.'%s': invalid value " + "in column mysql.proc.db_collation.", + (const char *) db_name, + (const char *) sr_name); + + invalid_creation_ctx= TRUE; } - DBUG_ASSERT(table->s->system_table); + if (invalid_creation_ctx) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_SR_INVALID_CREATION_CTX, + ER(ER_SR_INVALID_CREATION_CTX), + (const char *) db_name, + (const char *) sr_name); + } - table->reginfo.lock_type= TL_READ; /* - We have to ensure we are not blocked by a flush tables, as this - could lead to a deadlock if we have other tables opened. + If we failed to retrieve the database collation, load the default one + from the disk. */ - if (!(thd->lock= mysql_lock_tables(thd, &table, 1, - MYSQL_LOCK_IGNORE_FLUSH, ¬_used))) - { - close_proc_table(thd, backup); + + if (!db_cl) + db_cl= get_default_db_collation(thd, name->m_db.str); + + /* Create the context. */ + + return new Stored_routine_creation_ctx(client_cs, connection_cl, db_cl); +} + +/*************************************************************************/ + +/** + Open the mysql.proc table for read. + + @param thd Thread context + @param backup Pointer to Open_tables_state instance where information about + currently open tables will be saved, and from which will be + restored when we will end work with mysql.proc. + + @retval + 0 Error + @retval + \# Pointer to TABLE object of mysql.proc +*/ + +TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) +{ + DBUG_ENTER("open_proc_table_for_read"); + + TABLE_LIST table; + bzero((char*) &table, sizeof(table)); + table.db= (char*) "mysql"; + table.table_name= table.alias= (char*)"proc"; + table.lock_type= TL_READ; + + if (!open_system_tables_for_read(thd, &table, backup)) + DBUG_RETURN(table.table); + else DBUG_RETURN(0); - } - DBUG_RETURN(table); } -/* +/** Open the mysql.proc table for update. - SYNOPSIS - open_proc_table_for_update() - thd Thread context + @param thd Thread context - NOTES + @note Table opened with this call should closed using close_thread_tables(). - RETURN + @retval 0 Error - # Pointer to TABLE object of mysql.proc + @retval + \# Pointer to TABLE object of mysql.proc */ static TABLE *open_proc_table_for_update(THD *thd) { - TABLE_LIST tables; - TABLE *table; - DBUG_ENTER("open_proc_table"); + DBUG_ENTER("open_proc_table_for_update"); - bzero((char*) &tables, sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*)"proc"; - tables.lock_type= TL_WRITE; + TABLE_LIST table; + bzero((char*) &table, sizeof(table)); + table.db= (char*) "mysql"; + table.table_name= table.alias= (char*)"proc"; + table.lock_type= TL_WRITE; - table= open_ltable(thd, &tables, TL_WRITE); - - DBUG_RETURN(table); + DBUG_RETURN(open_system_table_for_update(thd, &table)); } -/* +/** Find row in open mysql.proc table representing stored routine. - SYNOPSIS - db_find_routine_aux() - thd Thread context - type Type of routine to find (function or procedure) - name Name of routine - table TABLE object for open mysql.proc table. + @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. - RETURN VALUE - SP_OK - Routine found - SP_KEY_NOT_FOUND- No routine with given name + @retval + SP_OK Routine found + @retval + SP_KEY_NOT_FOUND No routine with given name */ static int db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) { - byte key[MAX_KEY_LENGTH]; // db, name, optional key length type + 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, name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("type: %d name: %.*s", + type, (int) name->m_name.length, name->m_name.str)); /* Create key to find row. We have to use field->store() to be able to @@ -217,34 +347,32 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) key_copy(key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx(table->record[0], 0, - key, table->key_info->key_length, - HA_READ_KEY_EXACT)) + if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) DBUG_RETURN(SP_KEY_NOT_FOUND); DBUG_RETURN(SP_OK); } -/* +/** Find routine definition in mysql.proc table and create corresponding sp_head object for it. - SYNOPSIS - db_find_routine() - thd Thread context - type Type of routine (TYPE_ENUM_PROCEDURE/...) - name Name of routine - sphp Out parameter in which pointer to created sp_head - object is returned (0 in case of error). + @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). - NOTE + @note This function may damage current LEX during execution, so it is good idea to create temporary LEX and make it active before calling it. - RETURN VALUE - 0 - Success - non-0 - Error (may be one of special codes like SP_KEY_NOT_FOUND) + @retval + 0 Success + @retval + non-0 Error (may be one of special codes like SP_KEY_NOT_FOUND) */ static int @@ -261,17 +389,22 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) uint length; char buff[65]; String str(buff, sizeof(buff), &my_charset_bin); - ulong sql_mode; bool saved_time_zone_used= thd->time_zone_used; + ulong sql_mode, saved_mode= thd->variables.sql_mode; Open_tables_state open_tables_state_backup; + Stored_program_creation_ctx *creation_ctx; + DBUG_ENTER("db_find_routine"); DBUG_PRINT("enter", ("type: %d name: %.*s", - type, name->m_name.length, name->m_name.str)); + type, (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))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); + /* Reset sql_mode during data dictionary operations. */ + thd->variables.sql_mode= 0; + if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) goto done; @@ -364,13 +497,14 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) chistics.comment.str= ptr; chistics.comment.length= length; - close_proc_table(thd, &open_tables_state_backup); + creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, name, table); + + close_system_tables(thd, &open_tables_state_backup); table= 0; ret= db_load_routine(thd, type, name, sphp, sql_mode, params, returns, body, chistics, - definer, created, modified); - + definer, created, modified, creation_ctx); done: /* Restore the time zone flag as the timezone usage in proc table @@ -378,34 +512,62 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) */ thd->time_zone_used= saved_time_zone_used; if (table) - close_proc_table(thd, &open_tables_state_backup); + close_system_tables(thd, &open_tables_state_backup); + thd->variables.sql_mode= saved_mode; DBUG_RETURN(ret); } +/** + Silence DEPRECATED SYNTAX warnings when loading a stored procedure + into the cache. +*/ +struct Silence_deprecated_warning : public Internal_error_handler +{ +public: + virtual bool handle_error(uint sql_errno, const char *message, + MYSQL_ERROR::enum_warning_level level, + THD *thd); +}; + +bool +Silence_deprecated_warning::handle_error(uint sql_errno, const char *message, + MYSQL_ERROR::enum_warning_level level, + THD *thd) +{ + if (sql_errno == ER_WARN_DEPRECATED_SYNTAX && + level == MYSQL_ERROR::WARN_LEVEL_WARN) + return TRUE; + + return FALSE; +} + + static int db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong sql_mode, const char *params, const char *returns, const char *body, st_sp_chistics &chistics, - const char *definer, longlong created, longlong modified) + const char *definer, longlong created, longlong modified, + Stored_program_creation_ctx *creation_ctx) { LEX *old_lex= thd->lex, newlex; String defstr; - char old_db_buf[NAME_LEN+1]; - LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; - bool dbchanged; + char saved_cur_db_name_buf[NAME_LEN+1]; + LEX_STRING saved_cur_db_name= + { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) }; + bool cur_db_changed; ulong old_sql_mode= thd->variables.sql_mode; ha_rows old_select_limit= thd->variables.select_limit; sp_rcontext *old_spcont= thd->spcont; - + Silence_deprecated_warning warning_handler; + char definer_user_name_holder[USERNAME_LENGTH + 1]; - LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder, - USERNAME_LENGTH); + LEX_STRING definer_user_name= { definer_user_name_holder, + USERNAME_LENGTH }; char definer_host_name_holder[HOSTNAME_LENGTH + 1]; - LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder, - HOSTNAME_LENGTH); - + LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH }; + int ret; thd->variables.sql_mode= sql_mode; @@ -414,11 +576,11 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, thd->lex= &newlex; newlex.current_select= NULL; - parse_user(definer, (uint) strlen(definer), + parse_user(definer, strlen(definer), definer_user_name.str, &definer_user_name.length, definer_host_name.str, &definer_host_name.length); - defstr.set_charset(system_charset_info); + defstr.set_charset(creation_ctx->get_client_cs()); /* We have to add DEFINER clause and provide proper routine characterstics in @@ -439,49 +601,66 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, goto end; } - if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged))) + /* + Change the current database (if needed). + + TODO: why do we force switch here? + */ + + if (mysql_opt_change_db(thd, &name->m_db, &saved_cur_db_name, TRUE, + &cur_db_changed)) + { + ret= SP_INTERNAL_ERROR; goto end; + } + + thd->spcont= NULL; { Parser_state parser_state(thd, defstr.c_ptr(), defstr.length()); - thd->m_parser_state= &parser_state; + lex_start(thd); - thd->spcont= NULL; - ret= MYSQLparse(thd); - thd->m_parser_state= NULL; - if (ret == 0) - { - /* - Not strictly necessary to invoke this method here, since we know - that we've parsed CREATE PROCEDURE/FUNCTION and not an - UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to - maintain the invariant that this method is called for each - distinct statement, in case its logic is extended with other - types of analyses in future. - */ - newlex.set_trg_event_type_for_tables(); - } - } + thd->push_internal_handler(&warning_handler); + ret= parse_sql(thd, & parser_state, creation_ctx) || newlex.sphead == NULL; + thd->pop_internal_handler(); - if (ret || thd->is_fatal_error || newlex.sphead == NULL) - { - sp_head *sp= newlex.sphead; + /* + Force switching back to the saved current database (if changed), + because it may be NULL. In this case, mysql_change_db() would + generate an error. + */ - if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) + if (cur_db_changed && mysql_change_db(thd, &saved_cur_db_name, TRUE)) + { + delete newlex.sphead; + ret= SP_INTERNAL_ERROR; goto end; - delete sp; - ret= SP_PARSE_ERROR; - } - else - { - if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) + } + + if (ret) + { + delete newlex.sphead; + ret= SP_PARSE_ERROR; goto end; + } + *sphp= newlex.sphead; (*sphp)->set_definer(&definer_user_name, &definer_host_name); (*sphp)->set_info(created, modified, &chistics, sql_mode); + (*sphp)->set_creation_ctx(creation_ctx); (*sphp)->optimize(); + /* + Not strictly necessary to invoke this method here, since we know + that we've parsed CREATE PROCEDURE/FUNCTION and not an + UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to + maintain the invariant that this method is called for each + distinct statement, in case its logic is extended with other + types of analyses in future. + */ + newlex.set_trg_event_type_for_tables(); } + end: lex_end(thd->lex); thd->spcont= old_spcont; @@ -496,10 +675,12 @@ static void sp_returns_type(THD *thd, String &result, sp_head *sp) { TABLE table; + TABLE_SHARE share; Field *field; - bzero(&table, sizeof(table)); + bzero((char*) &table, sizeof(table)); + bzero((char*) &share, sizeof(share)); table.in_use= thd; - table.s = &table.share_not_to_be_used; + table.s = &share; field= sp->create_result_field(0, 0, &table); field->sql_type(result); @@ -512,17 +693,65 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) delete field; } -static int -db_create_routine(THD *thd, int type, sp_head *sp) + +/** + Write stored-routine object into mysql.proc. + + This operation stores attributes of the stored procedure/function into + 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 + that there are no locked tables in this thread at the time of + invocation. + Unlike some other DDL statements, *does* close the tables + in the end, since the call to this function is normally + followed by an implicit grant (sp_grant_privileges()) + and this subsequent call opens and closes mysql.procs_priv. + + @return Error code. SP_OK is returned on success. Other + SP_ constants are used to indicate about errors. +*/ + +int +sp_create_routine(THD *thd, int type, sp_head *sp) { int ret; TABLE *table; char definer[USER_HOST_BUFF_SIZE]; - DBUG_ENTER("db_create_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s",type,sp->m_name.length, + ulong saved_mode= thd->variables.sql_mode; + + CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str); + + enum_check_fields saved_count_cuted_fields; + + bool store_failed= FALSE; + + DBUG_ENTER("sp_create_routine"); + DBUG_PRINT("enter", ("type: %d name: %.*s",type, (int) sp->m_name.length, sp->m_name.str)); String retstr(64); + DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || + type == TYPE_ENUM_FUNCTION); + + /* Reset sql_mode during data dictionary operations. */ + thd->variables.sql_mode= 0; + + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + + saved_count_cuted_fields= thd->count_cuted_fields; + thd->count_cuted_fields= CHECK_FIELD_WARN; + if (!(table= open_proc_table_for_update(thd))) ret= SP_OPEN_TABLE_FAILED; else @@ -530,7 +759,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) restore_record(table, s->default_values); // Get default values for fields /* NOTE: all needed privilege checks have been already done. */ - strxmov(definer, thd->lex->definer->user.str, "@", + strxnmov(definer, sizeof(definer)-1, thd->lex->definer->user.str, "@", thd->lex->definer->host.str, NullS); if (table->s->fields < MYSQL_PROC_FIELD_COUNT) @@ -552,42 +781,76 @@ db_create_routine(THD *thd, int type, sp_head *sp) ret= SP_BODY_TOO_LONG; goto done; } - table->field[MYSQL_PROC_FIELD_DB]-> - store(sp->m_db.str, sp->m_db.length, system_charset_info); - table->field[MYSQL_PROC_FIELD_NAME]-> - store(sp->m_name.str, sp->m_name.length, system_charset_info); - table->field[MYSQL_PROC_FIELD_TYPE]-> - store((longlong)type, 1); - table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]-> - store(sp->m_name.str, sp->m_name.length, system_charset_info); + + store_failed= + table->field[MYSQL_PROC_FIELD_DB]-> + store(sp->m_db.str, sp->m_db.length, system_charset_info); + + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_NAME]-> + store(sp->m_name.str, sp->m_name.length, system_charset_info); + + store_failed= store_failed || + table->field[MYSQL_PROC_MYSQL_TYPE]-> + store((longlong)type, TRUE); + + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]-> + store(sp->m_name.str, sp->m_name.length, system_charset_info); + if (sp->m_chistics->daccess != SP_DEFAULT_ACCESS) - table->field[MYSQL_PROC_FIELD_ACCESS]-> - store((longlong)sp->m_chistics->daccess, 1); - table->field[MYSQL_PROC_FIELD_DETERMINISTIC]-> - store((longlong)(sp->m_chistics->detistic ? 1 : 2), 1); + { + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_ACCESS]-> + store((longlong)sp->m_chistics->daccess, TRUE); + } + + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_DETERMINISTIC]-> + store((longlong)(sp->m_chistics->detistic ? 1 : 2), TRUE); + if (sp->m_chistics->suid != SP_IS_DEFAULT_SUID) - table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> - store((longlong)sp->m_chistics->suid, 1); - table->field[MYSQL_PROC_FIELD_PARAM_LIST]-> - store(sp->m_params.str, sp->m_params.length, system_charset_info); + { + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> + store((longlong)sp->m_chistics->suid, TRUE); + } + + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_PARAM_LIST]-> + store(sp->m_params.str, sp->m_params.length, system_charset_info); + if (sp->m_type == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); - table->field[MYSQL_PROC_FIELD_RETURNS]-> - store(retstr.ptr(), retstr.length(), system_charset_info); + + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_RETURNS]-> + store(retstr.ptr(), retstr.length(), system_charset_info); } - table->field[MYSQL_PROC_FIELD_BODY]-> - store(sp->m_body.str, sp->m_body.length, system_charset_info); - table->field[MYSQL_PROC_FIELD_DEFINER]-> - store(definer, (uint)strlen(definer), system_charset_info); + + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_BODY]-> + store(sp->m_body.str, sp->m_body.length, system_charset_info); + + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_DEFINER]-> + store(definer, (uint)strlen(definer), system_charset_info); + ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time(); ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); - table->field[MYSQL_PROC_FIELD_SQL_MODE]-> - store((longlong)thd->variables.sql_mode, 1); + + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_SQL_MODE]-> + store((longlong)saved_mode, TRUE); + if (sp->m_chistics->comment.str) - table->field[MYSQL_PROC_FIELD_COMMENT]-> - store(sp->m_chistics->comment.str, sp->m_chistics->comment.length, - system_charset_info); + { + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_COMMENT]-> + store(sp->m_chistics->comment.str, sp->m_chistics->comment.length, + system_charset_info); + } if ((sp->m_type == TYPE_ENUM_FUNCTION) && !trust_function_creators && mysql_bin_log.is_open()) @@ -619,8 +882,38 @@ db_create_routine(THD *thd, int type, sp_head *sp) } } + table->field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]->set_notnull(); + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]->store( + thd->charset()->csname, + strlen(thd->charset()->csname), + system_charset_info); + + table->field[MYSQL_PROC_FIELD_COLLATION_CONNECTION]->set_notnull(); + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_COLLATION_CONNECTION]->store( + thd->variables.collation_connection->name, + strlen(thd->variables.collation_connection->name), + system_charset_info); + + table->field[MYSQL_PROC_FIELD_DB_COLLATION]->set_notnull(); + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_DB_COLLATION]->store( + db_cs->name, strlen(db_cs->name), system_charset_info); + + table->field[MYSQL_PROC_FIELD_BODY_UTF8]->set_notnull(); + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_BODY_UTF8]->store( + sp->m_body_utf8.str, sp->m_body_utf8.length, system_charset_info); + + if (store_failed) + { + ret= SP_FLD_STORE_FAILED; + goto done; + } + ret= SP_OK; - if (table->file->write_row(table->record[0])) + if (table->file->ha_write_row(table->record[0])) ret= SP_WRITE_ROW_FAILED; else if (mysql_bin_log.is_open()) { @@ -645,44 +938,67 @@ db_create_routine(THD *thd, int type, sp_head *sp) } /* Such a statement can always go directly to binlog, no trans cache */ - Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0, - FALSE); - mysql_bin_log.write(&qinfo); + thd->binlog_query(THD::MYSQL_QUERY_TYPE, + log_query.c_ptr(), log_query.length(), FALSE, FALSE); } } done: + thd->count_cuted_fields= saved_count_cuted_fields; + thd->variables.sql_mode= saved_mode; + close_thread_tables(thd); DBUG_RETURN(ret); } -static int -db_drop_routine(THD *thd, int type, sp_name *name) +/** + Delete the record for the stored routine object from mysql.proc. + + The operation deletes the record for the stored routine specified by name + 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 + used to indicate about errors. +*/ + +int +sp_drop_routine(THD *thd, int type, sp_name *name) { TABLE *table; int ret; - DBUG_ENTER("db_drop_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, name->m_name.length, name->m_name.str)); + 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); + + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); 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 (table->file->delete_row(table->record[0])) + if (table->file->ha_delete_row(table->record[0])) ret= SP_DELETE_ROW_FAILED; } if (ret == SP_OK) { - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, TRUE, thd->query, thd->query_length); + sp_cache_invalidate(); } close_thread_tables(thd); @@ -690,14 +1006,40 @@ db_drop_routine(THD *thd, int type, sp_name *name) } -static int -db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) +/** + Find and updated the record for the stored routine object in mysql.proc. + + The operation finds the record for the stored routine specified by name + in the mysql.proc table and updates it with new attributes. After + 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. + + @return Error code. SP_OK is returned on success. Other SP_ constants are + used to indicate about errors. +*/ + +int +sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) { TABLE *table; int ret; - DBUG_ENTER("db_update_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, name->m_name.length, name->m_name.str)); + DBUG_ENTER("sp_update_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); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); @@ -708,26 +1050,25 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); if (chistics->suid != SP_IS_DEFAULT_SUID) table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> - store((longlong)chistics->suid, 1); + store((longlong)chistics->suid, TRUE); if (chistics->daccess != SP_DEFAULT_ACCESS) table->field[MYSQL_PROC_FIELD_ACCESS]-> - store((longlong)chistics->daccess, 1); + store((longlong)chistics->daccess, TRUE); if (chistics->comment.str) table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str, chistics->comment.length, system_charset_info); - if ((table->file->update_row(table->record[1],table->record[0]))) + if ((ret= table->file->ha_update_row(table->record[1],table->record[0])) && + ret != HA_ERR_RECORD_IS_THE_SAME) ret= SP_WRITE_ROW_FAILED; + else + ret= 0; } if (ret == SP_OK) { - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, TRUE, thd->query, thd->query_length); + sp_cache_invalidate(); } close_thread_tables(thd); @@ -735,187 +1076,13 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) } -struct st_used_field -{ - const char *field_name; - uint field_length; - enum enum_field_types field_type; - Field *field; -}; - -static struct st_used_field init_fields[]= -{ - { "Db", NAME_LEN, MYSQL_TYPE_STRING, 0}, - { "Name", NAME_LEN, MYSQL_TYPE_STRING, 0}, - { "Type", 9, MYSQL_TYPE_STRING, 0}, - { "Definer", 77, MYSQL_TYPE_STRING, 0}, - { "Modified", 0, MYSQL_TYPE_TIMESTAMP, 0}, - { "Created", 0, MYSQL_TYPE_TIMESTAMP, 0}, - { "Security_type", 1, MYSQL_TYPE_STRING, 0}, - { "Comment", NAME_LEN, MYSQL_TYPE_STRING, 0}, - { 0, 0, MYSQL_TYPE_STRING, 0} -}; - - -static int -print_field_values(THD *thd, TABLE *table, - struct st_used_field *used_fields, - int type, const char *wild) -{ - Protocol *protocol= thd->protocol; - - if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type) - { - String db_string; - String name_string; - struct st_used_field *used_field= used_fields; - - if (get_field(thd->mem_root, used_field->field, &db_string)) - db_string.set_ascii("", 0); - used_field+= 1; - get_field(thd->mem_root, used_field->field, &name_string); - - if (!wild || !wild[0] || !wild_compare(name_string.ptr(), wild, 0)) - { - protocol->prepare_for_resend(); - protocol->store(&db_string); - protocol->store(&name_string); - for (used_field++; - used_field->field_name; - used_field++) - { - switch (used_field->field_type) { - case MYSQL_TYPE_TIMESTAMP: - { - MYSQL_TIME tmp_time; - - bzero((char *)&tmp_time, sizeof(tmp_time)); - ((Field_timestamp *) used_field->field)->get_time(&tmp_time); - protocol->store(&tmp_time); - } - break; - default: - { - String tmp_string; - - get_field(thd->mem_root, used_field->field, &tmp_string); - protocol->store(&tmp_string); - } - break; - } - } - if (protocol->write()) - return SP_INTERNAL_ERROR; - } - } - - return SP_OK; -} - - -static int -db_show_routine_status(THD *thd, int type, const char *wild) -{ - TABLE *table; - TABLE_LIST tables; - int res; - DBUG_ENTER("db_show_routine_status"); - - memset(&tables, 0, sizeof(tables)); - tables.db= (char*)"mysql"; - tables.table_name= tables.alias= (char*)"proc"; - - if (! (table= open_ltable(thd, &tables, TL_READ))) - { - res= SP_OPEN_TABLE_FAILED; - goto done; - } - else - { - Item *item; - List<Item> field_list; - struct st_used_field *used_field; - TABLE_LIST *leaves= 0; - st_used_field used_fields[array_elements(init_fields)]; - - memcpy((char*) used_fields, (char*) init_fields, sizeof(used_fields)); - /* Init header */ - for (used_field= &used_fields[0]; - used_field->field_name; - used_field++) - { - switch (used_field->field_type) { - case MYSQL_TYPE_TIMESTAMP: - field_list.push_back(item=new Item_datetime(used_field->field_name)); - break; - default: - field_list.push_back(item=new Item_empty_string(used_field->field_name, - used_field-> - field_length)); - break; - } - } - /* Print header */ - if (thd->protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | - Protocol::SEND_EOF)) - { - res= SP_INTERNAL_ERROR; - goto err_case; - } - - /* - Init fields - - tables is not VIEW for sure => we can pass 0 as condition - */ - thd->lex->select_lex.context.resolve_in_table_list_only(&tables); - setup_tables(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, - &tables, 0, &leaves, FALSE); - for (used_field= &used_fields[0]; - used_field->field_name; - used_field++) - { - Item_field *field= new Item_field(&thd->lex->select_lex.context, - "mysql", "proc", - used_field->field_name); - if (!field || - !(used_field->field= find_field_in_tables(thd, field, &tables, NULL, - 0, REPORT_ALL_ERRORS, 1, - TRUE))) - { - res= SP_INTERNAL_ERROR; - goto err_case1; - } - } - - table->file->ha_index_init(0); - if ((res= table->file->index_first(table->record[0]))) - { - res= (res == HA_ERR_END_OF_FILE) ? 0 : SP_INTERNAL_ERROR; - goto err_case1; - } - if ((res= print_field_values(thd, table, used_fields, type, wild))) - goto err_case1; - while (!table->file->index_next(table->record[0])) - { - if ((res= print_field_values(thd, table, used_fields, type, wild))) - goto err_case1; - } - res= SP_OK; - } - -err_case1: - send_eof(thd); -err_case: - table->file->ha_index_end(); - close_thread_tables(thd); -done: - DBUG_RETURN(res); -} +/** + Drop all routines in database 'db' + @note Close the thread tables, the calling code might want to + delete from other system tables afterwards. +*/ -/* Drop all routines in database 'db' */ int sp_drop_db_routines(THD *thd, char *db) { @@ -929,21 +1096,21 @@ sp_drop_db_routines(THD *thd, char *db) if (!(table= open_proc_table_for_update(thd))) goto err; - table->field[MYSQL_PROC_FIELD_DB]->store(db, (uint) strlen(db), system_charset_info); + table->field[MYSQL_PROC_FIELD_DB]->store(db, strlen(db), system_charset_info); key_len= table->key_info->key_part[0].store_length; ret= SP_OK; - table->file->ha_index_init(0); - if (! table->file->index_read(table->record[0], - (byte *)table->field[MYSQL_PROC_FIELD_DB]->ptr, - key_len, HA_READ_KEY_EXACT)) + table->file->ha_index_init(0, 1); + if (! table->file->index_read_map(table->record[0], + (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT)) { int nxtres; bool deleted= FALSE; do { - if (! table->file->delete_row(table->record[0])) + if (! table->file->ha_delete_row(table->record[0])) deleted= TRUE; /* We deleted something */ else { @@ -952,7 +1119,7 @@ sp_drop_db_routines(THD *thd, char *db) break; } } while (! (nxtres= table->file->index_next_same(table->record[0], - (byte *)table->field[MYSQL_PROC_FIELD_DB]->ptr, + (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr, key_len))); if (nxtres != HA_ERR_END_OF_FILE) ret= SP_KEY_NOT_FOUND; @@ -968,26 +1135,78 @@ err: } -/***************************************************************************** - PROCEDURE -******************************************************************************/ +/** + Implement SHOW CREATE statement for stored routines. -/* + The operation finds the stored routine object specified by name and then + 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. + @retval FALSE on success + @retval TRUE on error +*/ + +bool +sp_show_create_routine(THD *thd, int type, sp_name *name) +{ + bool err_status= TRUE; + sp_head *sp; + sp_cache **cache = type == TYPE_ENUM_PROCEDURE ? + &thd->sp_proc_cache : &thd->sp_func_cache; + + DBUG_ENTER("sp_show_create_routine"); + DBUG_PRINT("enter", ("name: %.*s", + (int) name->m_name.length, + name->m_name.str)); + + DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || + type == TYPE_ENUM_FUNCTION); + + if (type == TYPE_ENUM_PROCEDURE) + { + /* + SHOW CREATE PROCEDURE may require two instances of one sp_head + object when SHOW CREATE PROCEDURE is called for the procedure that + is being executed. Basically, there is no actual recursion, so we + increase the recursion limit for this statement (kind of hack). + + SHOW CREATE FUNCTION does not require this because SHOW CREATE + statements are prohibitted within stored functions. + */ + + thd->variables.max_sp_recursion_depth++; + } + + if ((sp= sp_find_routine(thd, type, name, cache, FALSE))) + err_status= sp->show_create_routine(thd, type); + + if (type == TYPE_ENUM_PROCEDURE) + thd->variables.max_sp_recursion_depth--; + + DBUG_RETURN(err_status); +} + + +/** Obtain object representing stored procedure/function by its name from stored procedures cache and looking into mysql.proc if needed. - SYNOPSIS - sp_find_routine() - thd - thread context - type - type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE) - name - name of procedure - cp - hash to look routine in - cache_only - if true perform cache-only lookup - (Don't look in mysql.proc). - - RETURN VALUE - Non-0 pointer to sp_head object for the procedure, or - 0 - in case of error. + @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 + (Don't look in mysql.proc). + + @retval + NonNULL pointer to sp_head object for the procedure + @retval + NULL in case of error. */ sp_head * @@ -999,9 +1218,9 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, thd->variables.max_sp_recursion_depth : 0); DBUG_ENTER("sp_find_routine"); - DBUG_PRINT("enter", ("name: %.*s.%.*s, type: %d, cache only %d", - name->m_db.length, name->m_db.str, - name->m_name.length, name->m_name.str, + DBUG_PRINT("enter", ("name: %.*s.%.*s type: %d cache only %d", + (int) name->m_db.length, name->m_db.str, + (int) name->m_name.length, name->m_name.str, type, cache_only)); if ((sp= sp_cache_lookup(cp, name))) @@ -1020,7 +1239,7 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp)); if (sp->m_first_free_instance) { - DBUG_PRINT("info", ("first free: 0x%lx, level: %lu, flags %x", + DBUG_PRINT("info", ("first free: 0x%lx level: %lu flags %x", (ulong)sp->m_first_free_instance, sp->m_first_free_instance->m_recursion_level, sp->m_first_free_instance->m_flags)); @@ -1055,7 +1274,8 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, if (db_load_routine(thd, type, name, &new_sp, sp->m_sql_mode, sp->m_params.str, returns, sp->m_body.str, *sp->m_chistics, definer, - sp->m_created, sp->m_modified) == SP_OK) + sp->m_created, sp->m_modified, + sp->get_creation_ctx()) == SP_OK) { sp->m_last_cached_sp->m_next_cached_sp= new_sp; new_sp->m_recursion_level= level; @@ -1082,7 +1302,7 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, } -/* +/** This is used by sql_acl.cc:mysql_routine_grant() and is used to find the routines in 'routines'. */ @@ -1099,8 +1319,8 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error) sp_name *name; LEX_STRING lex_db; LEX_STRING lex_name; - lex_db.length= (uint) strlen(routine->db); - lex_name.length= (uint) strlen(routine->table_name); + lex_db.length= strlen(routine->db); + lex_name.length= strlen(routine->table_name); lex_db.str= thd->strmake(routine->db, lex_db.length); lex_name.str= thd->strmake(routine->table_name, lex_name.length); name= new sp_name(lex_db, lex_name, true); @@ -1131,18 +1351,17 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error) } -/* +/** Check if a routine exists in the mysql.proc table, without actually - parsing the definition. (Used for dropping) + parsing the definition. (Used for dropping). - SYNOPSIS - sp_routine_exists_in_table() - thd - thread context - name - name of procedure + @param thd thread context + @param name name of procedure - RETURN VALUE - 0 - Success - non-0 - Error; SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND + @retval + 0 Success + @retval + non-0 Error; SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND */ int @@ -1158,158 +1377,13 @@ sp_routine_exists_in_table(THD *thd, int type, sp_name *name) { if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) ret= SP_KEY_NOT_FOUND; - close_proc_table(thd, &open_tables_state_backup); + close_system_tables(thd, &open_tables_state_backup); } return ret; } -int -sp_create_procedure(THD *thd, sp_head *sp) -{ - int ret; - DBUG_ENTER("sp_create_procedure"); - DBUG_PRINT("enter", ("name: %.*s", sp->m_name.length, sp->m_name.str)); - - ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, sp); - DBUG_RETURN(ret); -} - - -int -sp_drop_procedure(THD *thd, sp_name *name) -{ - int ret; - DBUG_ENTER("sp_drop_procedure"); - DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); - - ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name); - if (!ret) - sp_cache_invalidate(); - DBUG_RETURN(ret); -} - - -int -sp_update_procedure(THD *thd, sp_name *name, st_sp_chistics *chistics) -{ - int ret; - DBUG_ENTER("sp_update_procedure"); - DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); - - ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, chistics); - if (!ret) - sp_cache_invalidate(); - DBUG_RETURN(ret); -} - - -int -sp_show_create_procedure(THD *thd, sp_name *name) -{ - int ret= SP_KEY_NOT_FOUND; - sp_head *sp; - DBUG_ENTER("sp_show_create_procedure"); - DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); - - /* - Increase the recursion limit for this statement. SHOW CREATE PROCEDURE - does not do actual recursion. - */ - thd->variables.max_sp_recursion_depth++; - if ((sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, - &thd->sp_proc_cache, FALSE))) - ret= sp->show_create_procedure(thd); - - thd->variables.max_sp_recursion_depth--; - DBUG_RETURN(ret); -} - - -int -sp_show_status_procedure(THD *thd, const char *wild) -{ - int ret; - DBUG_ENTER("sp_show_status_procedure"); - - ret= db_show_routine_status(thd, TYPE_ENUM_PROCEDURE, wild); - DBUG_RETURN(ret); -} - - -/***************************************************************************** - FUNCTION -******************************************************************************/ - -int -sp_create_function(THD *thd, sp_head *sp) -{ - int ret; - DBUG_ENTER("sp_create_function"); - DBUG_PRINT("enter", ("name: %.*s", sp->m_name.length, sp->m_name.str)); - - ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, sp); - DBUG_RETURN(ret); -} - - -int -sp_drop_function(THD *thd, sp_name *name) -{ - int ret; - DBUG_ENTER("sp_drop_function"); - DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); - - ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name); - if (!ret) - sp_cache_invalidate(); - DBUG_RETURN(ret); -} - - -int -sp_update_function(THD *thd, sp_name *name, st_sp_chistics *chistics) -{ - int ret; - DBUG_ENTER("sp_update_procedure"); - DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); - - ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, chistics); - if (!ret) - sp_cache_invalidate(); - DBUG_RETURN(ret); -} - - -int -sp_show_create_function(THD *thd, sp_name *name) -{ - sp_head *sp; - DBUG_ENTER("sp_show_create_function"); - DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); - - if ((sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, name, - &thd->sp_func_cache, FALSE))) - { - int ret= sp->show_create_function(thd); - - DBUG_RETURN(ret); - } - DBUG_RETURN(SP_KEY_NOT_FOUND); -} - - -int -sp_show_status_function(THD *thd, const char *wild) -{ - int ret; - DBUG_ENTER("sp_show_status_function"); - ret= db_show_routine_status(thd, TYPE_ENUM_FUNCTION, wild); - DBUG_RETURN(ret); -} - - -/* +/** Structure that represents element in the set of stored routines used by statement or routine. */ @@ -1317,14 +1391,16 @@ struct Sroutine_hash_entry; struct Sroutine_hash_entry { - /* Set key consisting of one-byte routine type and quoted routine name. */ + /** + Set key consisting of one-byte routine type and quoted routine name. + */ LEX_STRING key; - /* + /** Next element in list linking all routines in set. See also comments for LEX::sroutine/sroutine_list and sp_head::m_sroutines. */ Sroutine_hash_entry *next; - /* + /** Uppermost view which directly or indirectly uses this routine. 0 if routine is not used in view. Note that it also can be 0 if statement uses routine both via view and directly. @@ -1333,32 +1409,31 @@ struct Sroutine_hash_entry }; -extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first) +extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, + my_bool first) { Sroutine_hash_entry *rn= (Sroutine_hash_entry *)ptr; *plen= rn->key.length; - return (byte *)rn->key.str; + return (uchar *)rn->key.str; } -/* +/** Check if - current statement (the one in thd->lex) needs table prelocking - first routine in thd->lex->sroutines_list needs to execute its body in prelocked mode. - SYNOPSIS - sp_get_prelocking_info() - thd Current thread, thd->lex is the statement to be - checked. - need_prelocking OUT TRUE - prelocked mode should be activated - before executing the statement - FALSE - Don't activate prelocking - first_no_prelocking OUT TRUE - Tables used by first routine in - thd->lex->sroutines_list should be - prelocked. - FALSE - Otherwise. - NOTES + @param thd Current thread, thd->lex is the statement to be + checked. + @param[out] need_prelocking TRUE - prelocked mode should be activated + before executing the statement; + FALSE - Don't activate prelocking + @param[out] first_no_prelocking TRUE - Tables used by first routine in + thd->lex->sroutines_list should be + prelocked. FALSE - Otherwise. + + @note This function assumes that for any "CALL proc(...)" statement routines_list will have 'proc' as first element (it may have several, consider e.g. "proc(sp_func(...)))". This property is currently guaranted by the parser. @@ -1378,36 +1453,37 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking, } -/* +/** Auxilary function that adds new element to the set of stored routines used by statement. - SYNOPSIS - add_used_routine() - lex LEX representing statement - arena Arena in which memory for new element will be allocated - key Key for the hash representing set - belong_to_view Uppermost view which uses this routine - (0 if routine is not used by view) + In case when statement uses stored routines but does not need + prelocking (i.e. it does not use any tables) we will access the + elements of LEX::sroutines set on prepared statement re-execution. + Because of this we have to allocate memory for both hash element + and copy of its key in persistent arena. - NOTES - Will also add element to end of 'LEX::sroutines_list' list. + @param lex LEX representing statement + @param arena Arena in which memory for new element will be + allocated + @param key Key for the hash representing set + @param belong_to_view Uppermost view which uses this routine + (0 if routine is not used by view) - In case when statement uses stored routines but does not need - prelocking (i.e. it does not use any tables) we will access the - elements of LEX::sroutines set on prepared statement re-execution. - Because of this we have to allocate memory for both hash element - and copy of its key in persistent arena. + @note + Will also add element to end of 'LEX::sroutines_list' list. - TODO + @todo When we will got rid of these accesses on re-executions we will be able to allocate memory for hash elements in non-persitent arena and directly use key values from sp_head::m_sroutines sets instead of making their copies. - RETURN VALUE - TRUE - new element was added. - FALSE - element was not added (because it is already present in the set). + @retval + TRUE new element was added. + @retval + FALSE element was not added (because it is already present in + the set). */ static bool add_used_routine(LEX *lex, Query_arena *arena, @@ -1418,7 +1494,7 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, Query_tables_list::START_SROUTINES_HASH_SIZE, 0, 0, sp_sroutine_key, 0, 0); - if (!hash_search(&lex->sroutines, (byte *)key->str, key->length)) + if (!hash_search(&lex->sroutines, (uchar *)key->str, key->length)) { Sroutine_hash_entry *rn= (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) + @@ -1428,8 +1504,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, rn->key.length= key->length; rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry); memcpy(rn->key.str, key->str, key->length + 1); - my_hash_insert(&lex->sroutines, (byte *)rn); - lex->sroutines_list.link_in_list((byte *)rn, (byte **)&rn->next); + my_hash_insert(&lex->sroutines, (uchar *)rn); + lex->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next); rn->belong_to_view= belong_to_view; return TRUE; } @@ -1437,24 +1513,22 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, } -/* +/** Add routine which is explicitly used by statement to the set of stored routines used by this statement. - SYNOPSIS - sp_add_used_routine() - lex - LEX representing statement - arena - arena in which memory for new element of the set - will be allocated - rt - routine name - rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...) + To be friendly towards prepared statements one should pass + persistent arena as second argument. + + @param lex LEX representing statement + @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/...) - NOTES + @note Will also add element to end of 'LEX::sroutines_list' list (and will take into account that this is explicitly used routine). - - To be friendly towards prepared statements one should pass - persistent arena as second argument. */ void sp_add_used_routine(LEX *lex, Query_arena *arena, @@ -1467,13 +1541,11 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena, } -/* +/** Remove routines which are only indirectly used by statement from the set of routines used by this statement. - SYNOPSIS - sp_remove_not_own_routines() - lex LEX representing statement + @param lex LEX representing statement */ void sp_remove_not_own_routines(LEX *lex) @@ -1487,7 +1559,7 @@ void sp_remove_not_own_routines(LEX *lex) but we want to be more future-proof. */ next_rt= not_own_rt->next; - hash_delete(&lex->sroutines, (byte *)not_own_rt); + hash_delete(&lex->sroutines, (uchar *)not_own_rt); } *(Sroutine_hash_entry **)lex->sroutines_list_own_last= NULL; @@ -1496,16 +1568,14 @@ void sp_remove_not_own_routines(LEX *lex) } -/* +/** Merge contents of two hashes representing sets of routines used by statements or by other routines. - SYNOPSIS - sp_update_sp_used_routines() - dst - hash to which elements should be added - src - hash from which elements merged + @param dst hash to which elements should be added + @param src hash from which elements merged - NOTE + @note This procedure won't create new Sroutine_hash_entry objects, instead it will simply add elements from source to destination hash. Thus time of life of elements in destination hash becomes @@ -1519,24 +1589,23 @@ void sp_update_sp_used_routines(HASH *dst, HASH *src) for (uint i=0 ; i < src->records ; i++) { Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i); - if (!hash_search(dst, (byte *)rt->key.str, rt->key.length)) - my_hash_insert(dst, (byte *)rt); + if (!hash_search(dst, (uchar *)rt->key.str, rt->key.length)) + my_hash_insert(dst, (uchar *)rt); } } -/* +/** Add contents of hash representing set of routines to the set of routines used by statement. - SYNOPSIS - sp_update_stmt_used_routines() - thd Thread context - lex LEX representing statement - src Hash representing set from which routines will be added - belong_to_view Uppermost view which uses these routines, 0 if none + @param thd Thread context + @param lex LEX representing statement + @param src Hash representing set from which routines will + be added + @param belong_to_view Uppermost view which uses these routines, 0 if none - NOTE + @note It will also add elements to end of 'LEX::sroutines_list' list. */ @@ -1552,18 +1621,17 @@ sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src, } -/* +/** Add contents of list representing set of routines to the set of routines used by statement. - SYNOPSIS - sp_update_stmt_used_routines() - thd Thread context - lex LEX representing statement - src List representing set from which routines will be added - belong_to_view Uppermost view which uses these routines, 0 if none + @param thd Thread context + @param lex LEX representing statement + @param src List representing set from which routines will + be added + @param belong_to_view Uppermost view which uses these routines, 0 if none - NOTE + @note It will also add elements to end of 'LEX::sroutines_list' list. */ @@ -1576,37 +1644,36 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src, } -/* +/** Cache sub-set of routines used by statement, add tables used by these routines to statement table list. Do the same for all routines used by these routines. - SYNOPSIS - sp_cache_routines_and_add_tables_aux() - thd - thread context - lex - LEX representing statement - start - first routine from the list of routines to be cached - (this list defines mentioned sub-set). - first_no_prelock - If true, don't add tables or cache routines used by - the body of the first routine (i.e. *start) - will be executed in non-prelocked mode. - tabs_changed - Set to TRUE some tables were added, FALSE otherwise - NOTE + @param thd thread context + @param lex LEX representing statement + @param start first routine from the list of routines to be cached + (this list defines mentioned sub-set). + @param first_no_prelock If true, don't add tables or cache routines used by + the body of the first routine (i.e. *start) + will be executed in non-prelocked mode. + @param tabs_changed Set to TRUE some tables were added, FALSE otherwise + + @note If some function is missing this won't be reported here. Instead this fact will be discovered during query execution. - RETURN VALUE - 0 - success - non-0 - failure + @retval + 0 success + @retval + non-0 failure */ static int sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, Sroutine_hash_entry *start, - bool first_no_prelock, bool *tabs_changed) + bool first_no_prelock) { int ret= 0; - bool tabschnd= 0; /* Set if tables changed */ bool first= TRUE; DBUG_ENTER("sp_cache_routines_and_add_tables_aux"); @@ -1646,7 +1713,7 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, an error with it's return value without calling my_error(), we set the generic "mysql.proc table corrupt" error here. */ - if (!thd->net.report_error) + if (! thd->is_error()) { /* SP allows full NAME_LEN chars thus he have to allocate enough @@ -1655,10 +1722,8 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, rest of the server checks agains NAME_LEN bytes and not chars. Hence, the overrun happens only if the name is in length > 32 and uses multibyte (cyrillic, greek, etc.) - - !! Change 3 with SYSTEM_CHARSET_MBMAXLEN when it's defined. */ - char n[NAME_LEN*3*2+2]; + char n[NAME_LEN*2+2]; /* m_qname.str is not always \0 terminated */ memcpy(n, name.m_qname.str, name.m_qname.length); @@ -1674,61 +1739,57 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, { sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines, rt->belong_to_view); - tabschnd|= - sp->add_used_tables_to_table_list(thd, &lex->query_tables_last, - rt->belong_to_view); + (void)sp->add_used_tables_to_table_list(thd, &lex->query_tables_last, + rt->belong_to_view); } + sp->propagate_attributes(lex); } first= FALSE; } - if (tabs_changed) /* it can be NULL */ - *tabs_changed= tabschnd; DBUG_RETURN(ret); } -/* +/** Cache all routines from the set of used by statement, add tables used by those routines to statement table list. Do the same for all routines used by those routines. - SYNOPSIS - sp_cache_routines_and_add_tables() - thd - thread context - lex - LEX representing statement - first_no_prelock - If true, don't add tables or cache routines used by - the body of the first routine (i.e. *start) - tabs_changed - Set to TRUE some tables were added, FALSE otherwise - - RETURN VALUE - 0 - success - non-0 - failure + @param thd thread context + @param lex LEX representing statement + @param first_no_prelock If true, don't add tables or cache routines used by + the body of the first routine (i.e. *start) + + @retval + 0 success + @retval + non-0 failure */ int -sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock, - bool *tabs_changed) +sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock) { return sp_cache_routines_and_add_tables_aux(thd, lex, (Sroutine_hash_entry *)lex->sroutines_list.first, - first_no_prelock, tabs_changed); + first_no_prelock); } -/* - Add all routines used by view to the set of routines used by statement. +/** + Add all routines used by view to the set of routines used by + statement. + Add tables used by those routines to statement table list. Do the same for all routines used by these routines. - SYNOPSIS - sp_cache_routines_and_add_tables_for_view() - thd Thread context - lex LEX representing statement - view Table list element representing view + @param thd Thread context + @param lex LEX representing statement + @param view Table list element representing view - RETURN VALUE - 0 - success - non-0 - failure + @retval + 0 success + @retval + non-0 failure */ int @@ -1738,26 +1799,24 @@ sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, TABLE_LIST *view) (Sroutine_hash_entry **)lex->sroutines_list.next; sp_update_stmt_used_routines(thd, lex, &view->view->sroutines_list, view->top_table()); - return sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr, FALSE, - NULL); + return sp_cache_routines_and_add_tables_aux(thd, lex, + *last_cached_routine_ptr, FALSE); } -/* +/** Add triggers for table to the set of routines used by statement. Add tables used by them to statement table list. Do the same for all implicitly used routines. - SYNOPSIS - sp_cache_routines_and_add_tables_for_triggers() - thd thread context - lex LEX respresenting statement - table Table list element for table with trigger + @param thd thread context + @param lex LEX respresenting statement + @param table Table list element for table with trigger - RETURN VALUE - 0 - success - non-0 - failure + @retval + 0 success + @retval + non-0 failure */ int @@ -1787,6 +1846,7 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, { trigger->add_used_tables_to_table_list(thd, &lex->query_tables_last, table->belong_to_view); + trigger->propagate_attributes(lex); sp_update_stmt_used_routines(thd, lex, &trigger->m_sroutines, table->belong_to_view); @@ -1797,15 +1857,17 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, } ret= sp_cache_routines_and_add_tables_aux(thd, lex, *last_cached_routine_ptr, - FALSE, NULL); + FALSE); return ret; } -/* - * Generates the CREATE... string from the table information. - * Returns TRUE on success, FALSE on (alloc) failure. - */ +/** + Generates the CREATE... string from the table information. + + @return + Returns TRUE on success, FALSE on (alloc) failure. +*/ static bool create_string(THD *thd, String *buf, int type, @@ -1873,70 +1935,3 @@ create_string(THD *thd, String *buf, buf->append(body, bodylen); return TRUE; } - - - -/* - Change the current database if needed. - - SYNOPSIS - sp_use_new_db() - thd thread handle - new_db new database name (a string and its length) - old_db [IN] str points to a buffer where to store the old - database, length contains the size of the buffer - [OUT] if old db was not NULL, its name is copied - to the buffer pointed at by str and length is updated - accordingly. Otherwise str[0] is set to '\0' and length - is set to 0. The out parameter should be used only if - the database name has been changed (see dbchangedp). - dbchangedp [OUT] is set to TRUE if the current database is changed, - FALSE otherwise. A database is not changed if the old - name is the same as the new one, both names are empty, - or an error has occurred. - - RETURN VALUE - 0 success - 1 access denied or out of memory (the error message is - set in THD) -*/ - -int -sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, - bool no_access_check, bool *dbchangedp) -{ - int ret; - DBUG_ENTER("sp_use_new_db"); - DBUG_PRINT("enter", ("newdb: %s", new_db.str)); - - /* - A stored routine always belongs to some database. The - old database (old_db) might be NULL, but to restore the - old database we will use mysql_change_db. - */ - DBUG_ASSERT(new_db.str && new_db.length); - - if (thd->db) - { - old_db->length= (uint) (strmake(old_db->str, thd->db, old_db->length - 1) - - old_db->str); - } - else - { - old_db->str[0]= '\0'; - old_db->length= 0; - } - - /* Don't change the database if the new name is the same as the old one. */ - if (my_strcasecmp(system_charset_info, old_db->str, new_db.str) == 0) - { - *dbchangedp= FALSE; - DBUG_RETURN(0); - } - - ret= mysql_change_db(thd, &new_db, no_access_check); - - *dbchangedp= ret == 0; - DBUG_RETURN(ret); -} - |