List:Commits« Previous MessageNext Message »
From:Dmitry Shulga Date:January 22 2013 9:25am
Subject:bzr push into mysql-trunk branch (Dmitry.Shulga:5355 to 5356)
View as plain text  
 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 Shulga8 Mar