diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/event_data_objects.cc | 145 | ||||
-rw-r--r-- | sql/event_data_objects.h | 7 | ||||
-rw-r--r-- | sql/event_db_repository.cc | 48 | ||||
-rw-r--r-- | sql/event_db_repository.h | 5 | ||||
-rw-r--r-- | sql/events.cc | 21 | ||||
-rw-r--r-- | sql/mysql_priv.h | 96 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 21 | ||||
-rw-r--r-- | sql/sp.cc | 254 | ||||
-rw-r--r-- | sql/sp_head.cc | 103 | ||||
-rw-r--r-- | sql/sp_head.h | 78 | ||||
-rw-r--r-- | sql/sql_lex.cc | 205 | ||||
-rw-r--r-- | sql/sql_lex.h | 65 | ||||
-rw-r--r-- | sql/sql_parse.cc | 46 | ||||
-rw-r--r-- | sql/sql_partition.cc | 2 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 3 | ||||
-rw-r--r-- | sql/sql_show.cc | 439 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 417 | ||||
-rw-r--r-- | sql/sql_trigger.h | 27 | ||||
-rw-r--r-- | sql/sql_view.cc | 80 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 100 | ||||
-rw-r--r-- | sql/table.cc | 124 | ||||
-rw-r--r-- | sql/table.h | 51 |
22 files changed, 2145 insertions, 192 deletions
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 77dc33e6265..786a31497a9 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -23,6 +23,126 @@ #define EVEX_MAX_INTERVAL_VALUE 1000000000L +/*************************************************************************/ + +/** + Event_creation_ctx -- creation context of events. +*/ + +class Event_creation_ctx :public Stored_program_creation_ctx, + public Sql_alloc +{ +public: + static bool load_from_db(THD *thd, + MEM_ROOT *event_mem_root, + const char *db_name, + const char *event_name, + TABLE *event_tbl, + Stored_program_creation_ctx **ctx); + +public: + virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) + { + return new (mem_root) + Event_creation_ctx(m_client_cs, m_connection_cl, m_db_cl); + } + +protected: + virtual Object_creation_ctx *create_backup_ctx(THD *thd) const + { + /* + We can avoid usual backup/restore employed in stored programs since we + know that this is a top level statement and the worker thread is + allocated exclusively to execute this event. + */ + + return NULL; + } + +private: + Event_creation_ctx(CHARSET_INFO *client_cs, + CHARSET_INFO *connection_cl, + CHARSET_INFO *db_cl) + : Stored_program_creation_ctx(client_cs, connection_cl, db_cl) + { } +}; + +/************************************************************************** + Event_creation_ctx implementation. +**************************************************************************/ + +bool +Event_creation_ctx::load_from_db(THD *thd, + MEM_ROOT *event_mem_root, + const char *db_name, + const char *event_name, + TABLE *event_tbl, + Stored_program_creation_ctx **ctx) +{ + /* Load character set/collation attributes. */ + + CHARSET_INFO *client_cs; + CHARSET_INFO *connection_cl; + CHARSET_INFO *db_cl; + + bool invalid_creation_ctx= FALSE; + + if (load_charset(event_mem_root, + event_tbl->field[ET_FIELD_CHARACTER_SET_CLIENT], + thd->variables.character_set_client, + &client_cs)) + { + sql_print_warning("Event '%s'.'%s': invalid value " + "in column mysql.event.character_set_client.", + (const char *) db_name, + (const char *) event_name); + + invalid_creation_ctx= TRUE; + } + + if (load_collation(event_mem_root, + event_tbl->field[ET_FIELD_COLLATION_CONNECTION], + thd->variables.collation_connection, + &connection_cl)) + { + sql_print_warning("Event '%s'.'%s': invalid value " + "in column mysql.event.collation_connection.", + (const char *) db_name, + (const char *) event_name); + + invalid_creation_ctx= TRUE; + } + + if (load_collation(event_mem_root, + event_tbl->field[ET_FIELD_DB_COLLATION], + NULL, + &db_cl)) + { + sql_print_warning("Event '%s'.'%s': invalid value " + "in column mysql.event.db_collation.", + (const char *) db_name, + (const char *) event_name); + + invalid_creation_ctx= TRUE; + } + + /* + If we failed to resolve the database collation, load the default one + from the disk. + */ + + if (!db_cl) + db_cl= get_default_db_collation(thd, db_name); + + /* Create the context. */ + + *ctx= new Event_creation_ctx(client_cs, connection_cl, db_cl); + + return invalid_creation_ctx; +} + +/*************************************************************************/ + /* Initiliazes dbname and name of an Event_queue_element_for_exec object @@ -761,6 +881,7 @@ Event_timed::init() /** Load an event's body from a row from mysql.event. + @details This method is silent on errors and should behave like that. Callers should handle throwing of error messages. The reason is that the class should not know about how to deal with communication. @@ -797,6 +918,9 @@ Event_job_data::load_from_row(THD *thd, TABLE *table) if (load_time_zone(thd, tz_name)) DBUG_RETURN(TRUE); + Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str, table, + &creation_ctx); + ptr= strchr(definer.str, '@'); if (! ptr) @@ -979,9 +1103,20 @@ Event_timed::load_from_row(THD *thd, TABLE *table) if (load_string_fields(table->field, ET_FIELD_BODY, &body, + ET_FIELD_BODY_UTF8, &body_utf8, ET_FIELD_COUNT)) DBUG_RETURN(TRUE); + if (Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str, + table, &creation_ctx)) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_EVENT_INVALID_CREATION_CTX, + ER(ER_EVENT_INVALID_CREATION_CTX), + (const char *) dbname.str, + (const char *) name.str); + } ptr= strchr(definer.str, '@'); @@ -1743,7 +1878,6 @@ Event_job_data::execute(THD *thd, bool drop) #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context event_sctx, *save_sctx= NULL; #endif - CHARSET_INFO *charset_connection; List<Item> empty_item_list; bool ret= TRUE; @@ -1808,12 +1942,6 @@ Event_job_data::execute(THD *thd, bool drop) this is a top level statement and the worker thread is allocated exclusively to execute this event. */ - charset_connection= get_charset_by_csname("utf8", - MY_CS_PRIMARY, MYF(MY_WME)); - thd->variables.character_set_client= charset_connection; - thd->variables.character_set_results= charset_connection; - thd->variables.collation_connection= charset_connection; - thd->update_charset(); thd->variables.sql_mode= sql_mode; thd->variables.time_zone= time_zone; @@ -1830,7 +1958,7 @@ Event_job_data::execute(THD *thd, bool drop) Lex_input_stream lip(thd, thd->query, thd->query_length); lex_start(thd); - if (parse_sql(thd, &lip)) + if (parse_sql(thd, &lip, creation_ctx)) { sql_print_error("Event Scheduler: " "%serror during compilation of %s.%s", @@ -1850,6 +1978,7 @@ Event_job_data::execute(THD *thd, bool drop) sphead->m_flags|= sp_head::LOG_GENERAL_LOG; sphead->set_info(0, 0, &thd->lex->sp_chistics, sql_mode); + sphead->set_creation_ctx(creation_ctx); sphead->optimize(); ret= sphead->execute_procedure(thd, &empty_item_list); diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index d79732780ff..a8e7d1720ca 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -20,8 +20,6 @@ #define EVEX_BAD_PARAMS -5 #define EVEX_MICROSECOND_UNSUP -6 -class sp_head; -class Sql_alloc; class Event_queue_element_for_exec { @@ -151,6 +149,9 @@ public: ulong sql_mode; + class Stored_program_creation_ctx *creation_ctx; + LEX_STRING body_utf8; + Event_timed(); virtual ~Event_timed(); @@ -174,6 +175,8 @@ public: ulong sql_mode; + class Stored_program_creation_ctx *creation_ctx; + Event_job_data(); virtual bool diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 703c4160216..a16a04739e2 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -123,6 +123,26 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = { C_STRING_WITH_LEN("time_zone") }, { C_STRING_WITH_LEN("char(64)") }, { C_STRING_WITH_LEN("latin1") } + }, + { + { C_STRING_WITH_LEN("character_set_client") }, + { C_STRING_WITH_LEN("char(32)") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("collation_connection") }, + { C_STRING_WITH_LEN("char(32)") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("db_collation") }, + { C_STRING_WITH_LEN("char(32)") }, + { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("body_utf8") }, + { C_STRING_WITH_LEN("longblob") }, + { NULL, 0 } } }; @@ -135,6 +155,7 @@ const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] = @param thd THD @param table The row to fill out @param et Event's data + @param sp Event stored routine @param is_update CREATE EVENT or ALTER EVENT @retval FALSE success @@ -281,6 +302,33 @@ mysql_event_fill_row(THD *thd, goto err_truncate; } + fields[ET_FIELD_CHARACTER_SET_CLIENT]->set_notnull(); + fields[ET_FIELD_CHARACTER_SET_CLIENT]->store( + thd->variables.character_set_client->csname, + strlen(thd->variables.character_set_client->csname), + system_charset_info); + + fields[ET_FIELD_COLLATION_CONNECTION]->set_notnull(); + fields[ET_FIELD_COLLATION_CONNECTION]->store( + thd->variables.collation_connection->name, + strlen(thd->variables.collation_connection->name), + system_charset_info); + + { + CHARSET_INFO *db_cl= get_default_db_collation(thd, et->dbname.str); + + fields[ET_FIELD_DB_COLLATION]->set_notnull(); + fields[ET_FIELD_DB_COLLATION]->store( + db_cl->name, strlen(db_cl->name), system_charset_info); + } + + if (et->body_changed) + { + fields[ET_FIELD_BODY_UTF8]->set_notnull(); + fields[ET_FIELD_BODY_UTF8]->store( + sp->m_body_utf8.str, sp->m_body_utf8.length, system_charset_info); + } + DBUG_RETURN(FALSE); err_truncate: diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h index 64e19854933..b60d2ea7afc 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -42,6 +42,10 @@ enum enum_events_table_field ET_FIELD_COMMENT, ET_FIELD_ORIGINATOR, ET_FIELD_TIME_ZONE, + ET_FIELD_CHARACTER_SET_CLIENT, + ET_FIELD_COLLATION_CONNECTION, + ET_FIELD_DB_COLLATION, + ET_FIELD_BODY_UTF8, ET_FIELD_COUNT /* a cool trick to count the number of fields :) */ }; @@ -53,6 +57,7 @@ events_table_index_read_for_db(THD *thd, TABLE *schema_table, int events_table_scan_all(THD *thd, TABLE *schema_table, TABLE *event_table); + class Event_basic; class Event_parse_data; diff --git a/sql/events.cc b/sql/events.cc index 2d1b3c59a4c..e48daeca63d 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -19,6 +19,7 @@ #include "event_db_repository.h" #include "event_queue.h" #include "event_scheduler.h" +#include "sp_head.h" // for Stored_program_creation_ctx /* TODO list : @@ -698,6 +699,15 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol) field_list.push_back(new Item_empty_string("Create Event", show_str.length())); + field_list.push_back( + new Item_empty_string("character_set_client", MY_CS_NAME_SIZE)); + + field_list.push_back( + new Item_empty_string("collation_connection", MY_CS_NAME_SIZE)); + + field_list.push_back( + new Item_empty_string("Database Collation", MY_CS_NAME_SIZE)); + if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -707,7 +717,16 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol) protocol->store(et->name.str, et->name.length, system_charset_info); protocol->store(sql_mode.str, sql_mode.length, system_charset_info); protocol->store(tz_name->ptr(), tz_name->length(), system_charset_info); - protocol->store(show_str.c_ptr(), show_str.length(), system_charset_info); + protocol->store(show_str.c_ptr(), show_str.length(), &my_charset_bin); + protocol->store(et->creation_ctx->get_client_cs()->csname, + strlen(et->creation_ctx->get_client_cs()->csname), + system_charset_info); + protocol->store(et->creation_ctx->get_connection_cl()->name, + strlen(et->creation_ctx->get_connection_cl()->name), + system_charset_info); + protocol->store(et->creation_ctx->get_db_cl()->name, + strlen(et->creation_ctx->get_db_cl()->name), + system_charset_info); if (protocol->write()) DBUG_RETURN(TRUE); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 5a50b03282f..ae7c4ca5f3f 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -150,6 +150,86 @@ extern MY_LOCALE *my_default_lc_time_names; MY_LOCALE *my_locale_by_name(const char *name); MY_LOCALE *my_locale_by_number(uint number); +/*************************************************************************/ + +/** + Object_creation_ctx -- interface for creation context of database objects + (views, stored routines, events, triggers). Creation context -- is a set + of attributes, that should be fixed at the creation time and then be used + each time the object is parsed or executed. +*/ + +class Object_creation_ctx +{ +public: + Object_creation_ctx *set_n_backup(THD *thd); + + void restore_env(THD *thd, Object_creation_ctx *backup_ctx); + +protected: + virtual Object_creation_ctx *create_backup_ctx(THD *thd) = 0; + + virtual void change_env(THD *thd) const = 0; + +public: + virtual ~Object_creation_ctx() + { } +}; + +/*************************************************************************/ + +/** + Default_object_creation_ctx -- default implementation of + Object_creation_ctx. +*/ + +class Default_object_creation_ctx : public Object_creation_ctx +{ +public: + CHARSET_INFO *get_client_cs() + { + return m_client_cs; + } + + CHARSET_INFO *get_connection_cl() + { + return m_connection_cl; + } + +protected: + Default_object_creation_ctx(THD *thd); + + Default_object_creation_ctx(CHARSET_INFO *client_cs, + CHARSET_INFO *connection_cl); + +protected: + virtual Object_creation_ctx *create_backup_ctx(THD *thd); + + virtual void change_env(THD *thd) const; + +protected: + /** + client_cs stores the value of character_set_client session variable. + The only character set attribute is used. + + Client character set is included into query context, because we save + query in the original character set, which is client character set. So, + in order to parse the query properly we have to switch client character + set on parsing. + */ + CHARSET_INFO *m_client_cs; + + /** + connection_cl stores the value of collation_connection session + variable. Both character set and collation attributes are used. + + Connection collation is included into query context, becase it defines + the character set and collation of text literals in internal + representation of query (item-objects). + */ + CHARSET_INFO *m_connection_cl; +}; + /*************************************************************************** Configuration parameters ****************************************************************************/ @@ -618,7 +698,9 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg, uint max_char_length, CHARSET_INFO *cs, bool no_error); -bool parse_sql(THD *thd, class Lex_input_stream *lip); +bool parse_sql(THD *thd, + class Lex_input_stream *lip, + class Object_creation_ctx *creation_ctx); enum enum_mysql_completiontype { ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7, @@ -2156,12 +2238,24 @@ bool schema_table_store_record(THD *thd, TABLE *table); int item_create_init(); void item_create_cleanup(); +bool show_create_trigger(THD *thd, const sp_name *trg_name); + inline void lex_string_set(LEX_STRING *lex_str, const char *c_str) { lex_str->str= (char *) c_str; lex_str->length= strlen(c_str); } +bool load_charset(MEM_ROOT *mem_root, + Field *field, + CHARSET_INFO *dflt_cs, + CHARSET_INFO **cs); + +bool load_collation(MEM_ROOT *mem_root, + Field *field, + CHARSET_INFO *dflt_cl, + CHARSET_INFO **cl); + #endif /* MYSQL_SERVER */ #endif /* MYSQL_CLIENT */ diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 1dde977800b..0e3544415d1 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6076,3 +6076,24 @@ ER_SLAVE_MASTER_COM_FAILURE eng "Master command %s failed: %s" ER_BINLOG_LOGGING_IMPOSSIBLE eng "Binary logging not possible. Message: %s" + +ER_VIEW_NO_CREATION_CTX + eng "View `%-.64s`.`%-.64s` has no creation context" +ER_VIEW_INVALID_CREATION_CTX + eng "Creation context of view `%-.64s`.`%-.64s' is invalid" + +ER_SR_INVALID_CREATION_CTX + eng "Creation context of stored routine `%-.64s`.`%-.64s` is invalid" + +ER_TRG_CORRUPTED_FILE + eng "Corrupted TRG file for table `%-.64s`.`%-.64s`" +ER_TRG_NO_CREATION_CTX + eng "Triggers for table `%-.64s`.`%-.64s` have no creation context" +ER_TRG_INVALID_CREATION_CTX + eng "Trigger creation context of table `%-.64s`.`%-.64s` is invalid" + +ER_EVENT_INVALID_CREATION_CTX + eng "Creation context of event `%-.64s`.`%-.64s` is invalid" + +ER_TRG_CANT_OPEN_TABLE + eng "Cannot open table for trigger `%-.64s`.`%-.64s`" diff --git a/sql/sp.cc b/sql/sp.cc index 6e890f638d0..aa2cf87bcee 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -35,7 +35,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); /* * @@ -61,12 +62,191 @@ 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 +/*************************************************************************/ + +/** + Stored_routine_creation_ctx -- creation context of stored routines + (stored procedures and functions). +*/ + +class Stored_routine_creation_ctx : public Stored_program_creation_ctx, + public Sql_alloc +{ +public: + static Stored_routine_creation_ctx * + load_from_db(THD *thd, const class 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 + { + 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; +} + +/*************************************************************************/ + +bool load_collation(MEM_ROOT *mem_root, + Field *field, + CHARSET_INFO *dflt_cl, + CHARSET_INFO **cl) +{ + String cl_name; + + if (get_field(mem_root, field, &cl_name)) + { + *cl= dflt_cl; + return TRUE; + } + + *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) +{ + /* Load character set/collation attributes. */ + + CHARSET_INFO *client_cs; + CHARSET_INFO *connection_cl; + CHARSET_INFO *db_cl; + + 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); + + 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)) + { + 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; + } + + 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); + } + + /* + If we failed to retrieve the database collation, load the default one + from the disk. + */ + + 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. @@ -213,6 +393,8 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) String str(buff, sizeof(buff), &my_charset_bin); ulong 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, (int) name->m_name.length, name->m_name.str)); @@ -313,13 +495,14 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) 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; ret= db_load_routine(thd, type, name, sphp, sql_mode, params, returns, body, chistics, - definer, created, modified); - + definer, created, modified, creation_ctx); done: if (table) close_system_tables(thd, &open_tables_state_backup); @@ -331,7 +514,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) { LEX *old_lex= thd->lex, newlex; String defstr; @@ -361,7 +545,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, 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 @@ -381,6 +565,15 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, goto end; } + /* + Change current database if needed. + + collation_database will be updated here. However, it can be wrong, + because it will contain the current value of the database collation. + We need collation_database to be fixed at the creation time -- so + we'll update it later in switch_query_ctx(). + */ + if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged))) goto end; @@ -388,9 +581,10 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, { Lex_input_stream lip(thd, defstr.c_ptr(), defstr.length()); + lex_start(thd); - if (parse_sql(thd, &lip) || newlex.sphead == NULL) + if (parse_sql(thd, &lip, creation_ctx) || newlex.sphead == NULL) { sp_head *sp= newlex.sphead; @@ -406,6 +600,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, *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(); } } @@ -465,6 +660,8 @@ sp_create_routine(THD *thd, int type, sp_head *sp) TABLE *table; char definer[USER_HOST_BUFF_SIZE]; + CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str); + DBUG_ENTER("sp_create_routine"); DBUG_PRINT("enter", ("type: %d name: %.*s",type, (int) sp->m_name.length, sp->m_name.str)); @@ -576,6 +773,26 @@ sp_create_routine(THD *thd, int type, sp_head *sp) } } + table->field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]->set_notnull(); + 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(); + 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(); + 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(); + table->field[MYSQL_PROC_FIELD_BODY_UTF8]->store( + sp->m_body_utf8.str, sp->m_body_utf8.length, system_charset_info); + ret= SP_OK; if (table->file->ha_write_row(table->record[0])) ret= SP_WRITE_ROW_FAILED; @@ -740,15 +957,18 @@ struct st_used_field static struct st_used_field init_fields[]= { - { "Db", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0}, - { "Name", NAME_CHAR_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_CHAR_LEN, MYSQL_TYPE_STRING, 0}, - { 0, 0, MYSQL_TYPE_STRING, 0} + { "Db", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0}, + { "Name", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0}, + { "Type", 9, MYSQL_TYPE_STRING, 0}, + { "Definer", USER_HOST_BUFF_SIZE, 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_CHAR_LEN, MYSQL_TYPE_STRING, 0}, + { "character_set_client", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0}, + { "collation_connection", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0}, + { "Database Collation", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0}, + { 0, 0, MYSQL_TYPE_STRING, 0} }; @@ -1124,7 +1344,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; @@ -1853,4 +2074,3 @@ sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, *dbchangedp= ret == 0; DBUG_RETURN(ret); } - diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 821d19dca79..3e1f50b91bb 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -180,6 +180,7 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_SHOW_CREATE_FUNC: case SQLCOM_SHOW_CREATE_PROC: case SQLCOM_SHOW_CREATE_EVENT: + case SQLCOM_SHOW_CREATE_TRIGGER: case SQLCOM_SHOW_DATABASES: case SQLCOM_SHOW_ERRORS: case SQLCOM_SHOW_FIELDS: @@ -280,7 +281,6 @@ sp_get_flags_for_command(LEX *lex) return flags; } - /* Prepare an Item for evaluation (call of fix_fields). @@ -426,8 +426,6 @@ check_routine_name(LEX_STRING *ident) return FALSE; } -/* ------------------------------------------------------------------ */ - /* * @@ -493,6 +491,10 @@ sp_head::sp_head() m_lex.empty(); hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0); hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0); + + m_body_utf8.str= NULL; + m_body_utf8.length= 0; + DBUG_VOID_RETURN; } @@ -550,32 +552,53 @@ sp_head::init_sp_name(THD *thd, sp_name *spname) void -sp_head::init_strings(THD *thd, LEX *lex) +sp_head::set_body_start(THD *thd, const char *begin_ptr) +{ + m_body_begin= begin_ptr; + thd->m_lip->body_utf8_start(thd, begin_ptr); +} + + +void +sp_head::set_stmt_end(THD *thd) { - DBUG_ENTER("sp_head::init_strings"); - const char *endp; /* Used to trim the end */ - /* During parsing, we must use thd->mem_root */ - MEM_ROOT *root= thd->mem_root; - Lex_input_stream *lip=thd->m_lip; + Lex_input_stream *lip= thd->m_lip; /* shortcut */ + const char *end_ptr= lip->get_cpp_ptr(); /* shortcut */ + + /* Make the string of parameters. */ if (m_param_begin && m_param_end) { m_params.length= m_param_end - m_param_begin; - m_params.str= strmake_root(root, m_param_begin, m_params.length); + m_params.str= thd->strmake(m_param_begin, m_params.length); } - endp= lip->get_cpp_ptr(); - lex->stmt_definition_end= endp; + /* Remember end pointer for further dumping of whole statement. */ + + thd->lex->stmt_definition_end= end_ptr; + + /* Make the string of body (in the original character set). */ - m_body.length= endp - m_body_begin; - m_body.str= strmake_root(root, m_body_begin, m_body.length); + m_body.length= end_ptr - m_body_begin; + m_body.str= thd->strmake(m_body_begin, m_body.length); trim_whitespace(thd->charset(), & m_body); - m_defstr.length= endp - lip->get_cpp_buf(); - m_defstr.str= strmake_root(root, lip->get_cpp_buf(), m_defstr.length); - trim_whitespace(thd->charset(), & m_defstr); + /* Make the string of UTF-body. */ - DBUG_VOID_RETURN; + lip->body_utf8_append(end_ptr); + + m_body_utf8.length= lip->get_body_utf8_length(); + m_body_utf8.str= thd->strmake(lip->get_body_utf8_str(), m_body_utf8.length); + trim_whitespace(thd->charset(), & m_body_utf8); + + /* + Make the string of whole stored-program-definition query (in the + original character set). + */ + + m_defstr.length= end_ptr - lip->get_cpp_buf(); + m_defstr.str= thd->strmake(lip->get_cpp_buf(), m_defstr.length); + trim_whitespace(thd->charset(), & m_defstr); } @@ -968,6 +991,8 @@ sp_head::execute(THD *thd) Item_change_list old_change_list; String old_packet; + Object_creation_ctx *saved_creation_ctx; + /* Use some extra margin for possible SP recursion and functions */ if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (uchar*)&old_packet)) DBUG_RETURN(TRUE); @@ -1057,6 +1082,10 @@ sp_head::execute(THD *thd) */ thd->spcont->callers_arena= &backup_arena; + /* Switch query context. */ + + saved_creation_ctx= m_creation_ctx->set_n_backup(thd); + do { sp_instr *i; @@ -1143,6 +1172,12 @@ sp_head::execute(THD *thd) } } while (!err_status && !thd->killed); + /* Restore query context. */ + + m_creation_ctx->restore_env(thd, saved_creation_ctx); + + /* Restore arena. */ + thd->restore_active_arena(&execute_arena, &backup_arena); thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error @@ -1971,18 +2006,15 @@ sp_head::fill_field_definition(THD *thd, LEX *lex, enum enum_field_types field_type, Create_field *field_def) { - HA_CREATE_INFO sp_db_info; LEX_STRING cmt = { 0, 0 }; uint unused1= 0; int unused2= 0; - load_db_opt_by_name(thd, m_db.str, &sp_db_info); - if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec, lex->type, (Item*) 0, (Item*) 0, &cmt, 0, &lex->interval_list, - (lex->charset ? lex->charset : - sp_db_info.default_table_charset), + lex->charset ? lex->charset : + thd->variables.collation_database, lex->uint_geom_type)) return TRUE; @@ -2054,13 +2086,6 @@ sp_head::set_info(longlong created, longlong modified, void -sp_head::set_body_begin_ptr(Lex_input_stream *lip, const char *begin_ptr) -{ - m_body_begin= begin_ptr; -} - - -void sp_head::set_definer(const char *definer, uint definerlen) { char user_name_holder[USERNAME_LENGTH + 1]; @@ -2212,6 +2237,15 @@ sp_head::show_create_routine(THD *thd, int type) fields.push_back(stmt_fld); } + fields.push_back(new Item_empty_string("character_set_client", + MY_CS_NAME_SIZE)); + + fields.push_back(new Item_empty_string("collation_connection", + MY_CS_NAME_SIZE)); + + fields.push_back(new Item_empty_string("Database Collation", + MY_CS_NAME_SIZE)); + if (protocol->send_fields(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { @@ -2230,6 +2264,11 @@ sp_head::show_create_routine(THD *thd, int type) else protocol->store_null(); + + protocol->store(m_creation_ctx->get_client_cs()->csname, system_charset_info); + protocol->store(m_creation_ctx->get_connection_cl()->name, system_charset_info); + protocol->store(m_creation_ctx->get_db_cl()->name, system_charset_info); + err_status= protocol->write(); if (!err_status) @@ -2424,7 +2463,9 @@ sp_head::show_routine_code(THD *thd) if ((res= protocol->write())) break; } - send_eof(thd); + + if (!res) + send_eof(thd); DBUG_RETURN(res); } diff --git a/sql/sp_head.h b/sql/sp_head.h index 50f6017ca0d..490fda67bfe 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -45,6 +45,58 @@ class sp_instr_jump_if_not; struct sp_cond_type; struct sp_variable; +/*************************************************************************/ + +/** + Stored_program_creation_ctx -- base class for creation context of stored + programs (stored routines, triggers, events). +*/ + +class Stored_program_creation_ctx :public Default_object_creation_ctx +{ +public: + CHARSET_INFO *get_db_cl() + { + return m_db_cl; + } + +public: + virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) = 0; + +protected: + Stored_program_creation_ctx(THD *thd) + : Default_object_creation_ctx(thd), + m_db_cl(thd->variables.collation_database) + { } + + Stored_program_creation_ctx(CHARSET_INFO *client_cs, + CHARSET_INFO *connection_cl, + CHARSET_INFO *db_cl) + : Default_object_creation_ctx(client_cs, connection_cl), + m_db_cl(db_cl) + { } + +protected: + virtual void change_env(THD *thd) const + { + thd->variables.collation_database= m_db_cl; + + Default_object_creation_ctx::change_env(thd); + } + +protected: + /** + db_cl stores the value of the database collation. Both character set + and collation attributes are used. + + Database collation is included into the context because it defines the + default collation for stored-program variables. + */ + CHARSET_INFO *m_db_cl; +}; + +/*************************************************************************/ + class sp_name : public Sql_alloc { public: @@ -136,9 +188,25 @@ public: LEX_STRING m_name; LEX_STRING m_params; LEX_STRING m_body; + LEX_STRING m_body_utf8; LEX_STRING m_defstr; LEX_STRING m_definer_user; LEX_STRING m_definer_host; + +private: + Stored_program_creation_ctx *m_creation_ctx; + +public: + inline Stored_program_creation_ctx *get_creation_ctx() + { + return m_creation_ctx; + } + + inline void set_creation_ctx(Stored_program_creation_ctx *creation_ctx) + { + m_creation_ctx= creation_ctx->clone(mem_root); + } + longlong m_created; longlong m_modified; /* Recursion level of the current SP instance. The levels are numbered from 0 */ @@ -205,9 +273,13 @@ public: void init_sp_name(THD *thd, sp_name *spname); - // Initialize strings after parsing header + /** Set the body-definition start position. */ + void + set_body_start(THD *thd, const char *begin_ptr); + + /** Set the statement-definition (body-definition) end position. */ void - init_strings(THD *thd, LEX *lex); + set_stmt_end(THD *thd); int create(THD *thd); @@ -300,8 +372,6 @@ public: void set_info(longlong created, longlong modified, st_sp_chistics *chistics, ulong sql_mode); - void set_body_begin_ptr(Lex_input_stream *lip, const char *begin_ptr); - void set_definer(const char *definer, uint definerlen); void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 304feea5f5c..8f2231dedf1 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -123,15 +123,19 @@ Lex_input_stream::Lex_input_stream(THD *thd, m_end_of_query(buffer + length), m_tok_start_prev(NULL), m_buf(buffer), + m_buf_length(length), m_echo(true), m_cpp_tok_start(NULL), m_cpp_tok_start_prev(NULL), m_cpp_tok_end(NULL), + m_body_utf8(NULL), + m_cpp_utf8_processed_ptr(NULL), next_state(MY_LEX_START), found_semicolon(NULL), ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)), stmt_prepare_mode(FALSE), - in_comment(NO_COMMENT) + in_comment(NO_COMMENT), + m_underscore_cs(NULL) { m_cpp_buf= (char*) thd->alloc(length + 1); m_cpp_ptr= m_cpp_buf; @@ -140,6 +144,133 @@ Lex_input_stream::Lex_input_stream(THD *thd, Lex_input_stream::~Lex_input_stream() {} +/** + The operation is called from the parser in order to + 1) designate the intention to have utf8 body; + 1) Indicate to the lexer that we will need a utf8 representation of this + statement; + 2) Determine the beginning of the body. + + @param thd Thread context. + @param begin_ptr Pointer to the start of the body in the pre-processed + buffer. +*/ + +void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr) +{ + DBUG_ASSERT(begin_ptr); + DBUG_ASSERT(m_cpp_buf <= begin_ptr && begin_ptr <= m_cpp_buf + m_buf_length); + + uint body_utf8_length= + (m_buf_length / thd->variables.character_set_client->mbminlen) * + my_charset_utf8_bin.mbmaxlen; + + m_body_utf8= (char *) thd->alloc(body_utf8_length + 1); + m_body_utf8_ptr= m_body_utf8; + *m_body_utf8_ptr= 0; + + m_cpp_utf8_processed_ptr= begin_ptr; +} + +/** + The operation appends unprocessed part of pre-processed buffer till + the given pointer (ptr) and sets m_cpp_utf8_processed_ptr to end_ptr. + + The idea is that some tokens in the pre-processed buffer (like character + set introducers) should be skipped. + + Example: + CPP buffer: SELECT 'str1', _latin1 'str2'; + m_cpp_utf8_processed_ptr -- points at the "SELECT ..."; + In order to skip "_latin1", the following call should be made: + body_utf8_append(<pointer to "_latin1 ...">, <pointer to " 'str2'...">) + + @param ptr Pointer in the pre-processed buffer, which specifies the + end of the chunk, which should be appended to the utf8 + body. + @param end_ptr Pointer in the pre-processed buffer, to which + m_cpp_utf8_processed_ptr will be set in the end of the + operation. +*/ + +void Lex_input_stream::body_utf8_append(const char *ptr, + const char *end_ptr) +{ + DBUG_ASSERT(m_cpp_buf <= ptr && ptr <= m_cpp_buf + m_buf_length); + DBUG_ASSERT(m_cpp_buf <= end_ptr && end_ptr <= m_cpp_buf + m_buf_length); + + if (!m_body_utf8) + return; + + if (m_cpp_utf8_processed_ptr >= ptr) + return; + + int bytes_to_copy= ptr - m_cpp_utf8_processed_ptr; + + memcpy(m_body_utf8_ptr, m_cpp_utf8_processed_ptr, bytes_to_copy); + m_body_utf8_ptr += bytes_to_copy; + *m_body_utf8_ptr= 0; + + m_cpp_utf8_processed_ptr= end_ptr; +} + +/** + The operation appends unprocessed part of the pre-processed buffer till + the given pointer (ptr) and sets m_cpp_utf8_processed_ptr to ptr. + + @param ptr Pointer in the pre-processed buffer, which specifies the end + of the chunk, which should be appended to the utf8 body. +*/ + +void Lex_input_stream::body_utf8_append(const char *ptr) +{ + body_utf8_append(ptr, ptr); +} + +/** + The operation converts the specified text literal to the utf8 and appends + the result to the utf8-body. + + @param thd Thread context. + @param txt Text literal. + @param txt_cs Character set of the text literal. + @param end_ptr Pointer in the pre-processed buffer, to which + m_cpp_utf8_processed_ptr will be set in the end of the + operation. +*/ + +void Lex_input_stream::body_utf8_append_literal(THD *thd, + const LEX_STRING *txt, + CHARSET_INFO *txt_cs, + const char *end_ptr) +{ + if (!m_cpp_utf8_processed_ptr) + return; + + LEX_STRING utf_txt; + + if (txt_cs->number != my_charset_utf8_general_ci.number) + { + thd->convert_string(&utf_txt, + &my_charset_utf8_general_ci, + txt->str, txt->length, + txt_cs); + } + else + { + utf_txt.str= txt->str; + utf_txt.length= txt->length; + } + + /* NOTE: utf_txt.length is in bytes, not in symbols. */ + + memcpy(m_body_utf8_ptr, utf_txt.str, utf_txt.length); + m_body_utf8_ptr += utf_txt.length; + *m_body_utf8_ptr= 0; + + m_cpp_utf8_processed_ptr= end_ptr; +} + /* This is called before every query that is to be parsed. @@ -231,6 +362,7 @@ void lex_start(THD *thd) lex->server_options.socket= 0; lex->server_options.owner= 0; lex->server_options.port= -1; + DBUG_VOID_RETURN; } @@ -311,6 +443,10 @@ static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length) lip->yyUnget(); // ptr points now after last token char tmp.length=lip->yytoklen=length; tmp.str= lip->m_thd->strmake(lip->get_tok_start() + skip, tmp.length); + + lip->m_cpp_text_start= lip->get_cpp_tok_start() + skip; + lip->m_cpp_text_end= lip->m_cpp_text_start + tmp.length; + return tmp; } @@ -334,10 +470,17 @@ static LEX_STRING get_quoted_token(Lex_input_stream *lip, from= lip->get_tok_start() + skip; to= tmp.str; end= to+length; + + lip->m_cpp_text_start= lip->get_cpp_tok_start() + skip; + lip->m_cpp_text_end= lip->m_cpp_text_start + length; + for ( ; to != end; ) { if ((*to++= *from++) == quote) + { from++; // Skip double quotes + lip->m_cpp_text_start++; + } } *to= 0; // End null for safety return tmp; @@ -402,6 +545,10 @@ static char *get_text(Lex_input_stream *lip, int pre_skip, int post_skip) if (!(start= (char*) lip->m_thd->alloc((uint) (end-str)+1))) return (char*) ""; // Sql_alloc has set error flag + + lip->m_cpp_text_start= lip->get_cpp_tok_start() + pre_skip; + lip->m_cpp_text_end= lip->get_cpp_ptr() - post_skip; + if (!found_escape) { lip->yytoklen=(uint) (end-str); @@ -743,18 +890,33 @@ int MYSQLlex(void *arg, void *yythd) } yylval->lex_str=get_token(lip, 0, length); - /* + /* Note: "SELECT _bla AS 'alias'" _bla should be considered as a IDENT if charset haven't been found. - So we don't use MYF(MY_WME) with get_charset_by_csname to avoid + So we don't use MYF(MY_WME) with get_charset_by_csname to avoid producing an error. */ - if ((yylval->lex_str.str[0]=='_') && - (lex->underscore_charset= - get_charset_by_csname(yylval->lex_str.str + 1, - MY_CS_PRIMARY,MYF(0)))) - return(UNDERSCORE_CHARSET); + if (yylval->lex_str.str[0] == '_') + { + CHARSET_INFO *cs= get_charset_by_csname(yylval->lex_str.str + 1, + MY_CS_PRIMARY, MYF(0)); + if (cs) + { + yylval->charset= cs; + lip->m_underscore_cs= cs; + + lip->body_utf8_append(lip->m_cpp_text_start, + lip->get_cpp_tok_start() + length); + return(UNDERSCORE_CHARSET); + } + } + + lip->body_utf8_append(lip->m_cpp_text_start); + + lip->body_utf8_append_literal(thd, &yylval->lex_str, cs, + lip->m_cpp_text_end); + return(result_state); // IDENT or IDENT_QUOTED case MY_LEX_IDENT_SEP: // Found ident and now '.' @@ -852,6 +1014,12 @@ int MYSQLlex(void *arg, void *yythd) lip->next_state=MY_LEX_IDENT_SEP;// Next is '.' yylval->lex_str= get_token(lip, 0, lip->yyLength()); + + lip->body_utf8_append(lip->m_cpp_text_start); + + lip->body_utf8_append_literal(thd, &yylval->lex_str, cs, + lip->m_cpp_text_end); + return(result_state); case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char @@ -887,6 +1055,12 @@ int MYSQLlex(void *arg, void *yythd) if (c == quote_char) lip->yySkip(); // Skip end ` lip->next_state= MY_LEX_START; + + lip->body_utf8_append(lip->m_cpp_text_start); + + lip->body_utf8_append_literal(thd, &yylval->lex_str, cs, + lip->m_cpp_text_end); + return(IDENT_QUOTED); } case MY_LEX_INT_OR_REAL: // Complete int or incomplete real @@ -995,6 +1169,15 @@ int MYSQLlex(void *arg, void *yythd) break; } yylval->lex_str.length=lip->yytoklen; + + lip->body_utf8_append(lip->m_cpp_text_start); + + lip->body_utf8_append_literal(thd, &yylval->lex_str, + lip->m_underscore_cs ? lip->m_underscore_cs : cs, + lip->m_cpp_text_end); + + lip->m_underscore_cs= NULL; + return(TEXT_STRING); case MY_LEX_COMMENT: // Comment @@ -1201,6 +1384,12 @@ int MYSQLlex(void *arg, void *yythd) return(tokval); // Was keyword } yylval->lex_str=get_token(lip, 0, length); + + lip->body_utf8_append(lip->m_cpp_text_start); + + lip->body_utf8_append_literal(thd, &yylval->lex_str, cs, + lip->m_cpp_text_end); + return(result_state); } } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7a47c57a722..254403fe736 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -112,7 +112,8 @@ enum enum_sql_command { SQLCOM_SHOW_CONTRIBUTORS, SQLCOM_CREATE_SERVER, SQLCOM_DROP_SERVER, SQLCOM_ALTER_SERVER, SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT, - SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, + SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, + SQLCOM_SHOW_CREATE_TRIGGER, /* This should be the last !!! */ @@ -1330,6 +1331,26 @@ public: return (uint) ((m_ptr - m_tok_start) - 1); } + /** Get the utf8-body string. */ + const char *get_body_utf8_str() + { + return m_body_utf8; + } + + /** Get the utf8-body length. */ + uint get_body_utf8_length() + { + return m_body_utf8_ptr - m_body_utf8; + } + + void body_utf8_start(THD *thd, const char *begin_ptr); + void body_utf8_append(const char *ptr); + void body_utf8_append(const char *ptr, const char *end_ptr); + void body_utf8_append_literal(THD *thd, + const LEX_STRING *txt, + CHARSET_INFO *txt_cs, + const char *end_ptr); + /** Current thread. */ THD *m_thd; @@ -1361,6 +1382,9 @@ private: /** Begining of the query text in the input stream, in the raw buffer. */ const char* m_buf; + /** Length of the raw buffer. */ + uint m_buf_length; + /** Echo the parsed stream to the pre-processed buffer. */ bool m_echo; @@ -1388,6 +1412,18 @@ private: */ const char* m_cpp_tok_end; + /** UTF8-body buffer created during parsing. */ + char *m_body_utf8; + + /** Pointer to the current position in the UTF8-body buffer. */ + char *m_body_utf8_ptr; + + /** + Position in the pre-processed buffer. The query from m_cpp_buf to + m_cpp_utf_processed_ptr is converted to UTF8-body. + */ + const char *m_cpp_utf8_processed_ptr; + public: /** Current state of the lexical analyser. */ @@ -1410,6 +1446,29 @@ public: /** State of the lexical analyser for comments. */ enum_comment_state in_comment; + + /** + Starting position of the TEXT_STRING or IDENT in the pre-processed + buffer. + + NOTE: this member must be used within MYSQLlex() function only. + */ + const char *m_cpp_text_start; + + /** + Ending position of the TEXT_STRING or IDENT in the pre-processed + buffer. + + NOTE: this member must be used within MYSQLlex() function only. + */ + const char *m_cpp_text_end; + + /** + Character set specified by the character-set-introducer. + + NOTE: this member must be used within MYSQLlex() function only. + */ + CHARSET_INFO *m_underscore_cs; }; @@ -1444,7 +1503,7 @@ typedef struct st_lex : public Query_tables_list DYNAMIC_ARRAY plugins; plugin_ref plugins_static_buffer[INITIAL_LEX_PLUGIN_LIST_SIZE]; - CHARSET_INFO *charset, *underscore_charset; + CHARSET_INFO *charset; /* store original leaf_tables for INSERT SELECT and PS/SP */ TABLE_LIST *leaf_tables_insert; @@ -1635,6 +1694,8 @@ typedef struct st_lex : public Query_tables_list const char *fname_start; const char *fname_end; + LEX_STRING view_body_utf8; + /* Reference to a struct that contains information in various commands to add/create/drop/change table spaces. diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 99c749aa441..5de00071f33 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4074,6 +4074,19 @@ create_sp_error: break; } #endif // ifndef DBUG_OFF + case SQLCOM_SHOW_CREATE_TRIGGER: + { + if (lex->spname->m_name.length > NAME_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); + goto error; + } + + if (show_create_trigger(thd, lex->spname)) + goto error; /* Error has been already logged. */ + + break; + } case SQLCOM_CREATE_VIEW: { /* @@ -5340,7 +5353,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, Lex_input_stream lip(thd, inBuf, length); - bool err= parse_sql(thd, &lip); + bool err= parse_sql(thd, &lip, NULL); *found_semicolon= lip.found_semicolon; if (!err) @@ -5424,7 +5437,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) lex_start(thd); mysql_reset_thd_for_next_command(thd); - if (!parse_sql(thd, &lip) && + if (!parse_sql(thd, &lip, NULL) && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ thd->end_statement(); @@ -7127,23 +7140,44 @@ extern int MYSQLparse(void *thd); // from sql_yacc.cc @param thd Thread context. @param lip Lexer context. + @param creation_ctx Object creation context. @return Error status. @retval FALSE on success. @retval TRUE on parsing error. */ -bool parse_sql(THD *thd, Lex_input_stream *lip) +bool parse_sql(THD *thd, + Lex_input_stream *lip, + Object_creation_ctx *creation_ctx) { - bool err_status; - DBUG_ASSERT(thd->m_lip == NULL); + /* Backup creation context. */ + + Object_creation_ctx *backup_ctx= NULL; + + if (creation_ctx) + backup_ctx= creation_ctx->set_n_backup(thd); + + /* Set Lex_input_stream. */ + thd->m_lip= lip; - err_status= MYSQLparse(thd) != 0 || thd->is_fatal_error; + /* Parse the query. */ + + bool err_status= MYSQLparse(thd) != 0 || thd->is_fatal_error; + + /* Reset Lex_input_stream. */ thd->m_lip= NULL; + /* Restore creation context. */ + + if (creation_ctx) + creation_ctx->restore_env(thd, backup_ctx); + + /* That's it. */ + return err_status; } diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index ad3cf2d3e7a..f3253e5b086 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3728,7 +3728,7 @@ bool mysql_unpack_partition(THD *thd, lex.part_info->part_state= part_state; lex.part_info->part_state_len= part_state_len; DBUG_PRINT("info", ("Parse: %s", part_buf)); - if (parse_sql(thd, &lip)) + if (parse_sql(thd, &lip, NULL)) { thd->free_items(); goto end; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 6994ff99422..406e242cada 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1769,6 +1769,7 @@ static bool check_prepared_statement(Prepared_statement *stmt, case SQLCOM_SHOW_CREATE_PROC: case SQLCOM_SHOW_CREATE_FUNC: case SQLCOM_SHOW_CREATE_EVENT: + case SQLCOM_SHOW_CREATE_TRIGGER: case SQLCOM_SHOW_CREATE: case SQLCOM_SHOW_PROC_CODE: case SQLCOM_SHOW_FUNC_CODE: @@ -2876,7 +2877,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lip.stmt_prepare_mode= TRUE; lex_start(thd); - error= parse_sql(thd, &lip) || + error= parse_sql(thd, &lip, NULL) || thd->net.report_error || init_param_array(this); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 903c8ab74f1..f66897df671 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -54,10 +54,12 @@ enum enum_i_s_events_fields ISE_LAST_ALTERED, ISE_LAST_EXECUTED, ISE_EVENT_COMMENT, - ISE_ORIGINATOR + ISE_ORIGINATOR, + ISE_CLIENT_CS, + ISE_CONNECTION_CL, + ISE_DB_CL }; - #ifndef NO_EMBEDDED_ACCESS_CHECKS static const char *grant_names[]={ "select","insert","update","delete","create","drop","reload","shutdown", @@ -592,6 +594,10 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) } buffer.length(0); + + if (table_list->view) + buffer.set_charset(table_list->view_creation_ctx->get_client_cs()); + if ((table_list->view ? view_store_create_info(thd, table_list, &buffer) : store_create_info(thd, table_list, &buffer, NULL))) @@ -603,6 +609,10 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) field_list.push_back(new Item_empty_string("View",NAME_CHAR_LEN)); field_list.push_back(new Item_empty_string("Create View", max(buffer.length(),1024))); + field_list.push_back(new Item_empty_string("character_set_client", + MY_CS_NAME_SIZE)); + field_list.push_back(new Item_empty_string("collation_connection", + MY_CS_NAME_SIZE)); } else { @@ -626,10 +636,23 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) else protocol->store(table_list->table->alias, system_charset_info); } - protocol->store(buffer.ptr(), buffer.length(), buffer.charset()); + + if (table_list->view) + { + protocol->store(buffer.ptr(), buffer.length(), &my_charset_bin); + + protocol->store(table_list->view_creation_ctx->get_client_cs()->csname, + system_charset_info); + + protocol->store(table_list->view_creation_ctx->get_connection_cl()->name, + system_charset_info); + } + else + protocol->store(buffer.ptr(), buffer.length(), buffer.charset()); if (protocol->write()) DBUG_RETURN(TRUE); + send_eof(thd); DBUG_RETURN(FALSE); } @@ -842,7 +865,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) if (q == EOF) { - packet->append(name, length, system_charset_info); + packet->append(name, length, packet->charset()); return; } @@ -860,7 +883,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) uchar chr= (uchar) *name; length= my_mbcharlen(system_charset_info, chr); /* - my_mbcharlen can retur 0 on a wrong multibyte + my_mbcharlen can return 0 on a wrong multibyte sequence. It is possible when upgrading from 4.0, and identifier contains some accented characters. The manual says it does not work. So we'll just @@ -870,7 +893,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) length= 1; if (length == 1 && chr == (uchar) quote_char) packet->append("e_char, 1, system_charset_info); - packet->append(name, length, packet->charset()); + packet->append(name, length, system_charset_info); } packet->append("e_char, 1, system_charset_info); } @@ -2374,6 +2397,7 @@ int make_db_list(THD *thd, List<char> *files, LEX *lex= thd->lex; *with_i_schema= 0; get_index_field_values(lex, idx_field_vals); + if (is_wild_value) { /* @@ -3421,7 +3445,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, } if (full_access) { - get_field(thd->mem_root, proc_table->field[10], &tmp_string); + get_field(thd->mem_root, proc_table->field[19], &tmp_string); table->field[7]->store(tmp_string.ptr(), tmp_string.length(), cs); table->field[7]->set_notnull(); } @@ -3444,6 +3468,16 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, get_field(thd->mem_root, proc_table->field[15], &tmp_string); table->field[18]->store(tmp_string.ptr(), tmp_string.length(), cs); table->field[19]->store(definer.ptr(), definer.length(), cs); + + get_field(thd->mem_root, proc_table->field[16], &tmp_string); + table->field[20]->store(tmp_string.ptr(), tmp_string.length(), cs); + + get_field(thd->mem_root, proc_table->field[17], &tmp_string); + table->field[21]->store(tmp_string.ptr(), tmp_string.length(), cs); + + get_field(thd->mem_root, proc_table->field[18], &tmp_string); + table->field[22]->store(tmp_string.ptr(), tmp_string.length(), cs); + return schema_table_store_record(thd, table); } } @@ -3621,14 +3655,9 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, table->field[2]->store(tables->view_name.str, tables->view_name.length, cs); if (tables->allowed_show) { - char buff[2048]; - String qwe_str(buff, sizeof(buff), cs); - qwe_str.length(0); - qwe_str.append(STRING_WITH_LEN("/* ")); - append_algorithm(tables, &qwe_str); - qwe_str.append(STRING_WITH_LEN("*/ ")); - qwe_str.append(tables->query.str, tables->query.length); - table->field[3]->store(qwe_str.ptr(), qwe_str.length(), cs); + table->field[3]->store(tables->view_body_utf8.str, + tables->view_body_utf8.length, + cs); } if (tables->with_check != VIEW_CHECK_NONE) @@ -3679,6 +3708,17 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, table->field[7]->store(STRING_WITH_LEN("DEFINER"), cs); else table->field[7]->store(STRING_WITH_LEN("INVOKER"), cs); + + table->field[8]->store( + tables->view_creation_ctx->get_client_cs()->csname, + strlen(tables->view_creation_ctx->get_client_cs()->csname), + cs); + + table->field[9]->store( + tables->view_creation_ctx->get_connection_cl()->name, + strlen(tables->view_creation_ctx->get_connection_cl()->name), + cs); + if (schema_table_store_record(thd, table)) DBUG_RETURN(1); if (res) @@ -3772,7 +3812,10 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db, enum trg_action_time_type timing, LEX_STRING *trigger_stmt, ulong sql_mode, - LEX_STRING *definer_buffer) + LEX_STRING *definer_buffer, + LEX_STRING *client_cs_name, + LEX_STRING *connection_cl_name, + LEX_STRING *db_cl_name) { CHARSET_INFO *cs= system_charset_info; LEX_STRING sql_mode_rep; @@ -3794,7 +3837,12 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db, sys_var_thd_sql_mode::symbolic_mode_representation(thd, sql_mode, &sql_mode_rep); table->field[17]->store(sql_mode_rep.str, sql_mode_rep.length, cs); - table->field[18]->store((const char *)definer_buffer->str, definer_buffer->length, cs); + table->field[18]->store(definer_buffer->str, definer_buffer->length, cs); + table->field[19]->store(client_cs_name->str, client_cs_name->length, cs); + table->field[20]->store(connection_cl_name->str, + connection_cl_name->length, cs); + table->field[21]->store(db_cl_name->str, db_cl_name->length, cs); + return schema_table_store_record(thd, table); } @@ -3830,19 +3878,29 @@ static int get_schema_triggers_record(THD *thd, struct st_table_list *tables, ulong sql_mode; char definer_holder[USER_HOST_BUFF_SIZE]; LEX_STRING definer_buffer; + LEX_STRING client_cs_name; + LEX_STRING connection_cl_name; + LEX_STRING db_cl_name; + definer_buffer.str= definer_holder; if (triggers->get_trigger_info(thd, (enum trg_event_type) event, (enum trg_action_time_type)timing, &trigger_name, &trigger_stmt, &sql_mode, - &definer_buffer)) + &definer_buffer, + &client_cs_name, + &connection_cl_name, + &db_cl_name)) continue; if (store_trigger(thd, table, base_name, file_name, &trigger_name, (enum trg_event_type) event, (enum trg_action_time_type) timing, &trigger_stmt, sql_mode, - &definer_buffer)) + &definer_buffer, + &client_cs_name, + &connection_cl_name, + &db_cl_name)) DBUG_RETURN(1); } } @@ -4320,7 +4378,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; CHARSET_INFO *scs= system_charset_info; MYSQL_TIME time; - Event_timed et; + Event_timed et; DBUG_ENTER("copy_event_to_schema_table"); restore_record(sch_table, s->default_values); @@ -4357,8 +4415,8 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) store(tz_name->ptr(), tz_name->length(), scs); sch_table->field[ISE_EVENT_BODY]-> store(STRING_WITH_LEN("SQL"), scs); - sch_table->field[ISE_EVENT_DEFINITION]-> - store(et.body.str, et.body.length, scs); + sch_table->field[ISE_EVENT_DEFINITION]->store( + et.body_utf8.str, et.body_utf8.length, scs); /* SQL_MODE */ { @@ -4461,6 +4519,24 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) sch_table->field[ISE_EVENT_COMMENT]-> store(et.comment.str, et.comment.length, scs); + sch_table->field[ISE_CLIENT_CS]->set_notnull(); + sch_table->field[ISE_CLIENT_CS]->store( + et.creation_ctx->get_client_cs()->csname, + strlen(et.creation_ctx->get_client_cs()->csname), + scs); + + sch_table->field[ISE_CONNECTION_CL]->set_notnull(); + sch_table->field[ISE_CONNECTION_CL]->store( + et.creation_ctx->get_connection_cl()->name, + strlen(et.creation_ctx->get_connection_cl()->name), + scs); + + sch_table->field[ISE_DB_CL]->set_notnull(); + sch_table->field[ISE_DB_CL]->store( + et.creation_ctx->get_db_cl()->name, + strlen(et.creation_ctx->get_db_cl()->name), + scs); + if (schema_table_store_record(thd, sch_table)) DBUG_RETURN(1); @@ -4987,7 +5063,7 @@ int make_character_sets_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) int make_proc_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) { - int fields_arr[]= {2, 3, 4, 19, 16, 15, 14, 18, -1}; + int fields_arr[]= {2, 3, 4, 19, 16, 15, 14, 18, 20, 21, 22, -1}; int *field_num= fields_arr; ST_FIELD_INFO *field_info; Name_resolution_context *context= &thd->lex->select_lex.context; @@ -5370,14 +5446,20 @@ ST_FIELD_INFO events_fields_info[]= {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, {"STARTS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Starts"}, {"ENDS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Ends"}, - {"STATUS", 18, MYSQL_TYPE_STRING, 0, 0, "Status"}, + {"STATUS", 18, MYSQL_TYPE_STRING, 0, 0, "Status"}, {"ON_COMPLETION", 12, MYSQL_TYPE_STRING, 0, 0, 0}, {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0}, {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0}, {"LAST_EXECUTED", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, {"EVENT_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ORIGINATOR", 10, MYSQL_TYPE_LONGLONG, 0, 0, "Originator"}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} + {"ORIGINATOR", 10, MYSQL_TYPE_LONGLONG, 0, 0, "Originator"}, + {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, + "character_set_client"}, + {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, + "collation_connection"}, + {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, + "Database Collation"}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5412,6 +5494,12 @@ ST_FIELD_INFO proc_fields_info[]= {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, {"ROUTINE_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment"}, {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"}, + {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, + "character_set_client"}, + {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, + "collation_connection"}, + {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, + "Database Collation"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5448,6 +5536,8 @@ ST_FIELD_INFO view_fields_info[]= {"IS_UPDATABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0}, {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, 0}, {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, 0}, + {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0}, + {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5569,6 +5659,12 @@ ST_FIELD_INFO triggers_fields_info[]= {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 1, "Created"}, {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, "sql_mode"}, {"DEFINER", 65535, MYSQL_TYPE_STRING, 0, 0, "Definer"}, + {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, + "character_set_client"}, + {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, + "collation_connection"}, + {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, + "Database Collation"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5856,3 +5952,294 @@ int finalize_schema_table(st_plugin_int *plugin) } DBUG_RETURN(0); } + + +/** + Output trigger information (SHOW CREATE TRIGGER) to the client. + + @param thd Thread context. + @param triggers List of triggers for the table. + @param trigger_idx Index of the trigger to dump. + + @return Operation status + @retval TRUE Error. + @retval FALSE Success. +*/ + +static bool show_create_trigger_impl(THD *thd, + Table_triggers_list *triggers, + int trigger_idx) +{ + int ret_code; + + Protocol *p= thd->protocol; + List<Item> fields; + + LEX_STRING trg_name; + ulonglong trg_sql_mode; + LEX_STRING trg_sql_mode_str; + LEX_STRING trg_sql_original_stmt; + LEX_STRING trg_client_cs_name; + LEX_STRING trg_connection_cl_name; + LEX_STRING trg_db_cl_name; + + /* + TODO: Check privileges here. This functionality will be added by + implementation of the following WL items: + - WL#2227: New privileges for new objects + - WL#3482: Protect SHOW CREATE PROCEDURE | FUNCTION | VIEW | TRIGGER + properly + + SHOW TRIGGERS and I_S.TRIGGERS will be affected too. + */ + + /* Prepare trigger "object". */ + + triggers->get_trigger_info(thd, + trigger_idx, + &trg_name, + &trg_sql_mode, + &trg_sql_original_stmt, + &trg_client_cs_name, + &trg_connection_cl_name, + &trg_db_cl_name); + + sys_var_thd_sql_mode::symbolic_mode_representation(thd, + trg_sql_mode, + &trg_sql_mode_str); + + /* Send header. */ + + fields.push_back(new Item_empty_string("Trigger", NAME_LEN)); + fields.push_back(new Item_empty_string("sql_mode", trg_sql_mode_str.length)); + + { + /* + NOTE: SQL statement field must be not less than 1024 in order not to + confuse old clients. + */ + + Item_empty_string *stmt_fld= + new Item_empty_string("SQL Original Statement", + max(trg_sql_original_stmt.length, 1024)); + + stmt_fld->maybe_null= TRUE; + + fields.push_back(stmt_fld); + } + + fields.push_back(new Item_empty_string("character_set_client", + MY_CS_NAME_SIZE)); + + fields.push_back(new Item_empty_string("collation_connection", + MY_CS_NAME_SIZE)); + + fields.push_back(new Item_empty_string("Database Collation", + MY_CS_NAME_SIZE)); + + if (p->send_fields(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + return TRUE; + + /* Send data. */ + + p->prepare_for_resend(); + + p->store(trg_name.str, + trg_name.length, + system_charset_info); + + p->store(trg_sql_mode_str.str, + trg_sql_mode_str.length, + system_charset_info); + + p->store(trg_sql_original_stmt.str, + trg_sql_original_stmt.length, + &my_charset_bin); + + p->store(trg_client_cs_name.str, + trg_client_cs_name.length, + system_charset_info); + + p->store(trg_connection_cl_name.str, + trg_connection_cl_name.length, + system_charset_info); + + p->store(trg_db_cl_name.str, + trg_db_cl_name.length, + system_charset_info); + + ret_code= p->write(); + + if (!ret_code) + send_eof(thd); + + return ret_code != 0; +} + + +/** + Read TRN and TRG files to obtain base table name for the specified + trigger name and construct TABE_LIST object for the base table. + + @param thd Thread context. + @param trg_name Trigger name. + + @return TABLE_LIST object corresponding to the base table. + + TODO: This function is a copy&paste from add_table_to_list() and + sp_add_to_query_tables(). The problem is that in order to be compatible + with Stored Programs (Prepared Statements), we should not touch thd->lex. + The "source" functions also add created TABLE_LIST object to the + thd->lex->query_tables. + + The plan to eliminate this copy&paste is to: + + - get rid of sp_add_to_query_tables() and use Lex::add_table_to_list(). + Only add_table_to_list() must be used to add tables from the parser + into Lex::query_tables list. + + - do not update Lex::query_tables in add_table_to_list(). +*/ + +static TABLE_LIST *get_trigger_table_impl( + THD *thd, + const sp_name *trg_name) +{ + char trn_path_buff[FN_REFLEN]; + + LEX_STRING trn_path= { trn_path_buff, 0 }; + LEX_STRING tbl_name; + + build_trn_path(thd, trg_name, &trn_path); + + if (check_trn_exists(&trn_path)) + { + my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); + return NULL; + } + + if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name)) + return NULL; + + /* We need to reset statement table list to be PS/SP friendly. */ + + TABLE_LIST *table; + + if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST)))) + { + my_error(ER_OUTOFMEMORY, MYF(0), sizeof(TABLE_LIST)); + return NULL; + } + + table->db_length= trg_name->m_db.length; + table->db= thd->strmake(trg_name->m_db.str, trg_name->m_db.length); + + table->table_name_length= tbl_name.length; + table->table_name= thd->strmake(tbl_name.str, tbl_name.length); + + table->alias= thd->strmake(tbl_name.str, tbl_name.length); + + table->lock_type= TL_IGNORE; + table->cacheable_table= 0; + + return table; +} + +/** + Read TRN and TRG files to obtain base table name for the specified + trigger name and construct TABE_LIST object for the base table. Acquire + LOCK_open when doing this. + + @param thd Thread context. + @param trg_name Trigger name. + + @return TABLE_LIST object corresponding to the base table. +*/ + +static TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) +{ + /* Acquire LOCK_open (stop the server). */ + + pthread_mutex_lock(&LOCK_open); + + /* + Load base table name from the TRN-file and create TABLE_LIST object. + */ + + TABLE_LIST *lst= get_trigger_table_impl(thd, trg_name); + + /* Release LOCK_open (continue the server). */ + + pthread_mutex_unlock(&LOCK_open); + + /* That's it. */ + + return lst; +} + + +/** + SHOW CREATE TRIGGER high-level implementation. + + @param thd Thread context. + @param trg_name Trigger name. + + @return Operation status + @retval TRUE Error. + @retval FALSE Success. +*/ + +bool show_create_trigger(THD *thd, const sp_name *trg_name) +{ + TABLE_LIST *lst= get_trigger_table(thd, trg_name); + + /* + Open the table by name in order to load Table_triggers_list object. + + NOTE: there is race condition here -- the table can be dropped after + LOCK_open is released. It will be fixed later by introducing + acquire-shared-table-name-lock functionality. + */ + + uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ + + if (open_tables(thd, &lst, &num_tables, 0)) + { + my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0), + (const char *) trg_name->m_db.str, + (const char *) lst->table_name); + + return TRUE; + + /* Perform closing actions and return error status. */ + } + + DBUG_ASSERT(num_tables == 1); + + Table_triggers_list *triggers= lst->table->triggers; + + if (!triggers) + { + my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); + return TRUE; + } + + int trigger_idx= triggers->find_trigger_by_name(&trg_name->m_name); + + if (trigger_idx < 0) + { + my_error(ER_TRG_CORRUPTED_FILE, MYF(0), + (const char *) trg_name->m_db.str, + (const char *) lst->table_name); + + return TRUE; + } + + return show_create_trigger_impl(thd, triggers, trigger_idx); + + /* + NOTE: if show_create_trigger_impl() failed, that means we could not + send data to the client. In this case we simply raise the error + status and client connection will be closed. + */ +} diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 7c28dff850a..06dd0dded43 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -20,6 +20,149 @@ #include "sql_trigger.h" #include "parse_file.h" +/*************************************************************************/ + +template <class T> +inline T *alloc_type(MEM_ROOT *m) +{ + return (T *) alloc_root(m, sizeof (T)); +} + +/* + NOTE: Since alloc_type() is declared as inline, alloc_root() calls should + be inlined by the compiler. So, implementation of alloc_root() is not + needed. However, let's put the implementation in object file just in case + of stupid MS or other old compilers. +*/ + +template LEX_STRING *alloc_type<LEX_STRING>(MEM_ROOT *m); +template ulonglong *alloc_type<ulonglong>(MEM_ROOT *m); + +inline LEX_STRING *alloc_lex_string(MEM_ROOT *m) +{ + return alloc_type<LEX_STRING>(m); +} + +/*************************************************************************/ +/** + Trigger_creation_ctx -- creation context of triggers. +*/ + +class Trigger_creation_ctx : public Stored_program_creation_ctx, + public Sql_alloc +{ +public: + static Trigger_creation_ctx *create(THD *thd, + const char *db_name, + const char *table_name, + const LEX_STRING *client_cs_name, + const LEX_STRING *connection_cl_name, + const LEX_STRING *db_cl_name); + +public: + virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) + { + return new (mem_root) Trigger_creation_ctx(m_client_cs, + m_connection_cl, + m_db_cl); + } + +protected: + virtual Object_creation_ctx *create_backup_ctx(THD *thd) const + { + return new Trigger_creation_ctx(thd); + } + +private: + Trigger_creation_ctx(THD *thd) + :Stored_program_creation_ctx(thd) + { } + + Trigger_creation_ctx(CHARSET_INFO *client_cs, + CHARSET_INFO *connection_cl, + CHARSET_INFO *db_cl) + :Stored_program_creation_ctx(client_cs, connection_cl, db_cl) + { } +}; + +/************************************************************************** + Trigger_creation_ctx implementation. +**************************************************************************/ + +Trigger_creation_ctx * +Trigger_creation_ctx::create(THD *thd, + const char *db_name, + const char *table_name, + const LEX_STRING *client_cs_name, + const LEX_STRING *connection_cl_name, + const LEX_STRING *db_cl_name) +{ + CHARSET_INFO *client_cs; + CHARSET_INFO *connection_cl; + CHARSET_INFO *db_cl; + + bool invalid_creation_ctx= FALSE; + + if (resolve_charset(client_cs_name->str, + thd->variables.character_set_client, + &client_cs)) + { + sql_print_warning("Trigger for table '%s'.'%s': " + "invalid character_set_client value (%s).", + (const char *) db_name, + (const char *) table_name, + (const char *) client_cs_name->str); + + invalid_creation_ctx= TRUE; + } + + if (resolve_collation(connection_cl_name->str, + thd->variables.collation_connection, + &connection_cl)) + { + sql_print_warning("Trigger for table '%s'.'%s': " + "invalid collation_connection value (%s).", + (const char *) db_name, + (const char *) table_name, + (const char *) connection_cl_name->str); + + invalid_creation_ctx= TRUE; + } + + if (resolve_collation(db_cl_name->str, NULL, &db_cl)) + { + sql_print_warning("Trigger for table '%s'.'%s': " + "invalid database_collation value (%s).", + (const char *) db_name, + (const char *) table_name, + (const char *) db_cl_name->str); + + invalid_creation_ctx= TRUE; + } + + if (invalid_creation_ctx) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRG_INVALID_CREATION_CTX, + ER(ER_TRG_INVALID_CREATION_CTX), + (const char *) db_name, + (const char *) table_name); + } + + /* + If we failed to resolve the database collation, load the default one + from the disk. + */ + + if (!db_cl) + db_cl= get_default_db_collation(thd, db_name); + + return new Trigger_creation_ctx(client_cs, connection_cl, db_cl); +} + +/*************************************************************************/ + static const LEX_STRING triggers_file_type= { C_STRING_WITH_LEN("TRIGGERS") }; @@ -48,6 +191,21 @@ static File_option triggers_file_parameters[]= my_offsetof(class Table_triggers_list, definers_list), FILE_OPTIONS_STRLIST }, + { + { C_STRING_WITH_LEN("client_cs_names") }, + my_offsetof(class Table_triggers_list, client_cs_names), + FILE_OPTIONS_STRLIST + }, + { + { C_STRING_WITH_LEN("connection_cl_names") }, + my_offsetof(class Table_triggers_list, connection_cl_names), + FILE_OPTIONS_STRLIST + }, + { + { C_STRING_WITH_LEN("db_cl_names") }, + my_offsetof(class Table_triggers_list, db_cl_names), + FILE_OPTIONS_STRLIST + }, { { 0, 0 }, 0, FILE_OPTIONS_STRING } }; @@ -64,7 +222,7 @@ File_option sql_modes_parameters= .trg file. */ -static const int TRG_NUM_REQUIRED_PARAMETERS= 4; +static const int TRG_NUM_REQUIRED_PARAMETERS= 6; /* Structure representing contents of .TRN file which are used to support @@ -118,6 +276,7 @@ public: MEM_ROOT *mem_root, char *end); }; + class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook { public: @@ -359,6 +518,9 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, LEX_STRING *trg_definer; Item_trigger_field *trg_field; struct st_trigname trigname; + LEX_STRING *trg_client_cs_name; + LEX_STRING *trg_connection_cl_name; + LEX_STRING *trg_db_cl_name; /* Trigger must be in the same schema as target table. */ @@ -489,16 +651,26 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, QQ: Hmm... probably we should not care about setting up active thread mem_root too. */ - if (!(trg_def= (LEX_STRING *)alloc_root(&table->mem_root, - sizeof(LEX_STRING))) || + if (!(trg_def= alloc_lex_string(&table->mem_root)) || definitions_list.push_back(trg_def, &table->mem_root) || - !(trg_sql_mode= (ulonglong*)alloc_root(&table->mem_root, - sizeof(ulonglong))) || + + !(trg_sql_mode= alloc_type<ulonglong>(&table->mem_root)) || definition_modes_list.push_back(trg_sql_mode, &table->mem_root) || - !(trg_definer= (LEX_STRING*) alloc_root(&table->mem_root, - sizeof(LEX_STRING))) || - definers_list.push_back(trg_definer, &table->mem_root)) + + !(trg_definer= alloc_lex_string(&table->mem_root)) || + definers_list.push_back(trg_definer, &table->mem_root) || + + !(trg_client_cs_name= alloc_lex_string(&table->mem_root)) || + client_cs_names.push_back(trg_client_cs_name, &table->mem_root) || + + !(trg_connection_cl_name= alloc_lex_string(&table->mem_root)) || + connection_cl_names.push_back(trg_connection_cl_name, &table->mem_root) || + + !(trg_db_cl_name= alloc_lex_string(&table->mem_root)) || + db_cl_names.push_back(trg_db_cl_name, &table->mem_root)) + { goto err_with_cleanup; + } *trg_sql_mode= thd->variables.sql_mode; @@ -541,6 +713,21 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, } /* + Fill character set information: + - client character set contains charset info only; + - connection collation contains pair {character set, collation}; + - database collation contains pair {character set, collation}; + */ + + lex_string_set(trg_client_cs_name, thd->charset()->csname); + + lex_string_set(trg_connection_cl_name, + thd->variables.collation_connection->name); + + lex_string_set(trg_db_cl_name, + get_default_db_collation(thd, tables->db)->name); + + /* Create well-formed trigger definition query. Original query is not appropriated, because definer-clause can be not truncated. */ @@ -674,14 +861,20 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db, bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, String *stmt_query) { - LEX *lex= thd->lex; + const char *sp_name= thd->lex->spname->m_name.str; // alias + LEX_STRING *name; - List_iterator_fast<LEX_STRING> it_name(names_list); - List_iterator<LEX_STRING> it_def(definitions_list); - List_iterator<ulonglong> it_mod(definition_modes_list); - List_iterator<LEX_STRING> it_definer(definers_list); char path[FN_REFLEN]; + List_iterator_fast<LEX_STRING> it_name(names_list); + + List_iterator<ulonglong> it_mod(definition_modes_list); + List_iterator<LEX_STRING> it_def(definitions_list); + List_iterator<LEX_STRING> it_definer(definers_list); + List_iterator<LEX_STRING> it_client_cs_name(client_cs_names); + List_iterator<LEX_STRING> it_connection_cl_name(connection_cl_names); + List_iterator<LEX_STRING> it_db_cl_name(db_cl_names); + stmt_query->append(thd->query, thd->query_length); while ((name= it_name++)) @@ -689,9 +882,11 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, it_def++; it_mod++; it_definer++; + it_client_cs_name++; + it_connection_cl_name++; + it_db_cl_name++; - if (my_strcasecmp(table_alias_charset, lex->spname->m_name.str, - name->str) == 0) + if (my_strcasecmp(table_alias_charset, sp_name, name->str) == 0) { /* Again we don't care much about other things required for @@ -700,6 +895,9 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, it_def.remove(); it_mod.remove(); it_definer.remove(); + it_client_cs_name.remove(); + it_connection_cl_name.remove(); + it_db_cl_name.remove(); if (definitions_list.is_empty()) { @@ -718,7 +916,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, return 1; } - if (rm_trigname_file(path, tables->db, lex->spname->m_name.str)) + if (rm_trigname_file(path, tables->db, sp_name)) return 1; return 0; } @@ -857,9 +1055,13 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, we should initialize the list for safety: - sql_modes; - definers; + - character sets (client, connection, database); */ triggers->definition_modes_list.empty(); triggers->definers_list.empty(); + triggers->client_cs_names.empty(); + triggers->connection_cl_names.empty(); + triggers->db_cl_names.empty(); if (parser->parse((uchar*)triggers, &table->mem_root, triggers_file_parameters, @@ -880,8 +1082,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, We use one mode (current) for all triggers, because we have not information about mode in old format. */ - if (!(trg_sql_mode= (ulonglong*)alloc_root(&table->mem_root, - sizeof(ulonglong)))) + if (!(trg_sql_mode= alloc_type<ulonglong>(&table->mem_root))) { DBUG_RETURN(1); // EOM } @@ -910,8 +1111,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, LEX_STRING *trg_definer; - if (! (trg_definer= (LEX_STRING*)alloc_root(&table->mem_root, - sizeof(LEX_STRING)))) + if (!(trg_definer= alloc_lex_string(&table->mem_root))) DBUG_RETURN(1); // EOM trg_definer->str= (char*) ""; @@ -929,10 +1129,85 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, it.rewind(); } + if (!triggers->definitions_list.is_empty() && + (triggers->client_cs_names.is_empty() || + triggers->connection_cl_names.is_empty() || + triggers->db_cl_names.is_empty())) + { + /* + It is old file format => we should fill lists of character sets. + */ + + LEX_STRING *trg_client_cs_name; + LEX_STRING *trg_connection_cl_name; + LEX_STRING *trg_db_cl_name; + + if (!triggers->client_cs_names.is_empty() || + !triggers->connection_cl_names.is_empty() || + !triggers->db_cl_names.is_empty()) + { + my_error(ER_TRG_CORRUPTED_FILE, MYF(0), + (const char *) db, + (const char *) table_name); + + DBUG_RETURN(1); // EOM + } + + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRG_NO_CREATION_CTX, + ER(ER_TRG_NO_CREATION_CTX), + (const char*) db, + (const char*) table_name); + + if (!(trg_client_cs_name= alloc_lex_string(&table->mem_root)) || + !(trg_connection_cl_name= alloc_lex_string(&table->mem_root)) || + !(trg_db_cl_name= alloc_lex_string(&table->mem_root))) + { + DBUG_RETURN(1); // EOM + } + + /* + Backward compatibility: assume that the query is in the current + character set. + */ + + lex_string_set(trg_client_cs_name, + thd->variables.character_set_client->csname); + + lex_string_set(trg_connection_cl_name, + thd->variables.collation_connection->name); + + lex_string_set(trg_db_cl_name, + thd->variables.collation_database->name); + + while (it++) + { + if (triggers->client_cs_names.push_back(trg_client_cs_name, + &table->mem_root) || + + triggers->connection_cl_names.push_back(trg_connection_cl_name, + &table->mem_root) || + + triggers->db_cl_names.push_back(trg_db_cl_name, + &table->mem_root)) + { + DBUG_RETURN(1); // EOM + } + } + + it.rewind(); + } + DBUG_ASSERT(triggers->definition_modes_list.elements == triggers->definitions_list.elements); DBUG_ASSERT(triggers->definers_list.elements == triggers->definitions_list.elements); + DBUG_ASSERT(triggers->client_cs_names.elements == + triggers->definitions_list.elements); + DBUG_ASSERT(triggers->connection_cl_names.elements == + triggers->definitions_list.elements); + DBUG_ASSERT(triggers->db_cl_names.elements == + triggers->definitions_list.elements); table->triggers= triggers; @@ -956,6 +1231,9 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, List_iterator_fast<ulonglong> itm(triggers->definition_modes_list); List_iterator_fast<LEX_STRING> it_definer(triggers->definers_list); + List_iterator_fast<LEX_STRING> it_client_cs_name(triggers->client_cs_names); + List_iterator_fast<LEX_STRING> it_connection_cl_name(triggers->connection_cl_names); + List_iterator_fast<LEX_STRING> it_db_cl_name(triggers->db_cl_names); LEX *old_lex= thd->lex, lex; sp_rcontext *save_spcont= thd->spcont; ulong save_sql_mode= thd->variables.sql_mode; @@ -974,10 +1252,19 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, thd->variables.sql_mode= (ulong)*trg_sql_mode; Lex_input_stream lip(thd, trg_create_str->str, trg_create_str->length); + + Trigger_creation_ctx *creation_ctx= + Trigger_creation_ctx::create(thd, + db, + table_name, + it_client_cs_name++, + it_connection_cl_name++, + it_db_cl_name++); + lex_start(thd); - thd->spcont= 0; + thd->spcont= NULL; - if (parse_sql(thd, &lip)) + if (parse_sql(thd, &lip, creation_ctx)) { /* Currently sphead is always deleted in case of a parse error */ DBUG_ASSERT(lex.sphead == 0); @@ -986,8 +1273,11 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode); - triggers->bodies[lex.trg_chistics.event] - [lex.trg_chistics.action_time]= lex.sphead; + int event= lex.trg_chistics.event; + int action_time= lex.trg_chistics.action_time; + + lex.sphead->set_creation_ctx(creation_ctx); + triggers->bodies[event][action_time]= lex.sphead; if (!trg_definer->length) { @@ -1023,8 +1313,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, &table->mem_root)) goto err_with_lex_cleanup; - if (!(on_table_name= (LEX_STRING*) alloc_root(&table->mem_root, - sizeof(LEX_STRING)))) + if (!(on_table_name= alloc_lex_string(&table->mem_root))) goto err_with_lex_cleanup; on_table_name->str= (char*) lex.raw_trg_on_table_name_begin; @@ -1070,7 +1359,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, trg_field; trg_field= trg_field->next_trg_field) { - trg_field->setup_field(thd, table, + trg_field->setup_field(thd, table, &triggers->subject_table_grants[lex.trg_chistics.event] [lex.trg_chistics.action_time]); } @@ -1132,14 +1421,20 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, LEX_STRING *trigger_name, LEX_STRING *trigger_stmt, ulong *sql_mode, - LEX_STRING *definer) + LEX_STRING *definer, + LEX_STRING *client_cs_name, + LEX_STRING *connection_cl_name, + LEX_STRING *db_cl_name) { sp_head *body; DBUG_ENTER("get_trigger_info"); if ((body= bodies[event][time_type])) { + Stored_program_creation_ctx *creation_ctx= + bodies[event][time_type]->get_creation_ctx(); + *trigger_name= body->m_name; - *trigger_stmt= body->m_body; + *trigger_stmt= body->m_body_utf8; *sql_mode= body->m_sql_mode; if (body->m_chistics->suid == SP_IS_NOT_SUID) @@ -1153,12 +1448,74 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, body->m_definer_host.str, NullS) - definer->str; } + lex_string_set(client_cs_name, + creation_ctx->get_client_cs()->csname); + + lex_string_set(connection_cl_name, + creation_ctx->get_connection_cl()->name); + + lex_string_set(db_cl_name, + creation_ctx->get_db_cl()->name); + DBUG_RETURN(0); } DBUG_RETURN(1); } +void Table_triggers_list::get_trigger_info(THD *thd, + int trigger_idx, + LEX_STRING *trigger_name, + ulonglong *sql_mode, + LEX_STRING *sql_original_stmt, + LEX_STRING *client_cs_name, + LEX_STRING *connection_cl_name, + LEX_STRING *db_cl_name) +{ + List_iterator_fast<LEX_STRING> it_trigger_name(names_list); + List_iterator_fast<ulonglong> it_sql_mode(definition_modes_list); + List_iterator_fast<LEX_STRING> it_sql_orig_stmt(definitions_list); + List_iterator_fast<LEX_STRING> it_client_cs_name(client_cs_names); + List_iterator_fast<LEX_STRING> it_connection_cl_name(connection_cl_names); + List_iterator_fast<LEX_STRING> it_db_cl_name(db_cl_names); + + for (int i = 0; i < trigger_idx; ++i) + { + it_trigger_name.next_fast(); + it_sql_mode.next_fast(); + it_sql_orig_stmt.next_fast(); + + it_client_cs_name.next_fast(); + it_connection_cl_name.next_fast(); + it_db_cl_name.next_fast(); + } + + *trigger_name= *(it_trigger_name++); + *sql_mode= *(it_sql_mode++); + *sql_original_stmt= *(it_sql_orig_stmt++); + + *client_cs_name= *(it_client_cs_name++); + *connection_cl_name= *(it_connection_cl_name++); + *db_cl_name= *(it_db_cl_name++); +} + + +int Table_triggers_list::find_trigger_by_name(const LEX_STRING *trg_name) +{ + List_iterator_fast<LEX_STRING> it(names_list); + + for (int i = 0; ; ++i) + { + LEX_STRING *cur_name= it++; + + if (!cur_name) + return -1; + + if (strcmp(cur_name->str, trg_name->str) == 0) + return i; + } +} + /** Find trigger's table from trigger identifier and add it to the statement table list. @@ -1177,7 +1534,7 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, */ bool add_table_for_trigger(THD *thd, - sp_name *trg_name, + const sp_name *trg_name, bool if_exists, TABLE_LIST **table) { diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index c305efb5029..bfdbae12bdc 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -83,6 +83,14 @@ public: List<LEX_STRING> definers_list; + /* Character set context, used for parsing and executing triggers. */ + + List<LEX_STRING> client_cs_names; + List<LEX_STRING> connection_cl_names; + List<LEX_STRING> db_cl_names; + + /* End of character ser context. */ + Table_triggers_list(TABLE *table_arg): record1_field(0), trigger_table(table_arg) { @@ -97,11 +105,26 @@ public: bool process_triggers(THD *thd, trg_event_type event, trg_action_time_type time_type, bool old_row_is_record1); + bool get_trigger_info(THD *thd, trg_event_type event, trg_action_time_type time_type, LEX_STRING *trigger_name, LEX_STRING *trigger_stmt, ulong *sql_mode, - LEX_STRING *definer); + LEX_STRING *definer, + LEX_STRING *client_cs_name, + LEX_STRING *connection_cl_name, + LEX_STRING *db_cl_name); + + void get_trigger_info(THD *thd, + int trigger_idx, + LEX_STRING *trigger_name, + ulonglong *sql_mode, + LEX_STRING *sql_original_stmt, + LEX_STRING *client_cs_name, + LEX_STRING *connection_cl_name, + LEX_STRING *db_cl_name); + + int find_trigger_by_name(const LEX_STRING *trigger_name); static bool check_n_load(THD *thd, const char *db, const char *table_name, TABLE *table, bool names_only); @@ -144,7 +167,7 @@ extern const LEX_STRING trg_action_time_type_names[]; extern const LEX_STRING trg_event_type_names[]; bool add_table_for_trigger(THD *thd, - sp_name *trg_name, + const sp_name *trg_name, bool continue_if_not_exist, TABLE_LIST **table); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 7021a8cba3b..ce311f5d4a2 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -610,8 +610,8 @@ err: /* index of revision number in following table */ static const int revision_number_position= 8; -/* index of last required parameter for making view */ -static const int required_view_parameters= 10; +/* number of required parameters for making view */ +static const int required_view_parameters= 16; /* number of backups */ static const int num_view_backups= 3; @@ -623,7 +623,7 @@ static const int num_view_backups= 3; */ static File_option view_parameters[]= {{{ C_STRING_WITH_LEN("query")}, - my_offsetof(TABLE_LIST, query), + my_offsetof(TABLE_LIST, select_stmt), FILE_OPTIONS_ESTRING}, {{ C_STRING_WITH_LEN("md5")}, my_offsetof(TABLE_LIST, md5), @@ -658,6 +658,15 @@ static File_option view_parameters[]= {{ C_STRING_WITH_LEN("source")}, my_offsetof(TABLE_LIST, source), FILE_OPTIONS_ESTRING}, + {{(char*) STRING_WITH_LEN("client_cs_name")}, + my_offsetof(TABLE_LIST, view_client_cs_name), + FILE_OPTIONS_STRING}, + {{(char*) STRING_WITH_LEN("connection_cl_name")}, + my_offsetof(TABLE_LIST, view_connection_cl_name), + FILE_OPTIONS_STRING}, + {{(char*) STRING_WITH_LEN("view_body_utf8")}, + my_offsetof(TABLE_LIST, view_body_utf8), + FILE_OPTIONS_STRING}, {{NullS, 0}, 0, FILE_OPTIONS_STRING} }; @@ -685,7 +694,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, { LEX *lex= thd->lex; char buff[4096]; - String str(buff,(uint32) sizeof(buff), system_charset_info); + String view_query(buff, sizeof (buff), thd->charset()); char md5[MD5_BUFF_LENGTH]; bool can_be_merged; char dir_buff[FN_REFLEN], path_buff[FN_REFLEN]; @@ -694,18 +703,18 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, DBUG_ENTER("mysql_register_view"); /* print query */ - str.length(0); + view_query.length(0); { ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES; thd->variables.sql_mode&= ~MODE_ANSI_QUOTES; - lex->unit.print(&str); + lex->unit.print(&view_query); thd->variables.sql_mode|= sql_mode; } - DBUG_PRINT("info", ("View: %s", str.ptr())); + DBUG_PRINT("info", ("View: %s", view_query.ptr())); /* fill structure */ - view->query.str= str.c_ptr_safe(); - view->query.length= str.length(); + view->select_stmt.str= view_query.c_ptr_safe(); + view->select_stmt.length= view_query.length(); view->source.str= (char*) thd->lex->create_view_select_start; view->source.length= (thd->lex->create_view_select_end @@ -826,6 +835,23 @@ loop_out: } } + /* Initialize view creation context from the environment. */ + + view->view_creation_ctx= View_creation_ctx::create(thd); + + /* + Set LEX_STRING attributes in view-structure for parser to create + frm-file. + */ + + lex_string_set(&view->view_client_cs_name, + view->view_creation_ctx->get_client_cs()->csname); + + lex_string_set(&view->view_connection_cl_name, + view->view_creation_ctx->get_connection_cl()->name); + + view->view_body_utf8= lex->view_body_utf8; + /* Check that table of main select do not used in subqueries. @@ -862,8 +888,8 @@ loop_out: } DBUG_RETURN(0); err: - view->query.str= NULL; - view->query.length= 0; + view->select_stmt.str= NULL; + view->select_stmt.length= 0; view->md5.str= NULL; view->md5.length= 0; DBUG_RETURN(error); @@ -892,9 +918,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, LEX *old_lex, *lex; Query_arena *arena, backup; TABLE_LIST *top_view= table->top_table(); - bool res; + bool parse_status; bool result, view_is_mergeable; TABLE_LIST *view_main_select_tables; + DBUG_ENTER("mysql_make_view"); DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name)); @@ -995,6 +1022,14 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, /*TODO: md5 test here and warning if it is differ */ /* + Initialize view definition context by character set names loaded from + the view definition file. Use UTF8 character set if view definition + file is of old version and does not contain the character set names. + */ + + table->view_creation_ctx= View_creation_ctx::create(thd, table); + + /* TODO: TABLE mem root should be used here when VIEW will be stored in TABLE cache @@ -1003,12 +1038,15 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local; { - Lex_input_stream lip(thd, table->query.str, table->query.length); + Lex_input_stream lip(thd, + table->select_stmt.str, + table->select_stmt.length); + lex_start(thd); view_select= &lex->select_lex; view_select->select_number= ++thd->select_number; - ulong save_mode= thd->variables.sql_mode; + ulong saved_mode= thd->variables.sql_mode; /* switch off modes which can prevent normal parsing of VIEW - MODE_REAL_AS_FLOAT affect only CREATE TABLE parsing + MODE_PIPES_AS_CONCAT affect expression parsing @@ -1035,18 +1073,20 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, */ thd->variables.sql_mode&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); - CHARSET_INFO *save_cs= thd->variables.character_set_client; - thd->variables.character_set_client= system_charset_info; - res= parse_sql(thd, &lip); + + /* Parse the query. */ + + parse_status= parse_sql(thd, &lip, table->view_creation_ctx); + + /* Restore environment. */ if ((old_lex->sql_command == SQLCOM_SHOW_FIELDS) || (old_lex->sql_command == SQLCOM_SHOW_CREATE)) lex->sql_command= old_lex->sql_command; - thd->variables.character_set_client= save_cs; - thd->variables.sql_mode= save_mode; + thd->variables.sql_mode= saved_mode; } - if (!res) + if (!parse_status) { TABLE_LIST *view_tables= lex->query_tables; TABLE_LIST *view_tables_tail= 0; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 97e35d8c6ae..81dd8aebf65 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1089,7 +1089,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <lex_str> IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text - UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal + IDENT_sys TEXT_STRING_sys TEXT_STRING_literal NCHAR_STRING opt_component key_cache_name sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty @@ -1205,6 +1205,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); collation_name collation_name_or_default opt_load_data_charset + UNDERSCORE_CHARSET %type <variable> internal_variable_name @@ -1716,21 +1717,24 @@ event_tail: YYTHD->client_capabilities is set back to original value */ { - Lex->create_info.options= $2; + THD *thd= YYTHD; + LEX *lex=Lex; - if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) + lex->create_info.options= $2; + + if (!(lex->event_parse_data= Event_parse_data::new_instance(thd))) MYSQL_YYABORT; - Lex->event_parse_data->identifier= $3; + lex->event_parse_data->identifier= $3; /* We have to turn of CLIENT_MULTI_QUERIES while parsing a stored procedure, otherwise yylex will chop it into pieces at each ';'. */ - $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + $<ulong_num>$= thd->client_capabilities & CLIENT_MULTI_QUERIES; + thd->client_capabilities &= (~CLIENT_MULTI_QUERIES); - Lex->sql_command= SQLCOM_CREATE_EVENT; + lex->sql_command= SQLCOM_CREATE_EVENT; /* We need that for disallowing subqueries */ } ON SCHEDULE_SYM ev_schedule_time @@ -1863,16 +1867,16 @@ ev_sql_stmt: if (!(lex->sphead= new sp_head())) MYSQL_YYABORT; - lex->sphead->reset_thd_mem_root(YYTHD); + lex->sphead->reset_thd_mem_root(thd); lex->sphead->init(lex); - lex->sphead->init_sp_name(YYTHD, Lex->event_parse_data->identifier); + lex->sphead->init_sp_name(thd, lex->event_parse_data->identifier); lex->sphead->m_type= TYPE_ENUM_PROCEDURE; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_ptr()); + lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); } ev_sql_stmt_inner { @@ -1880,7 +1884,7 @@ ev_sql_stmt: LEX *lex= thd->lex; /* return back to the original memory root ASAP */ - lex->sphead->init_strings(thd, lex); + lex->sphead->set_stmt_end(thd); lex->sphead->restore_thd_mem_root(thd); lex->sp_chistics.suid= SP_IS_SUID; //always the definer! @@ -1946,7 +1950,7 @@ sp_name: MYSQL_YYABORT; $$= new sp_name(db, $1, false); if ($$) - $$->init_qname(YYTHD); + $$->init_qname(thd); } ; @@ -2066,7 +2070,7 @@ create_function_tail: Lex_input_stream *lip= thd->m_lip; lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_tok_start()); + lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); } sp_proc_stmt { @@ -2078,7 +2082,7 @@ create_function_tail: MYSQL_YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; - sp->init_strings(thd, lex); + sp->set_stmt_end(thd); if (!(sp->m_flags & sp_head::HAS_RETURN)) { my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str); @@ -3604,14 +3608,20 @@ create2: create3 {} | LIKE table_ident { - Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; - if (!Lex->select_lex.add_table_to_list(YYTHD, $2, NULL, 0, TL_READ)) + THD *thd= YYTHD; + LEX *lex= thd->lex; + + lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; + if (!lex->select_lex.add_table_to_list(thd, $2, NULL, 0, TL_READ)) MYSQL_YYABORT; } | '(' LIKE table_ident ')' { - Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; - if (!Lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0, TL_READ)) + THD *thd= YYTHD; + LEX *lex= thd->lex; + + lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0, TL_READ)) MYSQL_YYABORT; } ; @@ -8669,7 +8679,7 @@ show_param: { Lex->create_info.db_type= NULL; } | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where { - LEX *lex= Lex; + LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_FIELDS; if ($5) $4->change_db($5); @@ -8867,6 +8877,12 @@ show_param: lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; lex->spname= $3; } + | CREATE TRIGGER_SYM sp_name + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER; + lex->spname= $3; + } | PROCEDURE STATUS_SYM wild_and_where { LEX *lex= Lex; @@ -9307,7 +9323,9 @@ text_literal: | NCHAR_STRING { $$= new Item_string($1.str,$1.length,national_charset_info); } | UNDERSCORE_CHARSET TEXT_STRING - { $$ = new Item_string($2.str,$2.length,Lex->underscore_charset); } + { + $$ = new Item_string($2.str, $2.length, $1); + } | text_literal TEXT_STRING_literal { ((Item_string*) $1)->append($2.str,$2.length); } ; @@ -9394,7 +9412,7 @@ literal: (String*) 0; $$= new Item_string(str ? str->ptr() : "", str ? str->length() : 0, - Lex->underscore_charset); + $1); } | UNDERSCORE_CHARSET BIN_NUM { @@ -9669,6 +9687,7 @@ IDENT_sys: | IDENT_QUOTED { THD *thd= YYTHD; + if (thd->charset_is_system_charset) { CHARSET_INFO *cs= system_charset_info; @@ -9694,6 +9713,7 @@ TEXT_STRING_sys: TEXT_STRING { THD *thd= YYTHD; + if (thd->charset_is_system_charset) $$= $1; else @@ -9706,6 +9726,7 @@ TEXT_STRING_literal: TEXT_STRING { THD *thd= YYTHD; + if (thd->charset_is_collation_connection) $$= $1; else @@ -9719,6 +9740,7 @@ TEXT_STRING_filesystem: TEXT_STRING { THD *thd= YYTHD; + if (thd->charset_is_character_set_filesystem) $$= $1; else @@ -10408,7 +10430,8 @@ option_value: internal_variable_name: ident { - LEX *lex= Lex; + THD *thd= YYTHD; + LEX *lex= thd->lex; sp_pcontext *spc= lex->spcont; sp_variable_t *spv; @@ -10416,7 +10439,7 @@ internal_variable_name: if (!spc || !(spv = spc->find_variable(&$1))) { /* Not an SP local variable */ - sys_var *tmp=find_sys_var(YYTHD, $1.str, $1.length); + sys_var *tmp=find_sys_var(thd, $1.str, $1.length); if (!tmp) MYSQL_YYABORT; $$.var= tmp; @@ -11376,8 +11399,27 @@ view_tail: if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING)) MYSQL_YYABORT; } - view_list_opt AS view_select - {} + view_list_opt AS + { + THD *thd= YYTHD; + Lex_input_stream *lip= thd->m_lip; + + lip->body_utf8_start(thd, lip->get_cpp_ptr()); + } + view_select + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= thd->m_lip; + + lip->body_utf8_append(lip->get_cpp_ptr()); + + lex->view_body_utf8.str= thd->strmake(lip->get_body_utf8_str(), + lip->get_body_utf8_length()); + lex->view_body_utf8.length= lip->get_body_utf8_length(); + + trim_whitespace(&my_charset_utf8_general_ci, &lex->view_body_utf8); + } ; view_list_opt: @@ -11504,7 +11546,7 @@ trigger_tail: bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_ptr()); + lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); } sp_proc_stmt /* $16 */ { /* $17 */ @@ -11512,7 +11554,7 @@ trigger_tail: sp_head *sp= lex->sphead; lex->sql_command= SQLCOM_CREATE_TRIGGER; - sp->init_strings(YYTHD, lex); + sp->set_stmt_end(YYTHD); /* Restore flag if it was cleared above */ YYTHD->client_capabilities |= $<ulong_num>15; @@ -11607,14 +11649,14 @@ sp_tail: Lex_input_stream *lip= thd->m_lip; lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->set_body_begin_ptr(lip, lip->get_cpp_tok_start()); + lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); } sp_proc_stmt { LEX *lex= Lex; sp_head *sp= lex->sphead; - sp->init_strings(YYTHD, lex); + sp->set_stmt_end(YYTHD); lex->sql_command= SQLCOM_CREATE_PROCEDURE; /* Restore flag if it was cleared above diff --git a/sql/table.cc b/sql/table.cc index 45ca17afce4..254f0d5503c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -32,6 +32,127 @@ static void fix_type_pointers(const char ***array, TYPELIB *point_to_type, static uint find_field(Field **fields, uchar *record, uint start, uint length); +/************************************************************************** + Object_creation_ctx implementation. +**************************************************************************/ + +Object_creation_ctx *Object_creation_ctx::set_n_backup(THD *thd) +{ + Object_creation_ctx *backup_ctx= create_backup_ctx(thd); + + change_env(thd); + + return backup_ctx; +} + +void Object_creation_ctx::restore_env(THD *thd, Object_creation_ctx *backup_ctx) +{ + if (!backup_ctx) + return; + + backup_ctx->change_env(thd); + + delete backup_ctx; +} + +/************************************************************************** + Default_object_creation_ctx implementation. +**************************************************************************/ + +Default_object_creation_ctx::Default_object_creation_ctx(THD *thd) + : m_client_cs(thd->variables.character_set_client), + m_connection_cl(thd->variables.collation_connection) +{ } + +Default_object_creation_ctx::Default_object_creation_ctx( + CHARSET_INFO *client_cs, CHARSET_INFO *connection_cl) + : m_client_cs(client_cs), + m_connection_cl(connection_cl) +{ } + +Object_creation_ctx * +Default_object_creation_ctx::create_backup_ctx(THD *thd) +{ + return new Default_object_creation_ctx(thd); +} + +void Default_object_creation_ctx::change_env(THD *thd) const +{ + thd->variables.character_set_client= m_client_cs; + thd->variables.collation_connection= m_connection_cl; + + thd->update_charset(); +} + +/************************************************************************** + View_creation_ctx implementation. +**************************************************************************/ + +View_creation_ctx *View_creation_ctx::create(THD *thd) +{ + View_creation_ctx *ctx= new (thd->mem_root) View_creation_ctx(thd); + + return ctx; +} + +/*************************************************************************/ + +View_creation_ctx * View_creation_ctx::create(THD *thd, + st_table_list *view) +{ + View_creation_ctx *ctx= new (thd->mem_root) View_creation_ctx(thd); + + /* Throw a warning if there is NULL cs name. */ + + if (!view->view_client_cs_name.str || + !view->view_connection_cl_name.str) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_VIEW_NO_CREATION_CTX, + ER(ER_VIEW_NO_CREATION_CTX), + (const char *) view->db, + (const char *) view->table_name); + + ctx->m_client_cs= system_charset_info; + ctx->m_connection_cl= system_charset_info; + + return ctx; + } + + /* Resolve cs names. Throw a warning if there is unknown cs name. */ + + bool invalid_creation_ctx; + + invalid_creation_ctx= resolve_charset(view->view_client_cs_name.str, + system_charset_info, + &ctx->m_client_cs); + + invalid_creation_ctx= resolve_collation(view->view_connection_cl_name.str, + system_charset_info, + &ctx->m_connection_cl) || + invalid_creation_ctx; + + if (invalid_creation_ctx) + { + sql_print_warning("View '%s'.'%s': there is unknown charset/collation " + "names (client: '%s'; connection: '%s').", + (const char *) view->db, + (const char *) view->table_name, + (const char *) view->view_client_cs_name.str, + (const char *) view->view_connection_cl_name.str); + + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_VIEW_INVALID_CREATION_CTX, + ER(ER_VIEW_INVALID_CREATION_CTX), + (const char *) view->db, + (const char *) view->table_name); + } + + return ctx; +} + +/*************************************************************************/ + /* Get column name from column hash */ static uchar *get_field_name(Field **buff, size_t *length, @@ -42,7 +163,6 @@ static uchar *get_field_name(Field **buff, size_t *length, } - /* Returns pointer to '.frm' extension of the file name. @@ -2730,7 +2850,7 @@ void st_table_list::calc_md5(char *buffer) my_MD5_CTX context; uchar digest[16]; my_MD5Init(&context); - my_MD5Update(&context,(uchar *) query.str, query.length); + my_MD5Update(&context,(uchar *) select_stmt.str, select_stmt.length); my_MD5Final(digest, &context); sprintf((char *) buffer, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", diff --git a/sql/table.h b/sql/table.h index 90c07979512..4c98f5146ab 100644 --- a/sql/table.h +++ b/sql/table.h @@ -25,6 +25,29 @@ class partition_info; class COND_EQUAL; class Security_context; +/*************************************************************************/ + +/** + View_creation_ctx -- creation context of view objects. +*/ + +class View_creation_ctx : public Default_object_creation_ctx, + public Sql_alloc +{ +public: + static View_creation_ctx *create(THD *thd); + + static View_creation_ctx *create(THD *thd, + struct st_table_list *view); + +private: + View_creation_ctx(THD *thd) + : Default_object_creation_ctx(thd) + { } +}; + +/*************************************************************************/ + /* Order clause list element */ typedef struct st_order { @@ -863,7 +886,7 @@ typedef struct st_table_list st_table_list *next_leaf; Item *where; /* VIEW WHERE clause condition */ Item *check_option; /* WITH CHECK OPTION condition */ - LEX_STRING query; /* text of (CRETE/SELECT) statement */ + LEX_STRING select_stmt; /* text of (CREATE/SELECT) statement */ LEX_STRING md5; /* md5 of query text */ LEX_STRING source; /* source of CREATE VIEW */ LEX_STRING view_db; /* saved view database */ @@ -930,6 +953,32 @@ typedef struct st_table_list */ bool create; + + /* View creation context. */ + + View_creation_ctx *view_creation_ctx; + + /* + Attributes to save/load view creation context in/from frm-file. + + Ther are required only to be able to use existing parser to load + view-definition file. As soon as the parser parsed the file, view + creation context is initialized and the attributes become redundant. + + These attributes MUST NOT be used for any purposes but the parsing. + */ + + LEX_STRING view_client_cs_name; + LEX_STRING view_connection_cl_name; + + /* + View definition (SELECT-statement) in the UTF-form. + */ + + LEX_STRING view_body_utf8; + + /* End of view definition context. */ + enum enum_schema_table_state schema_table_state; void calc_md5(char *buffer); void set_underlying_merge(); |