5356 Dmitry Shulga 2013-01-22
Thhis is a refactoring of triggers support.
modified:
sql/sql_class.h
sql/sql_trigger.cc
sql/sql_trigger.h
5355 Venkata Sidagam 2013-01-10 [merge]
Bug #14553380 MYSQL C API LIBRARY EXITS AT NET_CLEAR AT NET_SERV.CC:223
Merging from 5.6 to trunk
modified:
libmysql/libmysql.c
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2013-01-07 12:52:53 +0000
+++ b/sql/sql_class.h 2013-01-22 09:24:23 +0000
@@ -4015,12 +4015,25 @@ my_eof(THD *thd)
#define reenable_binlog(A) (A)->variables.option_bits= tmp_disable_binlog__save_options;}
-
LEX_STRING *
make_lex_string_root(MEM_ROOT *mem_root,
LEX_STRING *lex_str, const char* str, uint length,
bool allocate_lex_string);
+inline LEX_STRING *lex_string_dup(MEM_ROOT *root,
+ const LEX_STRING *original)
+{
+ return make_lex_string_root(root, NULL, original->str, original->length,
+ true);
+}
+
+inline LEX_STRING *lex_string_copy(MEM_ROOT *root, LEX_STRING *dst,
+ const char *src, uint src_len)
+{
+ return make_lex_string_root(root, dst, src, src_len,
+ false);
+}
+
/*
Used to hold information about file and file structure in exchange
via non-DB file (...INTO OUTFILE..., ...LOAD DATA...)
=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc 2013-01-06 16:43:43 +0000
+++ b/sql/sql_trigger.cc 2013-01-22 09:24:23 +0000
@@ -177,66 +177,15 @@ Trigger_creation_ctx::create(THD *thd,
/*************************************************************************/
-static const LEX_STRING triggers_file_type=
- { C_STRING_WITH_LEN("TRIGGERS") };
-
const char * const TRG_EXT= ".TRG";
-/**
- Table of .TRG file field descriptors.
- We have here only one field now because in nearest future .TRG
- files will be merged into .FRM files (so we don't need something
- like md5 or created fields).
-*/
-static File_option triggers_file_parameters[]=
-{
- {
- { C_STRING_WITH_LEN("triggers") },
- my_offsetof(class Table_triggers_list, definitions_list),
- FILE_OPTIONS_STRLIST
- },
- {
- { C_STRING_WITH_LEN("sql_modes") },
- my_offsetof(class Table_triggers_list, definition_modes_list),
- FILE_OPTIONS_ULLLIST
- },
- {
- { C_STRING_WITH_LEN("definers") },
- 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 }
-};
-
File_option sql_modes_parameters=
{
{ C_STRING_WITH_LEN("sql_modes") },
- my_offsetof(class Table_triggers_list, definition_modes_list),
+ my_offsetof(class Triggers_definition_loader, definition_modes_list),
FILE_OPTIONS_ULLLIST
};
-/**
- This must be kept up to date whenever a new option is added to the list
- above, as it specifies the number of required parameters of the trigger in
- .trg file.
-*/
-
-static const int TRG_NUM_REQUIRED_PARAMETERS= 6;
/*
Structure representing contents of .TRN file which are used to support
@@ -248,22 +197,8 @@ struct st_trigname
LEX_STRING trigger_table;
};
-static const LEX_STRING trigname_file_type=
- { C_STRING_WITH_LEN("TRIGGERNAME") };
-
const char * const TRN_EXT= ".TRN";
-static File_option trigname_file_parameters[]=
-{
- {
- { C_STRING_WITH_LEN("trigger_table")},
- offsetof(struct st_trigname, trigger_table),
- FILE_OPTIONS_ESTRING
- },
- { { 0, 0 }, 0, FILE_OPTIONS_STRING }
-};
-
-
const LEX_STRING trg_action_time_type_names[]=
{
{ C_STRING_WITH_LEN("BEFORE") },
@@ -290,7 +225,6 @@ public:
MEM_ROOT *mem_root, const char *end);
};
-
class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook
{
public:
@@ -315,7 +249,7 @@ private:
Also, if possible, grabs name of the trigger being parsed so it can be
used to correctly drop problematic trigger.
*/
-class Deprecated_trigger_syntax_handler : public Internal_error_handler
+class Deprecated_trigger_syntax_handler : public Internal_error_handler
{
private:
@@ -602,6 +536,180 @@ end:
}
+bool Table_triggers_list::parse_trigger_definitions(THD *thd, TABLE *table,
+ const char *db,
+ const char *table_name,
+ bool names_only)
+{
+ DBUG_ENTER("Table_triggers_list::parse_trigger_definitions");
+
+ LEX_STRING save_db;
+ PSI_statement_locker *parent_locker= thd->m_statement_psi;
+ List_iterator_fast<LEX_STRING> it(trigger_loader.definitions_list);
+ List_iterator_fast<sql_mode_t> itm(trigger_loader.definition_modes_list);
+ List_iterator_fast<LEX_STRING> it_definer(trigger_loader.definers_list);
+ List_iterator_fast<LEX_STRING> it_client_cs_name(trigger_loader.client_cs_names);
+ List_iterator_fast<LEX_STRING> it_connection_cl_name(trigger_loader.connection_cl_names);
+ List_iterator_fast<LEX_STRING> it_db_cl_name(trigger_loader.db_cl_names);
+ LEX *old_lex= thd->lex, lex;
+ sp_rcontext *sp_runtime_ctx_saved= thd->sp_runtime_ctx;
+ sql_mode_t save_sql_mode= thd->variables.sql_mode;
+
+ /*
+ TODO: This could be avoided if there is no triggers
+ for UPDATE and DELETE.
+ */
+ if (!names_only && prepare_record1_accessors())
+ DBUG_RETURN(true);
+
+ thd->lex= &lex;
+
+ save_db.str= thd->db;
+ save_db.length= thd->db_length;
+ thd->reset_db((char*) db, strlen(db));
+
+ LEX_STRING *trg_create_str, *trg_definer;
+ LEX_STRING *client_cs_name, *connection_cl_name, *db_cl_name;
+ sql_mode_t *trg_sql_mode;
+
+ LEX_STRING *db_str= alloc_lex_string(&table->mem_root);
+ lex_string_set(db_str, db);
+ LEX_STRING *table_name_str= alloc_lex_string(&table->mem_root);
+ lex_string_set(table_name_str, table_name);
+ while ((trg_create_str= it++))
+ {
+ trg_sql_mode= itm++;
+ trg_definer= it_definer++;
+
+ thd->variables.sql_mode= *trg_sql_mode;
+
+ Parser_state parser_state;
+ if (parser_state.init(thd, trg_create_str->str, trg_create_str->length))
+ goto err_with_lex_cleanup;
+
+ client_cs_name= it_client_cs_name++;
+ connection_cl_name= it_connection_cl_name++;
+ db_cl_name= it_db_cl_name++;
+
+ Trigger_creation_ctx *creation_ctx=
+ Trigger_creation_ctx::create(thd,
+ db,
+ table_name,
+ client_cs_name,
+ connection_cl_name,
+ db_cl_name);
+
+ lex_start(thd);
+ thd->sp_runtime_ctx= NULL;
+
+ Deprecated_trigger_syntax_handler error_handler;
+ thd->push_internal_handler(&error_handler);
+ thd->m_statement_psi= NULL;
+ bool parse_error= parse_sql(thd, & parser_state, creation_ctx);
+ thd->m_statement_psi= parent_locker;
+ thd->pop_internal_handler();
+
+ /*
+ Not strictly necessary to invoke this method here, since we know
+ that we've parsed CREATE TRIGGER and not an
+ UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
+ maintain the invariant that this method is called for each
+ distinct statement, in case its logic is extended with other
+ types of analyses in future.
+ */
+ lex.set_trg_event_type_for_tables();
+
+ LEX_STRING *trigger_name;
+ if (parse_error)
+ {
+ if (!m_has_unparseable_trigger)
+ set_parse_error_message(error_handler.get_error_message());
+ /* Currently sphead is always set to NULL in case of a parse error */
+ DBUG_ASSERT(lex.sphead == NULL);
+ if (error_handler.get_trigger_name())
+ {
+ trigger_name= lex_string_dup(&table->mem_root,
+ error_handler.get_trigger_name());
+ if (!trigger_name)
+ goto err_with_lex_cleanup;
+
+ Trigger* new_trigger=
+ new (&table->mem_root) Trigger(trigger_name, db_str,
+ table_name_str, trg_create_str,
+ *trg_sql_mode, trg_definer,
+ client_cs_name, connection_cl_name,
+ db_cl_name);
+ new_trigger->set_has_parse_error();
+
+ if (table_triggers.push_back(new_trigger,
+ &table->mem_root))
+ goto err_with_lex_cleanup;
+ }
+ lex_end(&lex);
+ continue;
+ }
+
+ trigger_name= lex_string_dup(&table->mem_root, &lex.spname->m_name);
+ if (!trigger_name)
+ goto err_with_lex_cleanup;
+
+ Trigger* new_trigger=
+ new (&table->mem_root) Trigger(trigger_name, db_str,
+ table_name_str, trg_create_str,
+ *trg_sql_mode, trg_definer,
+ client_cs_name, connection_cl_name,
+ db_cl_name);
+
+ if (new_trigger->init(thd, &lex, this, creation_ctx,
+ db, table, names_only))
+ goto err_with_lex_cleanup;
+
+ if (table_triggers.push_back(new_trigger, &table->mem_root))
+ goto err_with_lex_cleanup;
+
+ bodies[new_trigger->trg_event][new_trigger->trg_action_time]= new_trigger;
+
+#ifndef DBUG_OFF
+ /*
+ Let us check that we correctly update trigger definitions when we
+ rename tables with triggers.
+
+ In special cases like "RENAME TABLE `#mysql50#somename` TO `somename`"
+ or "ALTER DATABASE `#mysql50#somename` UPGRADE DATA DIRECTORY NAME"
+ we might be given table or database name with "#mysql50#" prefix (and
+ trigger's definiton contains un-prefixed version of the same name).
+ To remove this prefix we use check_n_cut_mysql50_prefix().
+ */
+
+ char fname[NAME_LEN + 1];
+ DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) ||
+ (check_n_cut_mysql50_prefix(db, fname, sizeof(fname)) &&
+ !my_strcasecmp(table_alias_charset, lex.query_tables->db, fname))));
+ DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->table_name, table_name) ||
+ (check_n_cut_mysql50_prefix(table_name, fname, sizeof(fname)) &&
+ !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, fname))));
+#endif
+ lex_end(&lex);
+ }
+
+ thd->reset_db(save_db.str, save_db.length);
+ thd->lex= old_lex;
+ thd->sp_runtime_ctx= sp_runtime_ctx_saved;
+ thd->variables.sql_mode= save_sql_mode;
+
+ DBUG_RETURN(0);
+
+err_with_lex_cleanup:
+// QQ: anything else ?
+ lex_end(&lex);
+ thd->lex= old_lex;
+ thd->sp_runtime_ctx= sp_runtime_ctx_saved;
+ thd->variables.sql_mode= save_sql_mode;
+ thd->reset_db(save_db.str, save_db.length);
+ DBUG_RETURN(1);
+}
+
+
/**
Create trigger for table.
@@ -630,19 +738,14 @@ bool Table_triggers_list::create_trigger
{
LEX *lex= thd->lex;
TABLE *table= tables->table;
- char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN];
- LEX_STRING file, trigname_file;
LEX_STRING *trg_def;
LEX_STRING definer_user;
LEX_STRING definer_host;
- sql_mode_t *trg_sql_mode;
- char trg_definer_holder[USER_HOST_BUFF_SIZE];
+ sql_mode_t trg_sql_mode;
LEX_STRING *trg_definer;
- struct st_trigname trigname;
LEX_STRING *trg_client_cs_name;
LEX_STRING *trg_connection_cl_name;
LEX_STRING *trg_db_cl_name;
- bool was_truncated;
if (check_for_broken_triggers())
return true;
@@ -652,35 +755,12 @@ bool Table_triggers_list::create_trigger
lex->spname->m_db.str))
{
my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
- return 1;
- }
-
- /* Building .TRG and .TRN trigger filenames */
- file.length= build_table_filename(file_buff, FN_REFLEN - 1,
- tables->db, tables->table_name,
- TRG_EXT, 0);
- file.str= file_buff;
-
- trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
- tables->db,
- lex->spname->m_name.str,
- TRN_EXT, 0, &was_truncated);
- // Check if we hit FN_REFLEN bytes in path length
- if (was_truncated)
- {
- my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(trigname_buff)-1,
- trigname_buff);
- return 1;
+ return true;
}
- trigname_file.str= trigname_buff;
-
- /* Use the filesystem to enforce trigger namespace constraints. */
- if (!access(trigname_buff, F_OK))
- {
- my_error(ER_TRG_ALREADY_EXISTS, MYF(0));
- return 1;
- }
+ if (trigger_loader.check_for_uniqueness(tables->db,
+ thd->lex->spname->m_name.str))
+ return true;
sp_head *trg= lex->sphead;
int trg_event= trg->m_trg_chistics.event;
@@ -692,7 +772,7 @@ bool Table_triggers_list::create_trigger
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"multiple triggers with the same action time"
" and event for one table");
- return 1;
+ return true;
}
if (!lex->definer)
@@ -716,7 +796,7 @@ bool Table_triggers_list::create_trigger
if (!thd->slave_thread)
{
if (!(lex->definer= create_default_definer(thd)))
- return 1;
+ return true;
}
}
@@ -725,7 +805,7 @@ bool Table_triggers_list::create_trigger
that the current user has SUPER privilege (in order to create trigger
under another user one must have SUPER privilege).
*/
-
+
if (lex->definer &&
(strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
my_strcasecmp(system_charset_info,
@@ -735,7 +815,7 @@ bool Table_triggers_list::create_trigger
if (check_global_access(thd, SUPER_ACL))
{
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
- return TRUE;
+ return true;
}
}
@@ -766,20 +846,9 @@ bool Table_triggers_list::create_trigger
if (!trg_field->fixed &&
trg_field->fix_fields(thd, (Item **)0))
- return 1;
+ return true;
}
- /*
- Here we are creating file with triggers and save all triggers in it.
- sql_create_definition_file() files handles renaming and backup of older
- versions
- */
- trigname.trigger_table.str= tables->table_name;
- trigname.trigger_table.length= tables->table_name_length;
-
- if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type,
- (uchar*)&trigname, trigname_file_parameters))
- return 1;
/*
Soon we will invalidate table object and thus Table_triggers_list object
@@ -790,27 +859,15 @@ bool Table_triggers_list::create_trigger
mem_root too.
*/
if (!(trg_def= alloc_lex_string(&table->mem_root)) ||
- definitions_list.push_back(trg_def, &table->mem_root) ||
-
- !(trg_sql_mode= alloc_type<sql_mode_t>(&table->mem_root)) ||
- definition_modes_list.push_back(trg_sql_mode, &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))
+ !(trg_db_cl_name= alloc_lex_string(&table->mem_root)))
{
- goto err_with_cleanup;
+ return true;
}
- *trg_sql_mode= thd->variables.sql_mode;
+ trg_sql_mode= thd->variables.sql_mode;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (lex->definer && !is_acl_user(lex->definer->host.str,
@@ -827,14 +884,16 @@ bool Table_triggers_list::create_trigger
if (lex->definer)
{
+ char trg_definer_buf[USER_HOST_BUFF_SIZE];
/* SUID trigger. */
definer_user= lex->definer->user;
definer_host= lex->definer->host;
- trg_definer->str= trg_definer_holder;
- trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@",
- definer_host.str, NullS) - trg_definer->str;
+ size_t trg_definer_len= strxmov(trg_definer_buf, definer_user.str, "@",
+ definer_host.str, NullS) - trg_definer_buf;
+ lex_string_copy(&table->mem_root, trg_definer, trg_definer_buf,
+ trg_definer_len);
}
else
{
@@ -872,15 +931,12 @@ bool Table_triggers_list::create_trigger
stmt_query->append(STRING_WITH_LEN("CREATE "));
- if (trg_definer)
- {
- /*
- Append definer-clause if the trigger is SUID (a usual trigger in
- new MySQL versions).
- */
+ /*
+ Append definer-clause if the trigger is SUID (a usual trigger in
+ new MySQL versions).
+ */
- append_definer(thd, stmt_query, &definer_user, &definer_host);
- }
+ append_definer(thd, stmt_query, &definer_user, &definer_host);
LEX_STRING stmt_definition;
stmt_definition.str= (char*) thd->lex->stmt_definition_begin;
@@ -890,289 +946,235 @@ bool Table_triggers_list::create_trigger
stmt_query->append(stmt_definition.str, stmt_definition.length);
- trg_def->str= stmt_query->c_ptr();
- trg_def->length= stmt_query->length();
+ lex_string_copy(&table->mem_root, trg_def, stmt_query->c_ptr(),
+ stmt_query->length());
- /* Create trigger definition file. */
+ Trigger *new_trigger=
+ new (&table->mem_root) Trigger(&lex->spname->m_name,
+ &trigger_table->s->db,
+ &trigger_table->s->table_name,
+ trg_def, trg_sql_mode,
+ trg_definer, trg_client_cs_name,
+ trg_connection_cl_name, trg_db_cl_name);
- if (!sql_create_definition_file(NULL, &file, &triggers_file_type,
- (uchar*)this, triggers_file_parameters))
- return 0;
-
-err_with_cleanup:
- mysql_file_delete(key_file_trn, trigname_buff, MYF(MY_WME));
- return 1;
+ return trigger_loader.store_trigger(tables, new_trigger,
+ &table_triggers);
}
-/**
- Deletes the .TRG file for a table.
-
- @param path char buffer of size FN_REFLEN to be used
- for constructing path to .TRG file.
- @param db table's database name
- @param table_name table's name
-
- @retval
- False success
- @retval
- True error
-*/
-
-static bool rm_trigger_file(char *path, const char *db,
- const char *table_name)
+bool Trigger::init(THD *thd, LEX *lex, Table_triggers_list* object_owner,
+ Stored_program_creation_ctx *trg_creation_ctx,
+ const char *db, TABLE *table, bool names_only)
{
- build_table_filename(path, FN_REFLEN-1, db, table_name, TRG_EXT, 0);
- return mysql_file_delete(key_file_trg, path, MYF(MY_WME));
-}
+ sp= lex->sphead;
+ sp->set_info(0, 0, &lex->sp_chistics, definition_mode);
+ sp->m_trg_list= object_owner;
+ trg_event= sp->m_trg_chistics.event;
+ trg_action_time= sp->m_trg_chistics.action_time;
-/**
- Deletes the .TRN file for a trigger.
+ lex->sphead= NULL; /* Prevent double cleanup. */
- @param path char buffer of size FN_REFLEN to be used
- for constructing path to .TRN file.
- @param db trigger's database name
- @param trigger_name trigger's name
+ sp->set_creation_ctx(trg_creation_ctx);
- @retval
- False success
- @retval
- True error
-*/
+ if (!definer->length)
+ {
+ /*
+ This trigger was created/imported from the previous version of
+ MySQL, which does not support triggers definers. We should emit
+ warning here.
+ */
-static bool rm_trigname_file(char *path, const char *db,
- const char *trigger_name)
-{
- build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0);
- return mysql_file_delete(key_file_trn, path, MYF(MY_WME));
-}
+ push_warning_printf(thd, Sql_condition::SL_WARNING,
+ ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER),
+ (const char*) db,
+ (const char*) sp->m_name.str);
+ /*
+ Set definer to the '' to correct displaying in the information
+ schema.
+ */
-/**
- Helper function that saves .TRG file for Table_triggers_list object.
+ sp->set_definer((char*) "", 0);
- @param triggers Table_triggers_list object for which file should be saved
- @param db Name of database for subject table
- @param table_name Name of subject table
+ /*
+ Triggers without definer information are executed under the
+ authorization of the invoker.
+ */
- @retval
- FALSE Success
- @retval
- TRUE Error
-*/
+ sp->m_chistics->suid= SP_IS_NOT_SUID;
+ }
+ else
+ sp->set_definer(definer->str, definer->length);
-static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
- const char *table_name)
-{
- char file_buff[FN_REFLEN];
- LEX_STRING file;
+ if (!(on_table_name= alloc_lex_string(&table->mem_root)))
+ return true;
- file.length= build_table_filename(file_buff, FN_REFLEN - 1, db, table_name,
- TRG_EXT, 0);
- file.str= file_buff;
- return sql_create_definition_file(NULL, &file, &triggers_file_type,
- (uchar*)triggers, triggers_file_parameters);
-}
+ on_table_name->str= (char*) lex->raw_trg_on_table_name_begin;
+ on_table_name->length= lex->raw_trg_on_table_name_end -
+ lex->raw_trg_on_table_name_begin;
+ subject_table_grant= &object_owner->subject_table_grants[trg_event][trg_action_time];
-/**
- Drop trigger for table.
+ if (!names_only)
+ {
+ /*
+ Also let us bind these objects to Field objects in table being
+ opened.
- @param thd current thread context
- (including trigger definition in LEX)
- @param tables table list containing one open table for which trigger
- is dropped.
- @param[out] stmt_query after successful return, this string contains
- well-formed statement for creation this trigger.
+ We ignore errors here, because if even something is wrong we still
+ will be willing to open table to perform some operations (e.g.
+ SELECT)...
+ Anyway some things can be checked only during trigger execution.
+ */
+ for (Item_trigger_field *trg_field= sp->m_trg_table_fields.first;
+ trg_field;
+ trg_field= trg_field->next_trg_field)
+ {
+ trg_field->setup_field(thd, table, subject_table_grant);
+ }
+ }
- @todo
- Probably instead of removing .TRG file we should move
- to archive directory but this should be done as part of
- parse_file.cc functionality (because we will need it
- elsewhere).
+ return false;
+}
- @retval
- False success
- @retval
- True error
-*/
-bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
- String *stmt_query)
+bool Trigger::execute(THD *thd)
{
- const char *sp_name= thd->lex->spname->m_name.str; // alias
-
- LEX_STRING *name;
- char path[FN_REFLEN];
-
- List_iterator_fast<LEX_STRING> it_name(names_list);
-
- List_iterator<sql_mode_t> 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);
+ if (has_parse_error)
+ return true;
- stmt_query->append(thd->query(), thd->query_length());
+ bool err_status;
+ Sub_statement_state statement_state;
+ SELECT_LEX *save_current_select;
- while ((name= it_name++))
- {
- it_def++;
- it_mod++;
- it_definer++;
- it_client_cs_name++;
- it_connection_cl_name++;
- it_db_cl_name++;
-
- if (my_strcasecmp(table_alias_charset, sp_name, name->str) == 0)
- {
- /*
- Again we don't care much about other things required for
- clean trigger removing since table will be reopened anyway.
- */
- 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())
- {
- /*
- TODO: Probably instead of removing .TRG file we should move
- to archive directory but this should be done as part of
- parse_file.cc functionality (because we will need it
- elsewhere).
- */
- if (rm_trigger_file(path, tables->db, tables->table_name))
- return 1;
- }
- else
- {
- if (save_trigger_file(this, tables->db, tables->table_name))
- return 1;
- }
-
- if (rm_trigname_file(path, tables->db, sp_name))
- return 1;
- return 0;
- }
- }
-
- my_message(ER_TRG_DOES_NOT_EXIST, ER(ER_TRG_DOES_NOT_EXIST), MYF(0));
- return 1;
-}
+ thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
+ /*
+ Reset current_select before call execute_trigger() and
+ restore it after return from one. This way error is set
+ in case of failure during trigger execution.
+ */
+ save_current_select= thd->lex->current_select;
+ thd->lex->current_select= NULL;
+ err_status=
+ sp->execute_trigger(thd,
+ db_name,
+ table_name,
+ subject_table_grant);
+ thd->lex->current_select= save_current_select;
-Table_triggers_list::~Table_triggers_list()
-{
- for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
- for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
- delete bodies[i][j];
+ thd->restore_sub_statement_state(&statement_state);
- if (record1_field)
- for (Field **fld_ptr= record1_field; *fld_ptr; fld_ptr++)
- delete *fld_ptr;
+ return err_status;
}
-/**
- Prepare array of Field objects referencing to TABLE::record[1] instead
- of record[0] (they will represent OLD.* row values in ON UPDATE trigger
- and in ON DELETE trigger which will be called during REPLACE execution).
-
- @retval
- False success
- @retval
- True error
-*/
-bool Table_triggers_list::prepare_record1_accessors()
-{
- Field **fld, **old_fld;
+void Trigger::getInfo(LEX_STRING *trg_name,
+ LEX_STRING *trigger_stmt,
+ sql_mode_t *sql_mode,
+ LEX_STRING *trg_definer,
+ LEX_STRING *trg_definition,
+ LEX_STRING *client_cs_nam,
+ LEX_STRING *connection_cl_nam,
+ LEX_STRING *db_cl_nam)
+{
+ *trg_name= *trigger_name;
+ if (trigger_stmt)
+ *trigger_stmt= sp->m_body_utf8;
+ *sql_mode= definition_mode;
- if (!(record1_field= (Field **)alloc_root(&trigger_table->mem_root,
- (trigger_table->s->fields + 1) *
- sizeof(Field*))))
- return true;
+ if (trg_definer)
+ *trg_definer= *definer;
- for (fld= trigger_table->field, old_fld= record1_field; *fld; fld++, old_fld++)
- {
- /*
- QQ: it is supposed that it is ok to use this function for field
- cloning...
- */
- if (!(*old_fld= (*fld)->new_field(&trigger_table->mem_root, trigger_table,
- trigger_table == (*fld)->table)))
- return true;
- (*old_fld)->move_field_offset((my_ptrdiff_t)(trigger_table->record[1] -
- trigger_table->record[0]));
- }
- *old_fld= 0;
+ if (trg_definition)
+ *trg_definition= *definition;
- return false;
+ *client_cs_nam= *client_cs_name;
+ *connection_cl_nam= *connection_cl_name;
+ *db_cl_nam= *db_cl_name;
}
-
/**
- Adjust Table_triggers_list with new TABLE pointer.
-
- @param new_table new pointer to TABLE instance
+ Table of .TRG file field descriptors.
+ We have here only one field now because in nearest future .TRG
+ files will be merged into .FRM files (so we don't need something
+ like md5 or created fields).
*/
-
-void Table_triggers_list::set_table(TABLE *new_table)
+File_option Triggers_definition_loader::triggers_file_parameters[]=
{
- trigger_table= new_table;
- for (Field **field= new_table->triggers->record1_field ; *field ; field++)
{
- (*field)->table= (*field)->orig_table= new_table;
- (*field)->table_name= &new_table->alias;
- }
-}
-
-
-/**
- Check whenever .TRG file for table exist and load all triggers it contains.
+ { C_STRING_WITH_LEN("triggers") },
+ my_offsetof(class Triggers_definition_loader, definitions_list),
+ FILE_OPTIONS_STRLIST
+ },
+ {
+ { C_STRING_WITH_LEN("sql_modes") },
+ my_offsetof(class Triggers_definition_loader, definition_modes_list),
+ FILE_OPTIONS_ULLLIST
+ },
+ {
+ { C_STRING_WITH_LEN("definers") },
+ my_offsetof(class Triggers_definition_loader, definers_list),
+ FILE_OPTIONS_STRLIST
+ },
+ {
+ { C_STRING_WITH_LEN("client_cs_names") },
+ my_offsetof(class Triggers_definition_loader, client_cs_names),
+ FILE_OPTIONS_STRLIST
+ },
+ {
+ { C_STRING_WITH_LEN("connection_cl_names") },
+ my_offsetof(class Triggers_definition_loader, connection_cl_names),
+ FILE_OPTIONS_STRLIST
+ },
+ {
+ { C_STRING_WITH_LEN("db_cl_names") },
+ my_offsetof(class Triggers_definition_loader, db_cl_names),
+ FILE_OPTIONS_STRLIST
+ },
+ { { 0, 0 }, 0, FILE_OPTIONS_STRING }
+};
- @param thd current thread context
- @param db table's database name
- @param table_name table's name
- @param table pointer to table object
- @param names_only stop after loading trigger names
+File_option Triggers_definition_loader::trigname_file_parameters[]=
+{
+ {
+ { C_STRING_WITH_LEN("trigger_table")},
+ offsetof(struct st_trigname, trigger_table),
+ FILE_OPTIONS_ESTRING
+ },
+ { { 0, 0 }, 0, FILE_OPTIONS_STRING }
+};
- @todo
- A lot of things to do here e.g. how about other funcs and being
- more paranoical ?
+const LEX_STRING Triggers_definition_loader::triggers_file_type=
+ { C_STRING_WITH_LEN("TRIGGERS") };
- @todo
- This could be avoided if there is no triggers for UPDATE and DELETE.
+const LEX_STRING Triggers_definition_loader::trigname_file_type=
+ { C_STRING_WITH_LEN("TRIGGERNAME") };
- @retval
- False success
- @retval
- True error
-*/
-bool Table_triggers_list::check_n_load(THD *thd, const char *db,
- const char *table_name, TABLE *table,
- bool names_only)
+bool Triggers_definition_loader::load_triggers(THD *thd, const char *db,
+ const char *table_name,
+ TABLE *table,
+ bool *triggers_not_found)
{
char path_buff[FN_REFLEN];
LEX_STRING path;
File_parser *parser;
- LEX_STRING save_db;
- PSI_statement_locker *parent_locker= thd->m_statement_psi;
- DBUG_ENTER("Table_triggers_list::check_n_load");
+ DBUG_ENTER("Triggers_defintion_loader::load_triggers");
path.length= build_table_filename(path_buff, FN_REFLEN - 1,
db, table_name, TRG_EXT, 0);
path.str= path_buff;
- // QQ: should we analyze errno somehow ?
+ *triggers_not_found= false;
+
if (access(path_buff, F_OK))
- DBUG_RETURN(0);
+ {
+ if (errno == ENOENT)
+ *triggers_not_found= true;
+ DBUG_RETURN(true);
+ }
/*
File exists so we got to load triggers.
@@ -1184,13 +1186,8 @@ bool Table_triggers_list::check_n_load(T
{
if (is_equal(&triggers_file_type, parser->type()))
{
- Table_triggers_list *triggers=
- new (&table->mem_root) Table_triggers_list(table);
Handle_old_incorrect_sql_modes_hook sql_modes_hook(path.str);
- if (!triggers)
- DBUG_RETURN(1);
-
/*
We don't have the following attributes in old versions of .TRG file, so
we should initialize the list for safety:
@@ -1198,394 +1195,640 @@ bool Table_triggers_list::check_n_load(T
- 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();
+ definition_modes_list.empty();
+ definers_list.empty();
+ client_cs_names.empty();
+ connection_cl_names.empty();
+ db_cl_names.empty();
- if (parser->parse((uchar*)triggers, &table->mem_root,
+ if (parser->parse((uchar*)this, &table->mem_root,
triggers_file_parameters,
TRG_NUM_REQUIRED_PARAMETERS,
&sql_modes_hook))
- DBUG_RETURN(1);
+ DBUG_RETURN(true);
- List_iterator_fast<LEX_STRING> it(triggers->definitions_list);
- LEX_STRING *trg_create_str;
- sql_mode_t *trg_sql_mode;
+ List_iterator_fast<LEX_STRING> it(definitions_list);
- if (triggers->definition_modes_list.is_empty() &&
- !triggers->definitions_list.is_empty())
+ if (!definitions_list.is_empty())
{
- /*
- It is old file format => we should fill list of sql_modes.
-
- We use one mode (current) for all triggers, because we have not
- information about mode in old format.
- */
- if (!(trg_sql_mode= alloc_type<sql_mode_t>(&table->mem_root)))
+ if (definition_modes_list.is_empty())
{
- DBUG_RETURN(1); // EOM
- }
- *trg_sql_mode= global_system_variables.sql_mode;
- while (it++)
- {
- if (triggers->definition_modes_list.push_back(trg_sql_mode,
- &table->mem_root))
+ /*
+ It is old file format => we should fill list of sql_modes.
+
+ We use one mode (current) for all triggers, because we have not
+ information about mode in old format.
+ */
+ sql_mode_t *trg_sql_mode;
+ if (!(trg_sql_mode= alloc_type<sql_mode_t>(&table->mem_root)))
{
- DBUG_RETURN(1); // EOM
+ DBUG_RETURN(true); // EOM
}
+ *trg_sql_mode= global_system_variables.sql_mode;
+ while (it++)
+ {
+ if (definition_modes_list.push_back(trg_sql_mode,
+ &table->mem_root))
+ {
+ DBUG_RETURN(true); // EOM
+ }
+ }
+ it.rewind();
}
- it.rewind();
- }
- if (triggers->definers_list.is_empty() &&
- !triggers->definitions_list.is_empty())
- {
- /*
- It is old file format => we should fill list of definers.
+ if (definers_list.is_empty())
+ {
+ /*
+ It is old file format => we should fill list of definers.
- If there is no definer information, we should not switch context to
- definer when checking privileges. I.e. privileges for such triggers
- are checked for "invoker" rather than for "definer".
- */
+ If there is no definer information, we should not switch context to
+ definer when checking privileges. I.e. privileges for such triggers
+ are checked for "invoker" rather than for "definer".
+ */
- LEX_STRING *trg_definer;
+ LEX_STRING *trg_definer;
- if (!(trg_definer= alloc_lex_string(&table->mem_root)))
- DBUG_RETURN(1); // EOM
+ if (!(trg_definer= alloc_lex_string(&table->mem_root)))
+ DBUG_RETURN(true); // EOM
- trg_definer->str= (char*) "";
- trg_definer->length= 0;
+ trg_definer->str= (char*) "";
+ trg_definer->length= 0;
- while (it++)
- {
- if (triggers->definers_list.push_back(trg_definer,
- &table->mem_root))
+ while (it++)
{
- DBUG_RETURN(1); // EOM
+ if (definers_list.push_back(trg_definer,
+ &table->mem_root))
+ {
+ DBUG_RETURN(true); // EOM
+ }
}
+ it.rewind();
}
- 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())
+ if (client_cs_names.is_empty() ||
+ connection_cl_names.is_empty() ||
+ db_cl_names.is_empty())
{
- my_error(ER_TRG_CORRUPTED_FILE, MYF(0),
- (const char *) db,
- (const char *) table_name);
-
- DBUG_RETURN(1); // EOM
- }
+ LEX_STRING *trg_client_cs_name;
+ LEX_STRING *trg_connection_cl_name;
+ LEX_STRING *trg_db_cl_name;
+
+ if (!client_cs_names.is_empty() ||
+ !connection_cl_names.is_empty() ||
+ !db_cl_names.is_empty())
+ {
+ my_error(ER_TRG_CORRUPTED_FILE, MYF(0),
+ (const char *) db,
+ (const char *) table_name);
- push_warning_printf(thd, Sql_condition::SL_WARNING,
- 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
- }
+ DBUG_RETURN(true); // EOM
+ }
- /*
- Backward compatibility: assume that the query is in the current
- character set.
- */
+ push_warning_printf(thd, Sql_condition::SL_WARNING,
+ ER_TRG_NO_CREATION_CTX,
+ ER(ER_TRG_NO_CREATION_CTX),
+ (const char*) db,
+ (const char*) table_name);
- lex_string_set(trg_client_cs_name,
- thd->variables.character_set_client->csname);
+ 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(true); // EOM
+ }
- lex_string_set(trg_connection_cl_name,
- thd->variables.collation_connection->name);
+ /*
+ Backward compatibility: assume that the query is in the current
+ character set.
+ */
- lex_string_set(trg_db_cl_name,
- thd->variables.collation_database->name);
+ lex_string_set(trg_client_cs_name,
+ thd->variables.character_set_client->csname);
- while (it++)
- {
- if (triggers->client_cs_names.push_back(trg_client_cs_name,
- &table->mem_root) ||
+ lex_string_set(trg_connection_cl_name,
+ thd->variables.collation_connection->name);
- triggers->connection_cl_names.push_back(trg_connection_cl_name,
- &table->mem_root) ||
+ lex_string_set(trg_db_cl_name,
+ thd->variables.collation_database->name);
- triggers->db_cl_names.push_back(trg_db_cl_name,
- &table->mem_root))
+ while (it++)
{
- DBUG_RETURN(1); // EOM
+ if (client_cs_names.push_back(trg_client_cs_name,
+ &table->mem_root) ||
+ connection_cl_names.push_back(trg_connection_cl_name,
+ &table->mem_root) ||
+ db_cl_names.push_back(trg_db_cl_name,
+ &table->mem_root))
+ {
+ DBUG_RETURN(true); // EOM
+ }
}
+
+ it.rewind();
}
+ }
+
+ DBUG_ASSERT(definition_modes_list.elements ==
+ definitions_list.elements);
+ DBUG_ASSERT(definers_list.elements ==
+ definitions_list.elements);
+ DBUG_ASSERT(client_cs_names.elements ==
+ definitions_list.elements);
+ DBUG_ASSERT(connection_cl_names.elements ==
+ definitions_list.elements);
+ DBUG_ASSERT(db_cl_names.elements ==
+ definitions_list.elements);
+
+ DBUG_RETURN(false);
+ }
+
+ /*
+ We don't care about this error message much because .TRG files will
+ be merged into .FRM anyway.
+ */
+ my_error(ER_WRONG_OBJECT, MYF(0),
+ table_name, TRG_EXT + 1, "TRIGGER");
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(true);
+}
+
+
+bool Triggers_definition_loader::store_trigger(TABLE_LIST *tables,
+ Trigger *new_trigger,
+ List<Trigger> *table_triggers)
+{
+ LEX_STRING trigname_file;
+ TABLE *table= tables->table;
+ char trigname_buff[FN_REFLEN];
+ struct st_trigname trigname;
+ bool was_truncated;
+
+ if (table_triggers->push_back(new_trigger, &table->mem_root))
+ return true;
+
+ if (update_triggers_definition(tables->table, table_triggers, NULL, NULL))
+ return true;
+
+ /*
+ Here we are creating file with triggers and save all triggers in it.
+ sql_create_definition_file() files handles renaming and backup of older
+ versions
+ */
+ trigname.trigger_table.str= tables->table_name;
+ trigname.trigger_table.length= tables->table_name_length;
+
+ /* Building .TRN trigger filenames */
+ trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
+ tables->db,
+ new_trigger->trigger_name->str,
+ TRN_EXT, 0, &was_truncated);
+
+ // Check if we hit FN_REFLEN bytes in path length
+ if (was_truncated)
+ {
+ my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(trigname_buff)-1,
+ trigname_buff);
+ return true;
+ }
+ trigname_file.str= trigname_buff;
+
+ if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type,
+ (uchar*)&trigname, trigname_file_parameters))
+ return true;
+
+ /* Create trigger definition file. */
+ if (save_trigger_file(tables->db, tables->table_name))
+ {
+ mysql_file_delete(key_file_trn, trigname_buff, MYF(MY_WME));
+ return true;
+ }
+
+ return false;
+}
+
+
+bool Triggers_definition_loader::drop_trigger(TABLE_LIST *tables,
+ const char *trigger_name,
+ List<Trigger> *table_triggers)
+{
+ char path[FN_REFLEN];
+
+ bool found= false;
+ update_triggers_definition(tables->table, table_triggers,
+ trigger_name, &found);
+
+ if (found)
+ {
+ if (table_triggers->is_empty())
+ {
+ /*
+ TODO: Probably instead of removing .TRG file we should move
+ to archive directory but this should be done as part of
+ parse_file.cc functionality (because we will need it
+ elsewhere).
+ */
+ if (rm_trigger_file(path, tables->db, tables->table_name))
+ return true;
+ }
+ else
+ {
+ if (save_trigger_file(tables->db, tables->table_name))
+ return true;
+ }
+
+ if (rm_trigname_file(path, tables->db, trigger_name))
+ return true;
+ return false;
+ }
+
+ my_message(ER_TRG_DOES_NOT_EXIST, ER(ER_TRG_DOES_NOT_EXIST), MYF(0));
+ return true;
+}
+
+
+/**
+ This method saves .TRG file for the table specified by arguments.
+
+ @param triggers Table_triggers_list object for which file should be saved
+ @param db Name of database for subject table
+ @param table_name Name of subject table
+
+ @retval
+ FALSE Success
+ @retval
+ TRUE Error
+*/
+
+bool Triggers_definition_loader::save_trigger_file(const char *db,
+ const char *table_name)
+{
+ char file_buff[FN_REFLEN];
+ LEX_STRING file;
+ bool was_truncated= false;
+
+ file.length= build_table_filename(file_buff, FN_REFLEN - 1, db, table_name,
+ TRG_EXT, 0, &was_truncated);
+
+ if (was_truncated)
+ {
+ my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(file_buff)-1,
+ file_buff);
+ return true;
+ }
+
+ file.str= file_buff;
+ return sql_create_definition_file(NULL, &file, &triggers_file_type,
+ (uchar*)this, triggers_file_parameters);
+}
+
+
+/**
+ found has to be set to false before passing to this method.
+ name != NULL if it needs to remove trigger definition by its name.
+
+ @return Operation status
+ @retval false On success.
+ @retval true Otherwise.
+*/
+bool Triggers_definition_loader::update_triggers_definition(TABLE *table,
+ List<Trigger> *trgs,
+ const char *name,
+ bool *found)
+{
+ List_iterator<Trigger> it_table_triggers(*trgs);
+
+ definitions_list.empty();
+ definition_modes_list.empty();
+ definers_list.empty();
+ client_cs_names.empty();
+ connection_cl_names.empty();
+ db_cl_names.empty();
+
+ Trigger *next_trigger;
+ while ((next_trigger= it_table_triggers++))
+ {
+ if (name &&
+ my_strcasecmp(table_alias_charset, next_trigger->trigger_name->str,
+ name) == 0)
+ {
+ it_table_triggers.remove();
+ *found= true;
+ continue;
+ }
+ if (definitions_list.push_back(next_trigger->definition,
+ &table->mem_root) ||
+ definition_modes_list.push_back(&next_trigger->definition_mode,
+ &table->mem_root) ||
+ definers_list.push_back(next_trigger->definer,
+ &table->mem_root) ||
+ client_cs_names.push_back(next_trigger->client_cs_name,
+ &table->mem_root) ||
+ connection_cl_names.push_back(next_trigger->connection_cl_name,
+ &table->mem_root) ||
+ db_cl_names.push_back(next_trigger->db_cl_name, &table->mem_root))
+ return true;
+ }
+
+ return false;
+}
+
+
+bool Triggers_definition_loader::check_for_uniqueness(const char *db_name,
+ const char *trigger_name)
+{
+ LEX_STRING trigname_file;
+ char trigname_buff[FN_REFLEN];
+ bool was_truncated;
+
+ /* Building .TRN trigger filenames */
+ build_table_filename(trigname_buff, FN_REFLEN-1,
+ db_name,
+ trigger_name,
+ TRN_EXT, 0, &was_truncated);
+ // Check if we hit FN_REFLEN bytes in path length
+ if (was_truncated)
+ {
+ my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(trigname_buff)-1,
+ trigname_buff);
+ return true;
+ }
+ trigname_file.str= trigname_buff;
+
+ /* Use the filesystem to enforce trigger namespace constraints. */
+ if (!access(trigname_buff, F_OK))
+ {
+ my_error(ER_TRG_ALREADY_EXISTS, MYF(0));
+ return true;
+ }
+
+ return false;
+}
+
+
+bool Triggers_definition_loader::get_table_name_for_trigger(
+ THD *thd,
+ const sp_name *trg_name,
+ const LEX_STRING *trn_path,
+ LEX_STRING *tbl_name)
+{
+ File_parser *parser;
+ struct st_trigname trn_data;
+
+ Handle_old_incorrect_trigger_table_hook trigger_table_hook(
+ trn_path->str,
+ &trn_data.trigger_table);
+
+ DBUG_ENTER("load_table_name_for_trigger");
+
+ /* Parse the TRN-file. */
+
+ if (!(parser= sql_parse_prepare(trn_path, thd->mem_root, TRUE)))
+ DBUG_RETURN(TRUE);
+
+ if (!is_equal(&trigname_file_type, parser->type()))
+ {
+ my_error(ER_WRONG_OBJECT, MYF(0),
+ trg_name->m_name.str,
+ TRN_EXT + 1,
+ "TRIGGERNAME");
+
+ DBUG_RETURN(TRUE);
+ }
+
+ if (parser->parse((uchar*) &trn_data, thd->mem_root,
+ trigname_file_parameters, 1,
+ &trigger_table_hook))
+ DBUG_RETURN(TRUE);
+
+ /* Copy trigger table name. */
+
+ *tbl_name= trn_data.trigger_table;
+
+ /* That's all. */
+
+ DBUG_RETURN(FALSE);
+}
+
+
+bool Triggers_definition_loader::drop_all_triggers(const char *db_name,
+ const char *table_name,
+ List<Trigger> &table_trgs)
+{
+ Trigger *trigger;
+ char path[FN_REFLEN];
+ List_iterator_fast<Trigger> it_triggers(table_trgs);
+ bool result= false;
+
+ while ((trigger= it_triggers++))
+ {
+ if (rm_trigname_file(path, db_name, trigger->trigger_name->str))
+ {
+ /*
+ Instead of immediately bailing out with error if we were unable
+ to remove .TRN file we will try to drop other files.
+ */
+ result= true;
+ continue;
+ }
+ }
+
+ if (rm_trigger_file(path, db_name, table_name))
+ result= true;
+
+ return result;
+}
+
+
+/**
+ Deletes the .TRN file for a trigger.
+
+ @param path char buffer of size FN_REFLEN to be used
+ for constructing path to .TRN file.
+ @param db trigger's database name
+ @param trigger_name trigger's name
+
+ @retval
+ False success
+ @retval
+ True error
+*/
+
+bool Triggers_definition_loader::rm_trigname_file(char *path, const char *db,
+ const char *trigger_name)
+{
+ build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0);
+ return mysql_file_delete(key_file_trn, path, MYF(MY_WME));
+}
+
+
+/**
+ Deletes the .TRG file for a table.
+
+ @param path char buffer of size FN_REFLEN to be used
+ for constructing path to .TRG file.
+ @param db table's database name
+ @param table_name table's name
+
+ @retval
+ False success
+ @retval
+ True error
+*/
+
+bool Triggers_definition_loader::rm_trigger_file(char *path, const char *db,
+ const char *table_name)
+{
+ build_table_filename(path, FN_REFLEN-1, db, table_name, TRG_EXT, 0);
+ return mysql_file_delete(key_file_trg, path, MYF(MY_WME));
+}
- 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);
+/**
+ Drop trigger for table.
- table->triggers= triggers;
+ @param thd current thread context
+ (including trigger definition in LEX)
+ @param tables table list containing one open table for which trigger
+ is dropped.
+ @param[out] stmt_query after successful return, this string contains
+ well-formed statement for creation this trigger.
- /*
- TODO: This could be avoided if there is no triggers
- for UPDATE and DELETE.
- */
- if (!names_only && triggers->prepare_record1_accessors())
- DBUG_RETURN(1);
+ @todo
+ Probably instead of removing .TRG file we should move
+ to archive directory but this should be done as part of
+ parse_file.cc functionality (because we will need it
+ elsewhere).
- List_iterator_fast<sql_mode_t> 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 *sp_runtime_ctx_saved= thd->sp_runtime_ctx;
- sql_mode_t save_sql_mode= thd->variables.sql_mode;
- LEX_STRING *on_table_name;
-
- thd->lex= &lex;
-
- save_db.str= thd->db;
- save_db.length= thd->db_length;
- thd->reset_db((char*) db, strlen(db));
- while ((trg_create_str= it++))
- {
- trg_sql_mode= itm++;
- LEX_STRING *trg_definer= it_definer++;
+ @retval
+ False success
+ @retval
+ True error
+*/
+bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
+ String *stmt_query)
+{
+ const char *sp_name= thd->lex->spname->m_name.str; // alias
+ stmt_query->append(thd->query(), thd->query_length());
- thd->variables.sql_mode= *trg_sql_mode;
+ return trigger_loader.drop_trigger(tables, sp_name, &table_triggers);
+}
- Parser_state parser_state;
- if (parser_state.init(thd, trg_create_str->str, trg_create_str->length))
- goto err_with_lex_cleanup;
- 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->sp_runtime_ctx= NULL;
-
- Deprecated_trigger_syntax_handler error_handler;
- thd->push_internal_handler(&error_handler);
- thd->m_statement_psi= NULL;
- bool parse_error= parse_sql(thd, & parser_state, creation_ctx);
- thd->m_statement_psi= parent_locker;
- thd->pop_internal_handler();
-
- /*
- Not strictly necessary to invoke this method here, since we know
- that we've parsed CREATE TRIGGER and not an
- UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
- maintain the invariant that this method is called for each
- distinct statement, in case its logic is extended with other
- types of analyses in future.
- */
- lex.set_trg_event_type_for_tables();
+Table_triggers_list::~Table_triggers_list()
+{
+ for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
+ for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
+ delete bodies[i][j];
- if (parse_error)
- {
- if (!triggers->m_has_unparseable_trigger)
- triggers->set_parse_error_message(error_handler.get_error_message());
- /* Currently sphead is always set to NULL in case of a parse error */
- DBUG_ASSERT(lex.sphead == NULL);
- if (error_handler.get_trigger_name())
- {
- LEX_STRING *trigger_name;
- const LEX_STRING *orig_trigger_name= error_handler.get_trigger_name();
+ if (record1_field)
+ for (Field **fld_ptr= record1_field; *fld_ptr; fld_ptr++)
+ delete *fld_ptr;
+}
- if (!(trigger_name= alloc_lex_string(&table->mem_root)) ||
- !(trigger_name->str= strmake_root(&table->mem_root,
- orig_trigger_name->str,
- orig_trigger_name->length)))
- goto err_with_lex_cleanup;
-
- trigger_name->length= orig_trigger_name->length;
-
- if (triggers->names_list.push_back(trigger_name,
- &table->mem_root))
- goto err_with_lex_cleanup;
- }
- else
- {
- /*
- The Table_triggers_list is not constructed as a list of
- trigger objects as one would expect, but rather of lists of
- properties of equal length. Thus, even if we don't get the
- trigger name, we still fill all in all the lists with
- placeholders as we might otherwise create a skew in the
- lists. Obviously, this has to be refactored.
- */
- LEX_STRING *empty= alloc_lex_string(&table->mem_root);
- if (!empty)
- goto err_with_lex_cleanup;
-
- empty->str= const_cast<char*>("");
- empty->length= 0;
- if (triggers->names_list.push_back(empty, &table->mem_root))
- goto err_with_lex_cleanup;
- }
- lex_end(&lex);
- continue;
- }
- sp_head *sp= lex.sphead;
- sp->set_info(0, 0, &lex.sp_chistics, *trg_sql_mode);
- sp->m_trg_list= triggers;
+/**
+ Prepare array of Field objects referencing to TABLE::record[1] instead
+ of record[0] (they will represent OLD.* row values in ON UPDATE trigger
+ and in ON DELETE trigger which will be called during REPLACE execution).
- int trg_event= sp->m_trg_chistics.event;
- int trg_action_time= sp->m_trg_chistics.action_time;
+ @retval
+ False success
+ @retval
+ True error
+*/
+bool Table_triggers_list::prepare_record1_accessors()
+{
+ Field **fld, **old_fld;
- triggers->bodies[trg_event][trg_action_time]= sp;
- lex.sphead= NULL; /* Prevent double cleanup. */
+ if (!(record1_field= (Field **)alloc_root(&trigger_table->mem_root,
+ (trigger_table->s->fields + 1) *
+ sizeof(Field*))))
+ return true;
- sp->set_info(0, 0, &lex.sp_chistics, *trg_sql_mode);
- sp->set_creation_ctx(creation_ctx);
+ for (fld= trigger_table->field, old_fld= record1_field; *fld; fld++, old_fld++)
+ {
+ /*
+ QQ: it is supposed that it is ok to use this function for field
+ cloning...
+ */
+ if (!(*old_fld= (*fld)->new_field(&trigger_table->mem_root, trigger_table,
+ trigger_table == (*fld)->table)))
+ return true;
+ (*old_fld)->move_field_offset((my_ptrdiff_t)(trigger_table->record[1] -
+ trigger_table->record[0]));
+ }
+ *old_fld= 0;
- if (!trg_definer->length)
- {
- /*
- This trigger was created/imported from the previous version of
- MySQL, which does not support triggers definers. We should emit
- warning here.
- */
+ return false;
+}
- push_warning_printf(thd, Sql_condition::SL_WARNING,
- ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER),
- (const char*) db,
- (const char*) sp->m_name.str);
- /*
- Set definer to the '' to correct displaying in the information
- schema.
- */
+/**
+ Adjust Table_triggers_list with new TABLE pointer.
- sp->set_definer((char*) "", 0);
+ @param new_table new pointer to TABLE instance
+*/
- /*
- Triggers without definer information are executed under the
- authorization of the invoker.
- */
+void Table_triggers_list::set_table(TABLE *new_table)
+{
+ trigger_table= new_table;
+ for (Field **field= new_table->triggers->record1_field ; *field ; field++)
+ {
+ (*field)->table= (*field)->orig_table= new_table;
+ (*field)->table_name= &new_table->alias;
+ }
+}
- sp->m_chistics->suid= SP_IS_NOT_SUID;
- }
- else
- sp->set_definer(trg_definer->str, trg_definer->length);
- if (triggers->names_list.push_back(&sp->m_name, &table->mem_root))
- goto err_with_lex_cleanup;
+/**
+ Check whenever .TRG file for table exist and load all triggers it contains.
- if (!(on_table_name= alloc_lex_string(&table->mem_root)))
- goto err_with_lex_cleanup;
+ @param thd current thread context
+ @param db table's database name
+ @param table_name table's name
+ @param table pointer to table object
+ @param names_only stop after loading trigger names
- on_table_name->str= (char*) lex.raw_trg_on_table_name_begin;
- on_table_name->length= lex.raw_trg_on_table_name_end
- - lex.raw_trg_on_table_name_begin;
+ @todo
+ A lot of things to do here e.g. how about other funcs and being
+ more paranoical ?
- if (triggers->on_table_names_list.push_back(on_table_name, &table->mem_root))
- goto err_with_lex_cleanup;
-#ifndef DBUG_OFF
- /*
- Let us check that we correctly update trigger definitions when we
- rename tables with triggers.
-
- In special cases like "RENAME TABLE `#mysql50#somename` TO `somename`"
- or "ALTER DATABASE `#mysql50#somename` UPGRADE DATA DIRECTORY NAME"
- we might be given table or database name with "#mysql50#" prefix (and
- trigger's definiton contains un-prefixed version of the same name).
- To remove this prefix we use check_n_cut_mysql50_prefix().
- */
-
- char fname[NAME_LEN + 1];
- DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) ||
- (check_n_cut_mysql50_prefix(db, fname, sizeof(fname)) &&
- !my_strcasecmp(table_alias_charset, lex.query_tables->db, fname))));
- DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->table_name, table_name) ||
- (check_n_cut_mysql50_prefix(table_name, fname, sizeof(fname)) &&
- !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, fname))));
-#endif
- if (names_only)
- {
- lex_end(&lex);
- continue;
- }
+ @todo
+ This could be avoided if there is no triggers for UPDATE and DELETE.
- /*
- Also let us bind these objects to Field objects in table being
- opened.
-
- We ignore errors here, because if even something is wrong we still
- will be willing to open table to perform some operations (e.g.
- SELECT)...
- Anyway some things can be checked only during trigger execution.
- */
- for (Item_trigger_field *trg_field= sp->m_trg_table_fields.first;
- trg_field;
- trg_field= trg_field->next_trg_field)
- {
- trg_field->setup_field(thd, table,
- &triggers->subject_table_grants[trg_event][trg_action_time]);
- }
+ @retval
+ False success
+ @retval
+ True error
+*/
- lex_end(&lex);
- }
- thd->reset_db(save_db.str, save_db.length);
- thd->lex= old_lex;
- thd->sp_runtime_ctx= sp_runtime_ctx_saved;
- thd->variables.sql_mode= save_sql_mode;
+bool Table_triggers_list::check_n_load(THD *thd, const char *db,
+ const char *table_name, TABLE *table,
+ bool names_only)
+{
+ DBUG_ENTER("Table_triggers_list::check_n_load");
- DBUG_RETURN(0);
+ //if (Table_triggers_list::)
+ Table_triggers_list *triggers=
+ new (&table->mem_root) Table_triggers_list(table);
-err_with_lex_cleanup:
- // QQ: anything else ?
- lex_end(&lex);
- thd->lex= old_lex;
- thd->sp_runtime_ctx= sp_runtime_ctx_saved;
- thd->variables.sql_mode= save_sql_mode;
- thd->reset_db(save_db.str, save_db.length);
- DBUG_RETURN(1);
- }
+ if (!triggers)
+ DBUG_RETURN(true);
- /*
- We don't care about this error message much because .TRG files will
- be merged into .FRM anyway.
- */
- my_error(ER_WRONG_OBJECT, MYF(0),
- table_name, TRG_EXT + 1, "TRIGGER");
- DBUG_RETURN(1);
+ bool triggers_not_found;
+ if (triggers->load_triggers(thd, db, table_name, &triggers_not_found))
+ {
+ delete triggers;
+ if (triggers_not_found)
+ DBUG_RETURN(false);
+ DBUG_RETURN(true);
}
- DBUG_RETURN(1);
+ table->triggers= triggers;
+
+ return triggers->parse_trigger_definitions(thd, table, db,
+ table_name, names_only);
}
@@ -1618,40 +1861,17 @@ bool Table_triggers_list::get_trigger_in
LEX_STRING *connection_cl_name,
LEX_STRING *db_cl_name)
{
- sp_head *body;
+ Trigger *trigger;
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_utf8;
- *sql_mode= body->m_sql_mode;
-
- if (body->m_chistics->suid == SP_IS_NOT_SUID)
- {
- definer->str[0]= 0;
- definer->length= 0;
- }
- else
- {
- definer->length= strxmov(definer->str, body->m_definer_user.str, "@",
- 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);
+ if ((trigger= bodies[event][time_type]))
+ {
+ trigger->getInfo(trigger_name, trigger_stmt, sql_mode, definer, NULL,
+ client_cs_name, connection_cl_name, db_cl_name);
+ DBUG_RETURN(false);
}
- DBUG_RETURN(1);
+
+ DBUG_RETURN(true);
}
@@ -1664,46 +1884,30 @@ void Table_triggers_list::get_trigger_in
LEX_STRING *connection_cl_name,
LEX_STRING *db_cl_name)
{
- List_iterator_fast<LEX_STRING> it_trigger_name(names_list);
- List_iterator_fast<sql_mode_t> 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);
-
+ List_iterator_fast<Trigger> it_table_triggers(table_triggers);
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++);
+ it_table_triggers.next_fast();
+ }
+
+ Trigger *found_trigger= it_table_triggers++;
+ found_trigger->getInfo(trigger_name, NULL, sql_mode, NULL,
+ sql_original_stmt, client_cs_name,
+ connection_cl_name, db_cl_name);
}
int Table_triggers_list::find_trigger_by_name(const LEX_STRING *trg_name)
{
- List_iterator_fast<LEX_STRING> it(names_list);
-
+ List_iterator_fast<Trigger> it_table_triggers(table_triggers);
for (int i = 0; ; ++i)
{
- LEX_STRING *cur_name= it++;
+ Trigger *trigger= it_table_triggers++;
- if (!cur_name)
+ if (!trigger)
return -1;
- if (strcmp(cur_name->str, trg_name->str) == 0)
+ if (strcmp(trigger->trigger_name->str, trg_name->str) == 0)
return i;
}
}
@@ -1784,7 +1988,6 @@ bool add_table_for_trigger(THD *thd,
bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
{
TABLE table;
- char path[FN_REFLEN];
bool result= 0;
DBUG_ENTER("drop_all_triggers");
@@ -1798,38 +2001,16 @@ bool Table_triggers_list::drop_all_trigg
}
if (table.triggers)
{
- LEX_STRING *trigger;
- List_iterator_fast<LEX_STRING> it_name(table.triggers->names_list);
+ Triggers_definition_loader triggers_definition_loader;
+ result= triggers_definition_loader.drop_all_triggers(
+ db, name,
+ table.triggers->table_triggers);
- while ((trigger= it_name++))
- {
- /*
- Trigger, which body we failed to parse during call
- Table_triggers_list::check_n_load(), might be missing name.
- Such triggers have zero-length name and are skipped here.
- */
- if (trigger->length == 0)
- continue;
- if (rm_trigname_file(path, db, trigger->str))
- {
- /*
- Instead of immediately bailing out with error if we were unable
- to remove .TRN file we will try to drop other files.
- */
- result= 1;
- continue;
- }
- }
-
- if (rm_trigger_file(path, db, name))
- {
- result= 1;
- goto end;
- }
}
end:
if (table.triggers)
delete table.triggers;
+ table.triggers= NULL;
free_root(&table.mem_root, MYF(0));
DBUG_RETURN(result);
}
@@ -1856,24 +2037,21 @@ Table_triggers_list::change_table_name_i
const char *old_db_name,
const char *new_db_name,
LEX_STRING *old_table_name,
- LEX_STRING *new_table_name)
+ LEX_STRING *new_table_name,
+ bool upgrading50to51)
{
- char path_buff[FN_REFLEN];
LEX_STRING *def, *on_table_name, new_def;
sql_mode_t save_sql_mode= thd->variables.sql_mode;
- List_iterator_fast<LEX_STRING> it_def(definitions_list);
- List_iterator_fast<LEX_STRING> it_on_table_name(on_table_names_list);
- List_iterator_fast<ulonglong> it_mode(definition_modes_list);
+ List_iterator_fast<Trigger> it_table_triggers(table_triggers);
size_t on_q_table_name_len, before_on_len;
String buff;
+ Trigger *trigger;
- DBUG_ASSERT(definitions_list.elements == on_table_names_list.elements &&
- definitions_list.elements == definition_modes_list.elements);
-
- while ((def= it_def++))
+ while ((trigger= it_table_triggers++))
{
- on_table_name= it_on_table_name++;
- thd->variables.sql_mode= *(it_mode++);
+ def= trigger->definition;
+ on_table_name= trigger->on_table_name;
+ thd->variables.sql_mode= trigger->definition_mode;
/* Construct CREATE TRIGGER statement with new table name. */
buff.length(0);
@@ -1905,19 +2083,62 @@ Table_triggers_list::change_table_name_i
thd->variables.sql_mode= save_sql_mode;
if (thd->is_fatal_error)
- return TRUE; /* OOM */
+ return true; /* OOM */
+
+ if (trigger_loader.rename_table_in_trigger(trigger_table,
+ &table_triggers,
+ old_db_name, old_table_name,
+ new_db_name, new_table_name,
+ upgrading50to51))
+ return true;
+
+ return false;
+}
+
+bool Triggers_definition_loader::rename_table_in_trigger(
+ TABLE *trigger_table,
+ List<Trigger> *table_triggers,
+ const char *old_db_name,
+ LEX_STRING *old_table_name,
+ const char *new_db_name,
+ LEX_STRING *new_table_name,
+ bool upgrading50to51)
+{
+ char path_buff[FN_REFLEN];
+ LEX_STRING *err_trigname;
+
+ if (update_triggers_definition(trigger_table, table_triggers,
+ NULL, NULL))
+ return true; /* OOM */
+
+ if ((err_trigname= change_table_name_in_trignames(
+ *table_triggers,
+ upgrading50to51 ? old_db_name : NULL,
+ new_db_name, new_table_name, 0)))
+ {
+ /*
+ If we were unable to update one of .TRN files properly we will
+ revert all changes that we have done and report about error.
+ We assume that we will be able to undo our changes without errors
+ (we can't do much if there will be an error anyway).
+ */
+ (void) change_table_name_in_trignames(
+ *table_triggers,
+ upgrading50to51 ? new_db_name : NULL, old_db_name,
+ old_table_name, err_trigname);
+ return true;
+ }
- if (save_trigger_file(this, new_db_name, new_table_name->str))
- return TRUE;
+ if (save_trigger_file(new_db_name, new_table_name->str))
+ return true;
if (rm_trigger_file(path_buff, old_db_name, old_table_name->str))
{
(void) rm_trigger_file(path_buff, new_db_name, new_table_name->str);
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
-
/**
Iterate though Table_triggers_list::names_list list and update
.TRN files after renaming triggers' subject table.
@@ -1936,21 +2157,26 @@ Table_triggers_list::change_table_name_i
*/
LEX_STRING*
-Table_triggers_list::change_table_name_in_trignames(const char *old_db_name,
- const char *new_db_name,
- LEX_STRING *new_table_name,
- LEX_STRING *stopper)
+Triggers_definition_loader::change_table_name_in_trignames(
+ List<Trigger> &table_triggers,
+ const char *old_db_name,
+ const char *new_db_name,
+ LEX_STRING *new_table_name,
+ LEX_STRING *stopper)
{
char trigname_buff[FN_REFLEN];
struct st_trigname trigname;
LEX_STRING trigname_file;
- LEX_STRING *trigger;
- List_iterator_fast<LEX_STRING> it_name(names_list);
+ Trigger *trigger;
+ List_iterator_fast<Trigger> it_table_triggers(table_triggers);
- while ((trigger= it_name++) != stopper)
+ while ((trigger= it_table_triggers++))
{
+ if (trigger->trigger_name == stopper)
+ break;
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
- new_db_name, trigger->str,
+ new_db_name,
+ trigger->trigger_name->str,
TRN_EXT, 0);
trigname_file.str= trigname_buff;
@@ -1958,20 +2184,22 @@ Table_triggers_list::change_table_name_i
if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type,
(uchar*)&trigname, trigname_file_parameters))
- return trigger;
-
+ return trigger->trigger_name;
+
/* Remove stale .TRN file in case of database upgrade */
if (old_db_name)
{
- if (rm_trigname_file(trigname_buff, old_db_name, trigger->str))
+ if (rm_trigname_file(trigname_buff, old_db_name,
+ trigger->trigger_name->str))
{
- (void) rm_trigname_file(trigname_buff, new_db_name, trigger->str);
- return trigger;
+ (void) rm_trigname_file(trigname_buff, new_db_name,
+ trigger->trigger_name->str);
+ return trigger->trigger_name;
}
}
}
- return 0;
+ return NULL;
}
@@ -2003,9 +2231,8 @@ bool Table_triggers_list::change_table_n
const char *new_table)
{
TABLE table;
- bool result= 0;
- bool upgrading50to51= FALSE;
- LEX_STRING *err_trigname;
+ bool result= false;
+ bool upgrading50to51= false;
DBUG_ENTER("change_table_name");
memset(&table, 0, sizeof(table));
@@ -2023,14 +2250,14 @@ bool Table_triggers_list::change_table_n
if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE))
{
- result= 1;
+ result= true;
goto end;
}
if (table.triggers)
{
if (table.triggers->check_for_broken_triggers())
{
- result= 1;
+ result= true;
goto end;
}
LEX_STRING old_table_name= { (char *) old_alias, strlen(old_alias) };
@@ -2051,45 +2278,29 @@ bool Table_triggers_list::change_table_n
if (check_n_cut_mysql50_prefix(db, dbname, sizeof(dbname)) &&
!my_strcasecmp(table_alias_charset, dbname, new_db))
{
- upgrading50to51= TRUE;
+ upgrading50to51= true;
}
else
{
my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
- result= 1;
+ result= true;
goto end;
}
}
+
if (table.triggers->change_table_name_in_triggers(thd, db, new_db,
&old_table_name,
- &new_table_name))
- {
- result= 1;
- goto end;
- }
- if ((err_trigname= table.triggers->change_table_name_in_trignames(
- upgrading50to51 ? db : NULL,
- new_db, &new_table_name, 0)))
+ &new_table_name,
+ upgrading50to51))
{
- /*
- If we were unable to update one of .TRN files properly we will
- revert all changes that we have done and report about error.
- We assume that we will be able to undo our changes without errors
- (we can't do much if there will be an error anyway).
- */
- (void) table.triggers->change_table_name_in_trignames(
- upgrading50to51 ? new_db : NULL, db,
- &old_table_name, err_trigname);
- (void) table.triggers->change_table_name_in_triggers(
- thd, db, new_db,
- &new_table_name, &old_table_name);
- result= 1;
+ result= true;
goto end;
}
}
end:
delete table.triggers;
+ table.triggers= NULL;
free_root(&table.mem_root, MYF(0));
DBUG_RETURN(result);
}
@@ -2116,16 +2327,13 @@ bool Table_triggers_list::process_trigge
trg_action_time_type time_type,
bool old_row_is_record1)
{
- bool err_status;
- Sub_statement_state statement_state;
- sp_head *sp_trigger= bodies[event][time_type];
- SELECT_LEX *save_current_select;
+ Trigger *trigger= bodies[event][time_type];
if (check_for_broken_triggers())
return true;
- if (sp_trigger == NULL)
- return FALSE;
+ if (trigger == NULL)
+ return false;
if (old_row_is_record1)
{
@@ -2144,25 +2352,7 @@ bool Table_triggers_list::process_trigge
DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map &
static_cast<uint>(1 << static_cast<int>(event)));
- thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
-
- /*
- Reset current_select before call execute_trigger() and
- restore it after return from one. This way error is set
- in case of failure during trigger execution.
- */
- save_current_select= thd->lex->current_select;
- thd->lex->current_select= NULL;
- err_status=
- sp_trigger->execute_trigger(thd,
- &trigger_table->s->db,
- &trigger_table->s->table_name,
- &subject_table_grants[event][time_type]);
- thd->lex->current_select= save_current_select;
-
- thd->restore_sub_statement_state(&statement_state);
-
- return err_status;
+ return trigger->execute(thd);
}
@@ -2196,22 +2386,22 @@ add_tables_and_routines_for_triggers(THD
for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
{
/* We can have only one trigger per action type currently */
- sp_head *trigger= table_list->table->triggers->bodies[i][j];
+ Trigger *trigger= table_list->table->triggers->bodies[i][j];
if (trigger)
{
- MDL_key key(MDL_key::TRIGGER, trigger->m_db.str, trigger->m_name.str);
+ MDL_key key(MDL_key::TRIGGER, trigger->sp->m_db.str, trigger->sp->m_name.str);
if (sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
&key, table_list->belong_to_view))
{
- trigger->add_used_tables_to_table_list(thd,
+ trigger->sp->add_used_tables_to_table_list(thd,
&prelocking_ctx->query_tables_last,
table_list->belong_to_view);
sp_update_stmt_used_routines(thd, prelocking_ctx,
- &trigger->m_sroutines,
+ &trigger->sp->m_sroutines,
table_list->belong_to_view);
- trigger->propagate_attributes(prelocking_ctx);
+ trigger->sp->propagate_attributes(prelocking_ctx);
}
}
}
@@ -2234,10 +2424,10 @@ bool Table_triggers_list::is_fields_upda
trg_action_time_type action_time)
{
Item_trigger_field *trg_field;
- sp_head *sp= bodies[event_type][action_time];
+ Trigger *trigger= bodies[event_type][action_time];
DBUG_ASSERT(used_fields->n_bits == trigger_table->s->fields);
- for (trg_field= sp->m_trg_table_fields.first; trg_field;
+ for (trg_field= trigger->sp->m_trg_table_fields.first; trg_field;
trg_field= trg_field->next_trg_field)
{
/* We cannot check fields which does not present in table. */
@@ -2312,12 +2502,12 @@ void Table_triggers_list::mark_fields_us
for (action_time= 0; action_time < (int)TRG_ACTION_MAX; action_time++)
{
- sp_head *sp= bodies[event][action_time];
+ Trigger *trigger= bodies[event][action_time];
- if (!sp)
+ if (!trigger)
continue;
- for (trg_field= sp->m_trg_table_fields.first; trg_field;
+ for (trg_field= trigger->sp->m_trg_table_fields.first; trg_field;
trg_field= trg_field->next_trg_field)
{
/* We cannot mark fields which does not present in table. */
@@ -2500,40 +2690,8 @@ bool load_table_name_for_trigger(THD *th
const LEX_STRING *trn_path,
LEX_STRING *tbl_name)
{
- File_parser *parser;
- struct st_trigname trn_data;
-
- Handle_old_incorrect_trigger_table_hook trigger_table_hook(
- trn_path->str,
- &trn_data.trigger_table);
-
- DBUG_ENTER("load_table_name_for_trigger");
-
- /* Parse the TRN-file. */
-
- if (!(parser= sql_parse_prepare(trn_path, thd->mem_root, TRUE)))
- DBUG_RETURN(TRUE);
-
- if (!is_equal(&trigname_file_type, parser->type()))
- {
- my_error(ER_WRONG_OBJECT, MYF(0),
- trg_name->m_name.str,
- TRN_EXT + 1,
- "TRIGGERNAME");
-
- DBUG_RETURN(TRUE);
- }
-
- if (parser->parse((uchar*) &trn_data, thd->mem_root,
- trigname_file_parameters, 1,
- &trigger_table_hook))
- DBUG_RETURN(TRUE);
-
- /* Copy trigger table name. */
-
- *tbl_name= trn_data.trigger_table;
-
- /* That's all. */
+ Triggers_definition_loader trigger_loader;
+ return trigger_loader.get_table_name_for_trigger(thd, trg_name,
+ trn_path, tbl_name);
- DBUG_RETURN(FALSE);
}
=== modified file 'sql/sql_trigger.h'
--- a/sql/sql_trigger.h 2012-12-11 11:56:43 +0000
+++ b/sql/sql_trigger.h 2013-01-22 09:24:23 +0000
@@ -25,6 +25,7 @@ class sp_name;
class Query_tables_list;
struct TABLE_LIST;
class Query_tables_list;
+class Stored_program_creation_ctx;
/** Event on which trigger is invoked. */
enum trg_event_type
@@ -50,6 +51,190 @@ enum trg_action_time_type
};
+class Table_triggers_list;
+class Trigger : public Sql_alloc
+{
+public:
+ Trigger(LEX_STRING *trg_name, LEX_STRING *db_nam, LEX_STRING *table_nam,
+ LEX_STRING *trg_create_str, sql_mode_t trg_sql_mode,
+ LEX_STRING *trg_definer, LEX_STRING *client_cs_nam,
+ LEX_STRING *connection_cl_nam, LEX_STRING *db_cl_nam)
+ : has_parse_error(false),
+ trigger_name(trg_name), db_name(db_nam), table_name(table_nam),
+ on_table_name(NULL), definition(trg_create_str),
+ definition_mode(trg_sql_mode), definer(trg_definer),
+ client_cs_name(client_cs_nam), connection_cl_name(connection_cl_nam),
+ db_cl_name(db_cl_nam), sp(NULL),
+ trg_action_time(TRG_ACTION_MAX), trg_event(TRG_EVENT_MAX)
+ {}
+
+ void set_has_parse_error()
+ {
+ has_parse_error= true;
+ }
+
+ bool init(THD *thd, LEX *lex, Table_triggers_list* object_owner,
+ Stored_program_creation_ctx *trg_creation_ctx,
+ const char *db, TABLE *table, bool names_only);
+
+ bool execute(THD *thd);
+
+ void getInfo(LEX_STRING *trg_name,
+ LEX_STRING *trigger_stmt,
+ sql_mode_t *sql_mode,
+ LEX_STRING *trg_definer,
+ LEX_STRING *trg_definition,
+ LEX_STRING *client_cs_nam,
+ LEX_STRING *connection_cl_nam,
+ LEX_STRING *db_cl_nam);
+
+ bool has_parse_error;
+
+ /**
+ Names of trigger.
+ */
+ LEX_STRING *trigger_name;
+
+ LEX_STRING *db_name;
+ LEX_STRING *table_name;
+ /**
+ "ON table_name" part in trigger definition, used for
+ updating trigger definition during RENAME TABLE.
+ */
+ LEX_STRING *on_table_name;
+
+ /**
+ Grant information for the trigger.
+ */
+ GRANT_INFO *subject_table_grant;
+
+ /*
+ Trigger definition to save it in file.
+ */
+ LEX_STRING *definition;
+
+ /*
+ sql mode for trigger
+ */
+ sql_mode_t definition_mode;
+
+ LEX_STRING *definer;
+
+ /*
+ Character set context, used for parsing and executing trigger.
+ */
+ LEX_STRING *client_cs_name;
+ LEX_STRING *connection_cl_name;
+ LEX_STRING *db_cl_name;
+ sp_head *sp;
+
+ trg_action_time_type trg_action_time;
+ trg_event_type trg_event;
+};
+
+
+class Triggers_definition_loader
+{
+public:
+ bool load_triggers(THD *thd, const char *db,
+ const char *table_name,
+ TABLE *table, bool *trigger_not_found);
+
+ bool store_trigger(TABLE_LIST *tables,
+ Trigger *new_trigger,
+ List<Trigger> *table_triggers);
+
+ bool drop_trigger(TABLE_LIST *tables,
+ const char *trigger_name,
+ List<Trigger> *table_triggers);
+
+ bool check_for_uniqueness(const char *db_name,
+ const char *trigger_name);
+
+ bool rename_table_in_trigger(TABLE *trigger_table,
+ List<Trigger> *table_triggers,
+ const char *old_db_name,
+ LEX_STRING *old_table_name,
+ const char *new_db_name,
+ LEX_STRING *new_table_name,
+ bool upgrading50to51);
+
+ bool get_table_name_for_trigger(
+ THD *thd,
+ const sp_name *trg_name,
+ const LEX_STRING *trn_path,
+ LEX_STRING *tbl_name);
+
+ bool drop_all_triggers(const char* db_name,
+ const char* table_name,
+ List<Trigger> &table_triggers);
+
+private:
+ bool update_triggers_definition(TABLE *table,
+ List<Trigger> *triggers,
+ const char *name,
+ bool *found);
+
+ bool save_trigger_file(const char *db,
+ const char *table_name);
+
+ LEX_STRING* change_table_name_in_trignames(
+ List<Trigger> &table_triggers,
+ const char *old_db_name,
+ const char *new_db_name,
+ LEX_STRING *new_table_name,
+ LEX_STRING *stopper);
+
+ bool rm_trigname_file(char *path, const char *db,
+ const char *trigger_name);
+
+ bool rm_trigger_file(char *path, const char *db,
+ const char *table_name);
+
+ /**
+ This must be kept up to date whenever a new option is added to the list
+ above, as it specifies the number of required parameters of the trigger in
+ .trg file.
+ */
+
+ static const int TRG_NUM_REQUIRED_PARAMETERS= 6;
+
+ /**
+ Table of .TRG file field descriptors.
+ We have here only one field now because in nearest future .TRG
+ files will be merged into .FRM files (so we don't need something
+ like md5 or created fields).
+ */
+ static File_option triggers_file_parameters[];
+
+ static File_option trigname_file_parameters[];
+
+ static const LEX_STRING triggers_file_type;
+
+ static const LEX_STRING trigname_file_type;
+
+public:
+ /**
+ Field responsible for storing triggers definitions in file.
+ It have to be public because we are using it directly from parser.
+ */
+ List<LEX_STRING> definitions_list;
+
+ /**
+ List of sql modes for triggers
+ */
+ List<ulonglong> definition_modes_list;
+
+ 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;
+};
+
+
/**
This class holds all information about triggers of table.
@@ -58,8 +243,8 @@ enum trg_action_time_type
class Table_triggers_list: public Sql_alloc
{
- /** Triggers as SPs grouped by event, action_time */
- sp_head *bodies[TRG_EVENT_MAX][TRG_ACTION_MAX];
+ /** Triggers grouped by event, action_time */
+ Trigger *bodies[TRG_EVENT_MAX][TRG_ACTION_MAX];
/**
Copy of TABLE::Field array with field pointers set to TABLE::record[1]
@@ -81,18 +266,10 @@ public:
TABLE *trigger_table;
private:
- /**
- Names of triggers.
- Should correspond to order of triggers on definitions_list,
- used in CREATE/DROP TRIGGER for looking up trigger by name.
- */
- List<LEX_STRING> names_list;
-
- /**
- List of "ON table_name" parts in trigger definitions, used for
- updating trigger definitions during RENAME TABLE.
+ /*
+ List of triggers assigned to this table object.
*/
- List<LEX_STRING> on_table_names_list;
+ List<Trigger> table_triggers;
public:
/**
@@ -122,28 +299,9 @@ private:
*/
char m_parse_error_message[MYSQL_ERRMSG_SIZE];
-public:
- /**
- Field responsible for storing triggers definitions in file.
- It have to be public because we are using it directly from parser.
- */
- List<LEX_STRING> definitions_list;
-
- /**
- List of sql modes for triggers
- */
- List<ulonglong> definition_modes_list;
-
- 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. */
+ Triggers_definition_loader trigger_loader;
+public:
Table_triggers_list(TABLE *table_arg)
:record1_field(0), trigger_table(table_arg),
m_has_unparseable_trigger(false)
@@ -154,6 +312,13 @@ public:
~Table_triggers_list();
+ bool load_triggers(THD *thd, const char *db, const char *table_name,
+ bool *triggers_not_found)
+ {
+ return trigger_loader.load_triggers(thd, db, table_name, trigger_table,
+ triggers_not_found);
+ }
+
bool create_trigger(THD *thd, TABLE_LIST *table, String *stmt_query);
bool drop_trigger(THD *thd, TABLE_LIST *table, String *stmt_query);
bool process_triggers(THD *thd, trg_event_type event,
@@ -218,16 +383,16 @@ public:
void disable_fields_temporary_nullability();
private:
+ bool parse_trigger_definitions(THD *thd, TABLE *table, const char *db,
+ const char *table_name, bool names_only);
+
bool prepare_record1_accessors();
- LEX_STRING* change_table_name_in_trignames(const char *old_db_name,
- const char *new_db_name,
- LEX_STRING *new_table_name,
- LEX_STRING *stopper);
bool change_table_name_in_triggers(THD *thd,
const char *old_db_name,
const char *new_db_name,
LEX_STRING *old_table_name,
- LEX_STRING *new_table_name);
+ LEX_STRING *new_table_name,
+ bool upgrading50to51);
bool check_for_broken_triggers()
{
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (Dmitry.Shulga:5355 to 5356) | Dmitry Shulga | 8 Mar 2013 |